177 lines
5.6 KiB
Text
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()
|