RosettaCodeData/Task/24-game/Phix/24-game.phix
2016-12-05 23:44:36 +01:00

177 lines
5.6 KiB
Text

-- Note this uses simple/strict left association, so for example:
-- 1+2*1*8 is ((1+2)*1)*8 not 1+((2*1)*8) [or 1+(2*(1*8))], and
-- 7-(2*2)*8 is (7-(2*2))*8 not 7-((2*2)*8)
-- Does not allow unary minus on the first digit.
-- Uses solve24() from the next task, when it can.
-- (you may want to comment out the last 2 lines/uncomment the if 0, in that file)
--
--include 24_game_solve.exw
--with trace
forward function eval(string equation, sequence unused, integer idx=1)
-- (the above definition is entirely optional, but good coding style)
constant errorcodes = {"digit expected", -- 1
"')' expected", -- 2
"digit already used", -- 3
"digit not offered", -- 4
"operand expected"} -- 5
function card(integer idx) -- (for error handling)
if idx=1 then return "1st" end if
if idx=2 then return "2nd" end if
-- (assumes expression is less than 21 characters)
return sprintf("%dth",idx)
end function
function errorchar(sequence equation, integer idx)
if idx>length(equation) then return "" end if
return sprintf("(%s)",equation[idx])
end function
sequence rset = repeat(0,4)
procedure new_rset()
for i=1 to length(rset) do
rset[i] = rand(9)
end for
end procedure
function get_operand(string equation, integer idx, sequence unused)
integer ch, k,
error = 1 -- "digit expected"
atom res
if idx<=length(equation) then
ch = equation[idx]
if ch='(' then
{error,res,unused,idx} = eval(equation,unused,idx+1)
if error=0
and idx<=length(equation) then
ch = equation[idx]
if ch=')' then
return {0,res,unused,idx+1}
end if
end if
if error=0 then
error = 2 -- "')' expected"
end if
elsif ch>='0' and ch<='9' then
res = ch-'0'
k = find(res,unused)
if k!=0 then
unused[k..k] = {}
return {0,res,unused,idx+1}
end if
if find(res,rset) then
error = 3 -- "digit already used"
else
error = 4 -- "digit not offered"
end if
end if
end if
return {error,0,unused,idx}
end function
function get_operator(string equation, integer idx)
integer ch, error = 5 -- "operand expected"
if idx<=length(equation) then
ch = equation[idx]
if find(ch,"+-/*") then
return {0,ch,idx+1}
end if
end if
return {error,0,idx}
end function
function eval(string equation, sequence unused, integer idx=1)
atom lhs, rhs
integer ch, error
{error,lhs,unused,idx} = get_operand(equation,idx,unused)
if error=0 then
while 1 do
{error,ch,idx} = get_operator(equation,idx)
if error!=0 then exit end if
{error,rhs,unused,idx} = get_operand(equation,idx,unused)
if error!=0 then exit end if
if ch='+' then lhs += rhs
elsif ch='-' then lhs -= rhs
elsif ch='/' then lhs /= rhs
elsif ch='*' then lhs *= rhs
else ?9/0 -- (should not happen)
end if
if idx>length(equation) then
return {0,lhs,unused,idx}
end if
ch = equation[idx]
if ch=')' then
return {0,lhs,unused,idx}
end if
end while
end if
return {error,0,unused,idx}
end function
function strip(string equation)
for i=length(equation) to 1 by -1 do
if find(equation[i]," \t\r\n") then
equation[i..i] = ""
end if
end for
return equation
end function
function strip0(atom a) -- (for error handling)
string res = sprintf("%f",a)
integer ch
for i=length(res) to 2 by -1 do
ch = res[i]
if ch='.' then return res[1..i-1] end if
if ch!='0' then return res[1..i] end if
end for
return res
end function
procedure play()
sequence unused
string equation
integer error,idx
atom res
new_rset()
printf(1,"Enter an expression which evaluates to exactly 24\n"&
"Use all of, and only, the digits %d, %d, %d, and %d\n"&
"You may only use the operators + - * /\n"&
"Parentheses and spaces are allowed\n",rset)
while 1 do
equation = strip(gets(0))
if upper(equation)="Q" then exit end if
if equation="?" then
puts(1,"\n")
integer r_solve24 = routine_id("solve24") -- see below
if r_solve24=-1 then -- (someone copied just this code out?)
puts(1,"no solve24 routine\n")
else
call_proc(r_solve24,{rset})
end if
else
{error,res,unused,idx} = eval(equation, rset)
if error!=0 then
printf(1," %s on the %s character%s\n",{errorcodes[error],card(idx),errorchar(equation,idx)})
elsif idx<=length(equation) then
printf(1,"\neval() returned only having processed %d of %d characters\n",{idx,length(equation)})
elsif length(unused) then
printf(1," not all the digits were used\n",error)
elsif res!=24 then
printf(1,"\nresult is %s, not 24\n",{strip0(res)})
else
puts(1," correct! Press any key to quit\n")
if getc(0) then end if
exit
end if
end if
puts(1,"enter Q to give up and quit\n")
end while
end procedure
play()