The 24 Game tests one's mental arithmetic.



Task
Write a program that randomly chooses and displays four digits, each from 1 ──► 9 (inclusive) with repetitions allowed.

The program should prompt for the player to enter an arithmetic expression using just those, and all of those four digits, used exactly once each. The program should check then evaluate the expression.

The goal is for the player to enter an expression that (numerically) evaluates to 24.

 Only the following operators/functions are allowed: multiplication, division, addition, subtraction
 Division should use floating point or rational arithmetic, etc, to preserve remainders.
 Brackets are allowed, if using an infix expression evaluator.
 Forming multiple digit numbers from the supplied digits is disallowed. (So an answer of 12+12 when given 1, 2, 2, and 1 is wrong).
 The order of the digits when given does not have to be preserved.


Notes
 The type of expression evaluator used is not mandated. An RPN evaluator is equally acceptable for example.
 The task is not for the program to generate the expression, or test whether an expression is even possible.


Related tasks
 24 game/Solve


Reference
 The 24 Game on h2g2.


invocable all 
link strings   # for csort, deletec
 
procedure main() 
help()  
repeat { 
   every (n := "") ||:= (1 to 4, string(1+?8))
   writes("Your four digits are : ")
   every writes(!n," ") 
   write()
 
   e := trim(read()) | fail
   case e of {
      "q"|"quit": return
      "?"|"help": help()
      default: {
         e := deletec(e,' \t')         # no whitespace
         d := deletec(e,~&digits)      # just digits
         if csort(n) ~== csort(d) then # and only the 4 given digits
            write("Invalid repsonse.") & next 
 
         if e ? (ans := eval(E()), pos(0)) then # parse and evaluate
            if ans = 24 then write("Congratulations you win!") 
            else write("Your answer was ",ans,". Try again.")
         else write("Invalid expression.")
         }
      }
   }
end
 
procedure eval(X)    #: return the evaluated AST
   if type(X) == "list" then {
      x := eval(get(X))
      while x := get(X)(real(x), real(eval(get(X) | stop("Malformed expression."))))
   }
   return \x | X
end
 
procedure E()    #: expression
   put(lex := [],T())
   while put(lex,tab(any('+-*/'))) do
      put(lex,T())  
   suspend if *lex = 1 then lex[1] else lex     # strip useless []  
end   
 
procedure T()                   #: Term
   suspend 2(="(", E(), =")") | # parenthesized subexpression, or ...
       tab(any(&digits))        # just a value
end
 
procedure help()
return write(
   "Welcome to 24\n\n",
   "Combine the 4 given digits to make 24 using only + - * / and ( ).\n ",
   "All operations have equal precedence and are evaluated left to right.\n",
   "Combining (concatenating) digits is not allowed.\n",
   "Enter 'help', 'quit', or an expression.\n")  
end