1 rem 24 game
2 rem for rosetta code
10 rem use appropriate basic base address
11 bh=08:bl=01: rem $0801 commodore 64
12 rem bh=16:bl=01: rem $1001 commodore +4
13 rem bh=18:bl=01: rem $1201 commodore vic-20 (35k ram)
14 rem bh=04:bl=01: rem $0401 commodore pet
15 rem bh=28:bl=01: rem $1c01 commodore 128 (bank 0)

35 print chr$(147);chr$(14);"Initializing...":gosub 1400
40 n$="":x=rnd(-ti):rem similar to 'randomize'
45 for i=1 to 4
50 t$=str$(int(rnd(1)*9)+1)
55 n$=n$+mid$(t$,2,1)
60 next i

65 print chr$(147)
70 print spc(16);"24 Game"
71 print:print " The goal of this game is to formulate"
72 print:print " an arithmetic expression that"
73 print:print " evaluates to a value of 24, however"
74 print:print " you may use only the four numbers"
75 print:print " given at random by the computer and"
76 print:print " the standard arithmetic operations of"
77 print:print " add, subtract, multiply, and divide."
78 print:print " Each digit must be used by itself. "
79 print:print " (e.g. if given 1, 2, 3, 4, you cannot"
80 print:print " combine 1 and 2 to make 12.)"
89 gosub 1000

90 i$="":f$="":p$=""
95 print chr$(147);"Allowed characters:"
100 i$=n$+"+-*/()"
110 print
120 for i=1 to len(i$)
130 print mid$(i$,i,1);" ";
140 next i:print
150 print:print "Spaces are ignored."
155 print "Enter 'end' to end.":print
160 input "Enter the formula";f$
170 if f$="end" then print "Program terminated.":end

180 print:print "Checking syntax... ";tab(34);
190 for i=1 to len(f$)
200 if mid$(f$,i,1)=" " then next i
210 c$=mid$(f$,i,1)
220 if c$="+" or c$="-" or c$="*" or c$="/" then p$=p$+"o":goto 250
230 if c$="(" or c$=")" then p$=p$+c$:goto 250
240 p$=p$+"n"
250 next i
260 restore
270 for i=1 to 11
280 read t$
290 if t$=p$ then i=11
300 next i
310 if t$<>p$ then gosub 1100:gosub 1000:goto 90

315 print "OK":print "Checking for illegal numbers... ";tab(34);
320 for i=1 to len(f$)
330 for j=1 to 10
335 ft$=mid$(f$,i,1)
336 il$=left$(i$,j-1):it$=mid$(i$,j,1):ir$=mid$(i$,j+1,len(i$))
340 if ft$=it$ and ft$>"0" and ft$<="9" then i$=il$+" "+ir$
350 next j
360 next i
370 if mid$(i$,1,4)<>"    " then gosub 1200:gosub 1000:goto 90

375 print "OK":print "Evaluating expression...":print:print tab(10);f$;" =";
380 gosub 600:rem r=val(f$)
390 print r;" "
400 if r<>24 then gosub 1300:gosub 1000:goto 90
410 print "Correct!"

420 print:print "Would you like to go again (y/n)? ";
425 get k$:if k$<>"y" and k$<>"n" then 425
430 print k$
435 if k$="y" then goto 40
440 print:print "Very well. Have a nice day!"
450 end

500 rem pattern matching
501 data "nononon","(non)onon","nono(non)"
504 data "no(no(non))","((non)on)on","no(non)on"
507 data "(non)o(non)","no((non)on)","(nonon)on"
510 data "(no(non))on","no(nonon)"

600 rem get basic to evaluate our expression
605 a$="r="+f$:gosub 1440
610 for i=1 to len(a$)
615 rem simple token translation
620 b=asc(mid$(a$,i,1))
625 if (b>41 and b<48) or b=61 or b=94 then b=t(b)
630 poke (ad+i-1),b
635 next
640 gosub 2000
645 rem gosub 1440:rem uncomment to clear evaluation line after use
650 return

1000 rem screen pause
1005 pt$=" Press a key to continue. "
1010 print:print spc(20-int(len(pt$)/2));
1015 print chr$(18);pt$;chr$(146);
1020 get k$:if k$=""then 1020
1030 return

1100 rem syntax error
1105 print "ERROR":print
1110 print "Maybe something is out of place..."
1120 return

1200 rem invalid arguments
1205 print "ERROR":print
1210 print "?Invalid Arguments - "
1215 print "You used a number that is not allowed."
1220 return

1300 rem wrong formula
1305 print:print "Wrong answer. Try again."
1310 return

1400 dim t(94):t(43)=170:t(45)=171:t(42)=172:t(47)=173:t(61)=178:t(94)=174
1405 rem locate line 2005 in ram
1410 lh=bh:ll=bl:nh=0:nl=0
1415 ad=lh*256+ll
1420 lh=peek(ad+1):ll=peek(ad)
1425 nl=peek(ad+2):nh=peek(ad+3):n=nh*256+nl
1430 if n<>2005 then goto 1415
1435 ad=ad+4:return

1440 for j=ad to ad+73:poke j,asc(":"):next
1445 return

2000 rem put 74 colons on the next line
2005 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
2010 return


(define-condition choose-digits () ())
(define-condition bad-equation (error) ())

(defun 24-game ()
  (let (chosen-digits)
    (labels ((prompt ()
               (format t "Chosen digits: ~{~D~^, ~}~%~
                          Enter expression (or `bye' to quit, `!' to choose new digits): "
                       chosen-digits)
               (read))
             (lose () (error 'bad-equation))
             (choose () (setf chosen-digits (loop repeat 4 collecting (1+ (random 9)))))
             (check (e)
               (typecase e
                 ((eql bye) (return-from 24-game))
                 ((eql !) (signal 'choose-digits))
                 (atom (lose))
                 (cons (check-sub (car e) (check-sub (cdr e) chosen-digits)) e)))
             (check-sub (sub allowed-digits)
               (typecase sub
                 ((member nil + - * /) allowed-digits)
                 (integer
                  (if (member sub allowed-digits)
                      (remove sub allowed-digits :count 1)
                      (lose)))
                 (cons (check-sub (car sub) (check-sub (cdr sub) allowed-digits)))
                 (t (lose))))
             (win ()
               (format t "You win.~%")
               (return-from 24-game)))
      (choose)
      (loop
       (handler-case
           (if (= 24 (eval (check (prompt)))) (win) (lose))
         (error () (format t "Bad equation, try again.~%"))
         (choose-digits () (choose)))))))


import std.stdio, std.random, std.math, std.algorithm, std.range,
       std.typetuple;

void main() {
    void op(char c)() {
        if (stack.length < 2)
            throw new Exception("Wrong expression.");
        stack[$ - 2] = mixin("stack[$ - 2]" ~ c ~ "stack[$ - 1]");
        stack.popBack();
    }

    const problem = iota(4).map!(_ => uniform(1, 10))().array();
    writeln("Make 24 with the digits: ", problem);

    double[] stack;
    int[] digits;
    foreach (const char c; readln())
        switch (c) {
            case ' ', '\t', '\n': break;
            case '1': .. case '9':
                stack ~= c - '0';
                digits ~= c - '0';
                break;
            foreach (o; TypeTuple!('+', '-', '*', '/')) {
                case o: op!o(); break;
            }
            break;
            default: throw new Exception("Wrong char: " ~ c);
        }

    if (!digits.sort().equal(problem.dup.sort()))
        throw new Exception("Not using the given digits.");
    if (stack.length != 1)
        throw new Exception("Wrong expression.");
    writeln("Result: ", stack[0]);
    writeln(abs(stack[0] - 24) < 0.001 ? "Good job!" : "Try again.");
}


(string-delimiter "")
;; check that nums are in expr, and only once
(define (is-valid? expr sorted: nums)
    (when (equal? 'q expr) (error "24-game" "Thx for playing"))
    (unless (and
        (list? expr)
        (equal? nums (list-sort < (filter number? (flatten expr)))))
    (writeln "🎃 Please use" nums)
    #f))

;; 4 random  digits
(define (gen24)
     (->> (append (range 1 10)(range 1 10)) shuffle (take 4) (list-sort < )))

(define (is-24? num)
    (unless (= 24 num)
    (writeln "😧 Sorry - Result = " num)
    #f))

(define (check-24 expr)
    (if (and
        (is-valid? expr nums)
        (is-24?  (js-eval (string expr)))) ;; use js evaluator
        "🍀 🌸 Congrats - (play24) for another one."
        (input-expr check-24 (string nums))))

(define nums null)
(define (play24)
    (set! nums (gen24))
    (writeln "24-game - Can you combine" nums "to get 24 ❓ (q to exit)")
    (input-expr check-24 (string-append  (string nums) " -> 24 ❓")))


import system'routines;
import system'collections;
import system'dynamic;
import extensions;

// --- Expression ---

class ExpressionTree
{
    object theTree;

    constructor(s)
    {
        auto level := new Integer(0);

        s.forEach:(ch)
        {
            var node := new DynamicStruct();

            ch =>
                $43 { node.Level := level + 1; node.Operation := __subj add }        // +
                $45 { node.Level := level + 1; node.Operation := __subj subtract }   // -
                $42 { node.Level := level + 2; node.Operation := __subj multiply }   // *
                $47 { node.Level := level + 2; node.Operation := __subj divide }     // /
                $40 { level.append(10); ^ self }                               // (
                $41 { level.reduce(10); ^ self }                               // )
                : {
                    node.Leaf := ch.toString().toReal();
                    node.Level := level + 3
                };

            if (nil == theTree)
            {
                theTree := node
            }
            else
            {
                if (theTree.Level >= node.Level)
                {
                    node.Left := theTree;
                    node.Right := nilValue;

                    theTree := node
                }
                else
                {
                        var top := theTree;
                        while ((nilValue != top.Right)&&(top.Right.Level < node.Level))
                            { top := top.Right };

                        node.Left := top.Right;
                        node.Right := nilValue;

                        top.Right := node
                }
            }
        }
    }

    eval(node)
    {
        if (node.containsProperty(subjconst Leaf))
        {
            ^ node.Leaf
        }
        else
        {
            var left := self.eval(node.Left);
            var right := self.eval(node.Right);

            var op := node.Operation;

            ^ op(left, right);
        }
    }

    get Value()
        <= eval(theTree);

    readLeaves(list, node)
    {
        if (nil == node)
            { InvalidArgumentException.raise() };

        if (node.containsProperty(subjconst Leaf))
        {
            list.append(node.Leaf)
        }
        else
        {
            self.readLeaves(list, node.Left);
            self.readLeaves(list, node.Right)
        }
    }

    readLeaves(list)
        <= readLeaves(list,theTree);
}

// --- Game ---

class TwentyFourGame
{
    object theNumbers;

    constructor()
    {
        self.newPuzzle();
    }

    newPuzzle()
    {
        theNumbers := new object[]
        {
            1 + randomGenerator.eval:9,
            1 + randomGenerator.eval:9,
            1 + randomGenerator.eval:9,
            1 + randomGenerator.eval:9
        }
    }

    help()
    {
        console
            .printLine:"------------------------------- Instructions ------------------------------"
            .printLine:"Four digits will be displayed."
            .printLine:"Enter an equation using all of those four digits that evaluates to 24"
            .printLine:"Only * / + - operators and () are allowed"
            .printLine:"Digits can only be used once, but in any order you need."
            .printLine:"Digits cannot be combined - i.e.: 12 + 12 when given 1,2,2,1 is not allowed"
            .printLine:"Submit a blank line to skip the current puzzle."
            .printLine:"Type 'q' to quit"
            .writeLine()
            .printLine:"Example: given 2 3 8 2, answer should resemble 8*3-(2-2)"
            .printLine:"------------------------------- --------------------------------------------"
    }

    prompt()
    {
        theNumbers.forEach:(n){ console.print(n," ") };

        console.print:": "
    }

    resolve(expr)
    {
        var tree := new ExpressionTree(expr);

        var leaves := new ArrayList();
        tree.readLeaves:leaves;

        ifnot (leaves.ascendant().sequenceEqual(theNumbers.ascendant()))
            { console.printLine:"Invalid input. Enter an equation using all of those four digits. Try again."; ^ self };

        var result := tree.Value;
        if (result == 24)
        {
            console.printLine("Good work. ",expr,"=",result);

            self.newPuzzle()
        }
        else
        {
            console.printLine("Incorrect. ",expr,"=",result)
        }
    }
}

extension gameOp
{
    playRound(expr)
    {
        if (expr == "q")
        {
            ^ false
        }
        else
        {
            if (expr == "")
            {
                console.printLine:"Skipping this puzzle"; self.newPuzzle()
            }
            else
            {
                try
                {
                    self.resolve(expr)
                }
                catch(Exception e)
                {
                    console.printLine:"An error occurred.  Check your input and try again."
                }
            };

            ^ true
        }
    }
}

public program()
{
    var game := new TwentyFourGame().help();

    while (game.prompt().playRound(console.readLine())) {}
}


defmodule Game24 do
  def main do
    IO.puts "24 Game"
    play
  end

  defp play do
    IO.puts "Generating 4 digits..."
    digts = for _ <- 1..4, do: Enum.random(1..9)
    IO.puts "Your digits\t#{inspect digts, char_lists: :as_lists}"
    read_eval(digts)
    play
  end

  defp read_eval(digits) do
    exp = IO.gets("Your expression: ") |> String.strip
    if exp in ["","q"], do: exit(:normal)        # give up
    case {correct_nums(exp, digits), eval(exp)} do
      {:ok, x} when x==24 -> IO.puts "You Win!"
      {:ok, x} -> IO.puts "You Lose with #{inspect x}!"
      {err, _} -> IO.puts "The following numbers are wrong: #{inspect err, char_lists: :as_lists}"
    end
  end

  defp correct_nums(exp, digits) do
    nums = String.replace(exp, ~r/\D/, " ") |> String.split |> Enum.map(&String.to_integer &1)
    if length(nums)==4 and (nums--digits)==[], do: :ok, else: nums
  end

  defp eval(exp) do
    try do
      Code.eval_string(exp) |> elem(0)
    rescue
      e -> Exception.message(e)
    end
  end
end

Game24.main


-module(g24).
-export([main/0]).

main() ->
    random:seed(now()),
    io:format("24 Game~n"),
    play().

play() ->
    io:format("Generating 4 digits...~n"),
    Digts = [random:uniform(X) || X <- [9,9,9,9]],
    io:format("Your digits\t~w~n", [Digts]),
    read_eval(Digts),
    play().

read_eval(Digits) ->
    Exp = string:strip(io:get_line(standard_io, "Your expression: "), both, $\n),
    case {correct_nums(Exp, Digits), eval(Exp)} of
        {ok, X} when X == 24 -> io:format("You Win!~n");
        {ok, X} -> io:format("You Lose with ~p!~n",[X]);
        {List, _} -> io:format("The following numbers are wrong: ~p~n", [List])
    end.

correct_nums(Exp, Digits) ->
    case re:run(Exp, "([0-9]+)", [global, {capture, all_but_first, list}]) of
        nomatch ->
            "No number entered";
        {match, IntLs} ->
            case [X || [X] <- IntLs, not lists:member(list_to_integer(X), Digits)] of
                [] -> ok;
                L -> L
            end
    end.

eval(Exp) ->
    {X, _} = eval(re:replace(Exp, "\\s", "", [{return, list},global]),
                  0),
    X.

eval([], Val) ->
    {Val,[]};
eval([$(|Rest], Val) ->
    {NewVal, Exp} = eval(Rest, Val),
    eval(Exp, NewVal);
eval([$)|Rest], Val) ->
    {Val, Rest};
eval([$[|Rest], Val) ->
    {NewVal, Exp} = eval(Rest, Val),
    eval(Exp, NewVal);
eval([$]|Rest], Val) ->
    {Val, Rest};
eval([$+|Rest], Val) ->
    {NewOperand, Exp} = eval(Rest, 0),
    eval(Exp, Val + NewOperand);
eval([$-|Rest], Val) ->
    {NewOperand, Exp} = eval(Rest, 0),
    eval(Exp, Val - NewOperand);
eval([$*|Rest], Val) ->
    {NewOperand, Exp} = eval(Rest, 0),
    eval(Exp, Val * NewOperand);
eval([$/|Rest], Val) ->
    {NewOperand, Exp} = eval(Rest, 0),
    eval(Exp, Val / NewOperand);
eval([X|Rest], 0) when X >= $1, X =< $9 ->
    eval(Rest, X-$0).


open System
open System.Text.RegularExpressions

// Some utilities
let (|Parse|_|) regex str =
   let m = Regex(regex).Match(str)
   if m.Success then Some ([for g in m.Groups -> g.Value]) else None
let rec gcd x y = if x = y || x = 0 then y else if x < y then gcd y x else gcd y (x-y)
let abs (x : int) = Math.Abs x
let sign (x: int) = Math.Sign x
let cint s = Int32.Parse(s)
let replace m (s : string) t = Regex.Replace(t, m, s)

// computing in Rationals
type Rat(x : int, y : int) =
    let g = if y <> 0 then gcd (abs x) (abs y) else raise <| DivideByZeroException()
    member this.n = sign y * x / g   // store a minus sign in the numerator
    member this.d =
        if y <> 0 then sign y * y / g else raise <| DivideByZeroException()
    static member (~-) (x : Rat) = Rat(-x.n, x.d)
    static member (+) (x : Rat, y : Rat) = Rat(x.n * y.d + y.n * x.d, x.d * y.d)
    static member (-) (x : Rat, y : Rat) = x + Rat(-y.n, y.d)
    static member (*) (x : Rat, y : Rat) = Rat(x.n * y.n, x.d * y.d)
    static member (/) (x : Rat, y : Rat) = x * Rat(y.d, y.n)
    override this.ToString() = sprintf @"<%d,%d>" this.n this.d
    new(x : string, y : string) = if y = "" then Rat(cint x, 1) else Rat(cint x, cint y)

// Due to the constraints imposed by the game (reduced set
// of operators, all left associativ) we can get away with a repeated reduction
// to evaluate the algebraic expression.
let rec reduce (str :string) =
    let eval (x : Rat) (y : Rat) = function
    | "*" -> x * y | "/" -> x / y | "+" -> x + y | "-" -> x - y | _ -> failwith "unknown op"
    let subst s r = str.Replace(s, r.ToString())
    let rstr =
        match str with
        | Parse @"\(<(-?\d+),(\d+)>([*/+-])<(-?\d+),(\d+)>\)" [matched; xn; xd; op; yn; yd] ->
            subst matched <| eval (Rat(xn,xd)) (Rat(yn,yd)) op
        | Parse @"<(-?\d+),(\d+)>([*/])<(-?\d+),(\d+)>" [matched; xn; xd; op; yn; yd] ->
            subst matched <| eval (Rat(xn,xd)) (Rat(yn,yd)) op
        | Parse @"<(-?\d+),(\d+)>([+-])<(-?\d+),(\d+)>" [matched; xn; xd; op; yn; yd] ->
            subst matched <| eval (Rat(xn,xd)) (Rat(yn,yd)) op
        | Parse @"\(<(-?\d+),(\d+)>\)" [matched; xn; xd] ->
            subst matched <| Rat(xn,xd)
        | Parse @"(?<!>)-<(-?\d+),(\d+)>" [matched; xn; xd] ->
            subst matched <| -Rat(xn,xd)
        | _ -> str
    if str = rstr then str else reduce rstr

let gameLoop() =
    let checkInput dddd input =
        match input with
        | "n" | "q" -> Some(input)
        | Parse @"[^1-9()*/+-]" [c] ->
            printfn "You used an illegal character in your expression: %s" c
            None
        | Parse @"^\D*(\d)\D+(\d)\D+(\d)\D+(\d)(?:\D*(\d))*\D*$" [m; d1; d2; d3; d4; d5] ->
            if d5 = "" && (String.Join(" ", Array.sort [|d1;d2;d3;d4|])) = dddd then Some(input)
            elif d5 = "" then
                printfn "Use this 4 digits with operators in between: %s." dddd
                None
            else
                printfn "Use only this 4 digits with operators in between: %s." dddd
                None
        | _ ->
            printfn "Use all 4 digits with operators in between: %s." dddd
            None

    let rec userLoop dddd  =
        let tryAgain msg =
            printfn "%s" msg
            userLoop dddd
        printf "[Expr|n|q]: "
        match Console.ReadLine() |> replace @"\s" "" |> checkInput dddd with
        | Some(input) ->
            let data = input |> replace @"((?<!\d)-)?\d+" @"<$&,1>"
            match data with
            | "n" -> true | "q" -> false
            | _ ->
                try
                    match reduce data with
                    | Parse @"^<(-?\d+),(\d+)>$" [_; x; y] ->
                        let n, d = (cint x), (cint y)
                        if n = 24 then
                            printfn "Correct!"
                            true
                        elif d=1 then tryAgain <| sprintf "Wrong! Value = %d." n
                        else tryAgain <| sprintf "Wrong! Value = %d/%d." n d
                    | _ -> tryAgain "Wrong! not a well-formed expression!"
                with
                    | :? System.DivideByZeroException ->
                        tryAgain "Wrong! Your expression results in a division by zero!"
                    | ex ->
                        tryAgain <| sprintf "There is an unforeseen problem with yout input: %s" ex.Message
        | None -> userLoop dddd

    let random = new Random(DateTime.Now.Millisecond)
    let rec loop() =
        let dddd = String.Join(" ", Array.init 4 (fun _ -> 1 + random.Next 9) |> Array.sort)
        printfn "\nCompute 24 from the following 4 numbers: %s" dddd
        printfn "Use them in any order with * / + - and parentheses; n = new numbers; q = quit"
        if userLoop dddd then loop()

    loop()

gameLoop()


USING:
    combinators.short-circuit
    continuations
    eval
    formatting
    fry
    kernel
    io
    math math.ranges
    prettyprint
    random
    sequences
    sets ;
IN: 24game

: choose4 ( -- seq )
    4 [ 9 [1,b] random ] replicate ;

: step ( numbers -- ? )
    readln
    [
        parse-string
        {
            ! Is only allowed tokens used?
            [ swap { + - / * } append subset? ]
            ! Digit count in expression should be equal to the given numbers.
            [ [ number? ] count swap length = ]
            ! Of course it must evaluate to 24
            [ nip call( -- x ) 24 = ]
        } 2&&
        [ f "You got it!" ]
        [ t "Expression isnt valid, or doesnt evaluate to 24." ]
        if
    ]
    [ 3drop f "Could not parse that." ]
    recover print flush ;

: main ( -- )
    choose4
    [ "Your numbers are %[%s, %], make an expression\n" printf flush ]
    [ '[ _ step ] loop ]
    bi ;


load compiler

function genRandomNumbers( amount )
  rtn = []
  for i in [ 0 : amount ]: rtn += random( 1, 9 )
  return( rtn )
end

function getAnswer( exp )
  ic = ICompiler()
  ic.compileAll(exp)

  return( ic.result )
end

function validInput( str )
  for i in [ 0 : str.len() ]
    if str[i] notin ' ()[]0123456789-+/*'
      > 'INVALID Character = ', str[i]
      return( false )
    end
  end

  return( true )
end

printl('
The 24 Game

Given any four digits in the range 1 to 9, which may have repetitions,
Using just the +, -, *, and / operators; and the possible use of
brackets, (), show how to make an answer of 24.

An answer of "q" will quit the game.
An answer of "!" will generate a new set of four digits.
Otherwise you are repeatedly asked for an expression until it evaluates to 24

Note: you cannot form multiple digit numbers from the supplied digits,
so an answer of 12+12 when given 1, 2, 2, and 1 would not be allowed.
')

num = genRandomNumbers( 4 )

while( true )

  >>  "Here are the numbers to choose from: "
  map({ a => print(a, " ") }, num)
  >

  exp = input()

  switch exp
    case "q", "Q"
      exit()

    case "!"
      > 'Generating new numbers list'
      num = genRandomNumbers( 4 )

    default
      if not validInput( exp ): continue

      answer = getAnswer( exp )

      if answer == 24
        > "By George you GOT IT! Your expression equals 24"
      else
        > "Ahh Sorry, So Sorry your answer of ", answer, " does not equal 24."
      end
  end
end


program game_24
  implicit none
  real               :: vector(4), reals(11), result, a, b, c, d
  integer            :: numbers(4), ascii(11), i
  character(len=11)  :: expression
  character          :: syntax(11)
  ! patterns:
  character, parameter :: one(11)   = (/ '(','(','1','x','1',')','x','1',')','x','1' /)
  character, parameter :: two(11)   = (/ '(','1','x','(','1','x','1',')',')','x','1' /)
  character, parameter :: three(11) = (/ '1','x','(','(','1','x','1',')','x','1',')' /)
  character, parameter :: four(11)  = (/ '1','x','(','1','x','(','1','x','1',')',')' /)
  character, parameter :: five(11)  = (/ '(','1','x','1',')','x','(','1','x','1',')' /)

  do
    call random_number(vector)
    numbers = 9 * vector + 1
    write (*,*) 'Digits: ',numbers
    write (*,'(a)',advance='no') 'Your expression: '
    read (*,'(a11)') expression

    forall (i=1:11) syntax(i) = expression(i:i)
    ascii = iachar(syntax)
    where (syntax >= '0' .and. syntax <= '9')
      syntax = '1'  ! number
    elsewhere (syntax == '+' .or. syntax == '-' .or. syntax == '*' .or. syntax == '/')
      syntax = 'x'  ! op
    elsewhere (syntax /= '(' .and. syntax /= ')')
      syntax = '-'  ! error
    end where

    reals = real(ascii-48)
    if ( all(syntax == one) ) then
      a = reals(3); b = reals(5); c = reals(8); d = reals(11)
      call check_numbers(a,b,c,d)
      result = op(op(op(a,4,b),7,c),10,d)
    else if ( all(syntax == two) ) then
      a = reals(2); b = reals(5); c = reals(7); d = reals(11)
      call check_numbers(a,b,c,d)
      result = op(op(a,3,op(b,6,c)),10,d)
    else if ( all(syntax == three) ) then
      a = reals(1); b = reals(5); c = reals(7); d = reals(10)
      call check_numbers(a,b,c,d)
      result = op(a,2,op(op(b,6,c),9,d))
    else if ( all(syntax == four) ) then
      a = reals(1); b = reals(4); c = reals(7); d = reals(9)
      call check_numbers(a,b,c,d)
      result = op(a,2,op(b,5,op(c,8,d)))
    else if ( all(syntax == five) ) then
      a = reals(2); b = reals(4); c = reals(8); d = reals(10)
      call check_numbers(a,b,c,d)
      result = op(op(a,3,b),6,op(c,9,d))
    else
      stop 'Input string: incorrect syntax.'
    end if

    if ( abs(result-24.0) < epsilon(1.0) ) then
      write (*,*) 'You won!'
    else
      write (*,*) 'Your result (',result,') is incorrect!'
    end if

    write (*,'(a)',advance='no') 'Another one? [y/n] '
    read (*,'(a1)') expression
    if ( expression(1:1) == 'n' .or. expression(1:1) == 'N' ) then
      stop
    end if
  end do

contains

  pure real function op(x,c,y)
    integer, intent(in) :: c
    real, intent(in) :: x,y
    select case ( char(ascii(c)) )
      case ('+')
        op = x+y
      case ('-')
        op = x-y
      case ('*')
        op = x*y
      case ('/')
        op = x/y
    end select
  end function op

  subroutine check_numbers(a,b,c,d)
    real, intent(in) :: a,b,c,d
    integer          :: test(4)
    test = (/ nint(a),nint(b),nint(c),nint(d) /)
    call Insertion_Sort(numbers)
    call Insertion_Sort(test)
    if ( any(test /= numbers) ) then
      stop 'You cheat ;-) (Incorrect numbers)'
    end if
  end subroutine check_numbers

  pure subroutine Insertion_Sort(a)
    integer, intent(inout) :: a(:)
    integer                :: temp, i, j
    do i=2,size(a)
      j = i-1
      temp = a(i)
      do while ( j>=1 .and. a(j)>temp )
        a(j+1) = a(j)
        j = j - 1
      end do
      a(j+1) = temp
    end do
  end subroutine Insertion_Sort

end program game_24


' The 24 game en FreeBASIC

Const operaciones = "*/+-"

Declare Sub Encabezado
Declare Function escoge4() As String
Declare Function quitaEspacios(cadena As String, subcadena1 As String, subcadena2 As String) As String
Declare Function evaluaEntrada(cadena As String) As Integer
Declare Function evaluador(oper1 As Byte, oper2 As Byte, operacion As String) As Integer

Dim Shared As String serie, entrada, cadena
Dim As Integer resultado

Sub Encabezado
    Cls: Color 15
    Print "The 24 Game"
    Print "============" + Chr(13) + Chr(10)
    Print "Dados cuatro dígitos en el rango de 1 a 9, que pueden repetirse, "
    Print "usando solo los operadores aritméticos suma (+), resta (-), "
    Print "multiplicación (*) y división (/) intentar obtener un resultado de 24." + Chr(13) + Chr(10)
    Print "Use la notación polaca inversa (primero los operandos y luego los operadores)."
    Print "Por ejemplo: en lugar de 2 + 4, escriba 2 4 +" + Chr(13) + Chr(10)
End Sub

Function escoge4() As String
    Dim As Byte i
    Dim As String a, b

    Print "Los dígitos a utilizar son:   ";
    For i = 1 To 4
	a = Str(Int(Rnd*9)+1)
	Print a;"      ";
	b = b + a
    Next i
    escoge4 = b
End Function

Function evaluaEntrada(cadena As String) As Integer
    Dim As Byte oper1, oper2, n(4), i
    Dim As String op
    oper1 = 0: oper2 = 0: i = 0

    While cadena <> ""
	op = Left(cadena, 1)
	entrada = Mid(cadena, 2)
	If Instr(serie, op) Then
		i = i + 1
		n(i) = Val(op)
        Elseif Instr(operaciones, op) Then
		oper2 = n(i)
		n(i) = 0
		i = i - 1
		oper1 = n(i)
		n(i) = evaluador(oper1, oper2, op)
        Else
		Print "Signo no v lido"
        End If
    Wend
    evaluaEntrada = n(i)
End Function

Function evaluador(oper1 As Byte, oper2 As Byte, operacion As String) As Integer
    Dim As Integer t

    Select Case operacion
    Case "+": t = oper1 + oper2
    Case "-": t = oper1 - oper2
    Case "*": t = oper1 * oper2
    Case "/": t = oper1 / oper2
    End Select

    evaluador = t
End Function

Function quitaEspacios(cadena As String, subcadena1 As String, subcadena2 As String) As String
    Dim As Byte len1 = Len(subcadena1), len2 = Len(subcadena2)
    Dim As Byte i

    i = Instr(cadena, subcadena1)
    While i
        cadena = Left(cadena, i - 1) & subcadena2 & Mid(cadena, i + len1)
        i = Instr(i + len2, cadena, subcadena1)
    Wend
    quitaEspacios = cadena
End Function

'--- Programa Principal ---
Randomize Timer
Do
    Encabezado
    serie = escoge4
    Print: Line Input "Introduzca su fórmula en notación polaca inversa: ", entrada
    entrada = quitaEspacios(entrada, " ", "")
    If (Len(entrada) <> 7) Then
        Print "Error en la serie introducida."
    Else
        resultado = evaluaEntrada(entrada)
        Print "El resultado es = "; resultado
        If resultado = 24 Then
            Print "¡Correcto!"
        Else
            Print "¡Error!"
        End If
    End If
    Print "¿Otra ronda? (Pulsa S para salir, u otra tecla para continuar)"
Loop Until (Ucase(Input(1)) = "S")
End
'--------------------------


Play24 := function()
	local input, digits, line, c, chars, stack, stackptr, cur, p, q, ok, a, b, run;
	input := InputTextUser();
	run := true;
	while run do
		digits := List([1 .. 4], n -> Random(1, 9));
		while true do
			Display(digits);
			line := ReadLine(input);
			line := Chomp(line);
			if line = "end" then
				run := false;
				break;
			elif line = "next" then
				break;
			else
				ok := true;
				stack := [ ];
				stackptr := 0;
				chars := "123456789+-*/ ";
				cur := ShallowCopy(digits);
				for c in line do
					if c = ' ' then
						continue;
					fi;
					p := Position(chars, c);
					if p = fail then
						ok := false;
						break;
					fi;
					if p < 10 then
						q := Position(cur, p);
						if q = fail then
							ok := false;
							break;
						fi;
						Unbind(cur[q]);
						stackptr := stackptr + 1;
						stack[stackptr] := p;
					else
						if stackptr < 2 then
							ok := false;
							break;
						fi;
						b := stack[stackptr];
						a := stack[stackptr - 1];
						stackptr := stackptr - 1;
						if c = '+' then
							a := a + b;
						elif c = '-' then
							a := a - b;
						elif c = '*' then
							a := a * b;
						elif c = '/' then
							if b = 0 then
								ok := false;
								break;
							fi;
							a := a / b;
						else
							ok := false;
							break;
						fi;
						stack[stackptr] := a;
					fi;
				od;
				if ok and stackptr = 1 and Size(cur) = 0 then
					if stack[1] = 24 then
						Print("Good !\n");
						break;
					else
						Print("Bad value: ", stack[1], "\n");
						continue;
					fi;
				fi;
				Print("Invalid expression\n");
			fi;
		od;
	od;
	CloseStream(input);
end;

# example session
# type "end" to quit the game, "next" to try another list of digits
gap> Play24();
[ 7, 6, 8, 5 ]
86*75-/
Good !
[ 5, 9, 2, 7 ]
end
gap>


package main

import (
    "fmt"
    "math"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().Unix())
    n := make([]rune, 4)
    for i := range n {
        n[i] = rune(rand.Intn(9) + '1')
    }
    fmt.Printf("Your numbers: %c\n", n)
    fmt.Print("Enter RPN: ")
    var expr string
    fmt.Scan(&expr)
    if len(expr) != 7 {
        fmt.Println("invalid. expression length must be 7." +
            " (4 numbers, 3 operators, no spaces)")
        return
    }
    stack := make([]float64, 0, 4)
    for _, r := range expr {
        if r >= '0' && r <= '9' {
            if len(n) == 0 {
                fmt.Println("too many numbers.")
                return
            }
            i := 0
            for n[i] != r {
                i++
                if i == len(n) {
                    fmt.Println("wrong numbers.")
                    return
                }
            }
            n = append(n[:i], n[i+1:]...)
            stack = append(stack, float64(r-'0'))
            continue
        }
        if len(stack) < 2 {
            fmt.Println("invalid expression syntax.")
            return
        }
        switch r {
        case '+':
            stack[len(stack)-2] += stack[len(stack)-1]
        case '-':
            stack[len(stack)-2] -= stack[len(stack)-1]
        case '*':
            stack[len(stack)-2] *= stack[len(stack)-1]
        case '/':
            stack[len(stack)-2] /= stack[len(stack)-1]
        default:
            fmt.Printf("%c invalid.\n", r)
            return
        }
        stack = stack[:len(stack)-1]
    }
    if math.Abs(stack[0]-24) > 1e-6 {
        fmt.Println("incorrect.", stack[0], "!= 24")
    } else {
        fmt.Println("correct.")
    }
}


uses java.lang.Double
uses java.lang.Integer
uses java.util.ArrayList
uses java.util.List
uses java.util.Scanner
uses java.util.Stack

function doEval( scanner : Scanner, allowed : List<Integer> ) : double {
    var stk = new Stack<Double>()

    while( scanner.hasNext() ) {
        if( scanner.hasNextInt() ) {
            var n = scanner.nextInt()

            // Make sure they're allowed to use n
            if( n <= 0 || n >= 10 ) {
                print( n + " isn't allowed" )
                return 0
            }
            var idx = allowed.indexOf( n )
            if( idx == -1 ) {
                print( "You aren't allowed to use so many " + n + "s!" )
                return 0
            }

            // Add the input number to the stack
            stk.push( new Double( n ) )

            // Mark n as used
            allowed.remove( idx )
        } else {
            // It has to be an operator...
            if( stk.size() < 2 ) {
                print( "Invalid Expression: Stack underflow!" )
                return 0
            }

            // Gets the next operator as a single character token
            var s = scanner.next("[\\+-/\\*]")

            // Get the operands
            var r = stk.pop().doubleValue()
            var l = stk.pop().doubleValue()

            // Determine which operator and invoke it
            if( s.equals( "+" ) ) {
                stk.push( new Double( l + r ) )
            } else if( s.equals( "-" ) ) {
                stk.push( new Double( l - r ) )
            } else if( s.equals( "*" ) ) {
                stk.push( new Double( l * r ) )
            } else if( s.equals( "/" ) ) {
                if( r == 0.0 ) {
                    print( "Invalid Expression: Division by zero!" )
                    return 0
                }
                stk.push( new Double( l / r ) )
            } else {
                print( "Internal Error: looking for operator yielded '" + s + "'" )
                return 0
            }
        }
    }

    // Did they skip any numbers?
    if( allowed.size() != 0 ) {
        print( "You didn't use ${allowed}" )
        return 0
    }

    // Did they use enough operators?
    if( stk.size() != 1 ) {
        print( "Invalid Expression: Not enough operators!" )
        return 0
    }

    return stk.pop().doubleValue()
}

// Pick 4 random numbers from [1..9]
var nums = new ArrayList<Integer>()
var gen = new java.util.Random( new java.util.Date().getTime() )
for( i in 0..3 ) {
    nums.add( gen.nextInt(9) + 1 )
}

// Prompt the user
print( "Using addition, subtraction, multiplication and division, write an" )
print( "expression that evaluates to 24 using" )
print( "${nums.get(0)}, ${nums.get(1)}, ${nums.get(2)} and ${nums.get(3)}" )
print( "" )
print( "Please enter your expression in RPN" )

// Build a tokenizer over a line of input
var sc = new Scanner( new java.io.BufferedReader( new java.io.InputStreamReader( java.lang.System.in ) ).readLine() )

// eval the expression
var val = doEval( sc, nums )

// winner?
if( java.lang.Math.abs( val - 24.0 ) < 0.001 ) {
    print( "You win!" )
} else {
    print( "You lose!" )
}


final random = new Random()
final input = new Scanner(System.in)

def evaluate = { expr ->
    if (expr == 'QUIT') {
        return 'QUIT'
    } else {
        try { Eval.me(expr.replaceAll(/(\d)/, '$1.0')) }
        catch (e) { 'syntax error' }
    }
}

def readGuess = { digits ->
    while (true) {
        print "Enter your guess using ${digits} (q to quit): "
        def expr = input.nextLine()

        switch (expr) {
            case ~/^[qQ].*/:
                return 'QUIT'

            case ~/.*[^\d\s\+\*\/\(\)-].*/:
                def badChars = expr.replaceAll(~/[\d\s\+\*\/\(\)-]/, '')
                println "invalid characters in input: ${(badChars as List) as Set}"
                break

            case { (it.replaceAll(~/\D/, '') as List).sort() != ([]+digits).sort() }:
                println '''you didn't use the right digits'''
                break

            case ~/.*\d\d.*/:
                println 'no multi-digit numbers allowed'
                break

            default:
                return expr
        }
    }
}

def digits = (1..4).collect { (random.nextInt(9) + 1) as String }

while (true) {
    def guess = readGuess(digits)
    def result = evaluate(guess)

    switch (result) {
        case 'QUIT':
            println 'Awwww. Maybe next time?'
            return

        case 24:
            println 'Yes! You got it.'
            return

        case 'syntax error':
            println "A ${result} was found in ${guess}"
            break

        default:
            println "Nope: ${guess} == ${result}, not 24"
            println 'One more try, then?'
    }
}


import Data.List (sort)
import Data.Char (isDigit)
import Data.Maybe (fromJust)
import Control.Monad (foldM)
import System.Random (randomRs, getStdGen)
import System.IO (hSetBuffering, stdout, BufferMode(NoBuffering))

main = do
  hSetBuffering stdout NoBuffering
  mapM_
    putStrLn
    [ "THE 24 GAME\n"
    , "Given four digits in the range 1 to 9"
    , "Use the +, -, *, and / operators in reverse polish notation"
    , "To show how to make an answer of 24.\n"
    ]
  digits <- fmap (sort . take 4 . randomRs (1, 9)) getStdGen :: IO [Int]
  putStrLn ("Your digits: " ++ unwords (fmap show digits))
  guessLoop digits
  where
    guessLoop digits =
      putStr "Your expression: " >> fmap (processGuess digits . words) getLine >>=
      either (\m -> putStrLn m >> guessLoop digits) putStrLn

processGuess _ [] = Right ""
processGuess digits xs
  | not matches = Left "Wrong digits used"
  where
    matches = digits == (sort . fmap read $ filter (all isDigit) xs)
processGuess digits xs = calc xs >>= check
  where
    check 24 = Right "Correct"
    check x = Left (show (fromRational (x :: Rational)) ++ " is wrong")

-- A Reverse Polish Notation calculator with full error handling
calc xs =
  foldM simplify [] xs >>=
  \ns ->
     (case ns of
        [n] -> Right n
        _ -> Left "Too few operators")

simplify (a:b:ns) s
  | isOp s = Right ((fromJust $ lookup s ops) b a : ns)
simplify _ s
  | isOp s = Left ("Too few values before " ++ s)
simplify ns s
  | all isDigit s = Right (fromIntegral (read s) : ns)
simplify _ s = Left ("Unrecognized symbol: " ++ s)

isOp v = elem v $ fmap fst ops

ops = [("+", (+)), ("-", (-)), ("*", (*)), ("/", (/))]


DIMENSION digits(4), input_digits(100), difference(4)
CHARACTER expression*100, prompt*100, answers='Wrong,Correct,', protocol='24 game.txt'

1  digits = CEILING( RAN(9) )
2  DLG(Edit=expression, Text=digits, TItle=prompt)

   READ(Text=expression, ItemS=n) input_digits
   IF(n == 4) THEN
     ALIAS(input_digits,1,  input,4)
     SORT(Vector=digits, Sorted=digits)
     SORT(Vector=input, Sorted=input)
     difference = ABS(digits - input)
     IF( SUM(difference) == 0 ) THEN
       EDIT(Text=expression, ScaNnot='123456789+-*/ ()', GetPos=i, CoPyto=prompt)
       IF( i > 0 ) THEN
         prompt = TRIM(expression) // ': ' //TRIM(prompt) // ' is an illegal character'
       ELSE
         prompt = TRIM(expression) // ': Syntax error'
         result = XEQ(expression, *2) ! on error branch to label 2
         EDIT(Text=answers, ITeM=(result==24)+1, Parse=answer)
         WRITE(Text=prompt, Name) TRIM(expression)//': ', answer, result
       ENDIF
     ELSE
       WRITE(Text=prompt) TRIM(expression), ': You used ', input, ' instead ', digits
     ENDIF
   ELSE
     prompt = TRIM(expression) // ': Instead 4 digits you used ' // n
   ENDIF

   OPEN(FIle=protocol, APPend)
   WRITE(FIle=protocol, CLoSe=1) prompt

   DLG(TItle=prompt, Button='>2:Try again', B='>1:New game', B='Quit')

END


#! /bin/sh
exec huginn --no-argv -E "${0}"
#! huginn

import Algorithms as algo;
import Mathematics as math;
import RegularExpressions as re;

make_game( rndGen_ ) {
  board = "";
  for ( i : algo.range( 4 ) ) {
    board += ( " " + string( character( rndGen_.next() + integer( '1' ) ) ) );
  }
  return ( board.strip() );
}

main() {
  rndGen = math.randomizer( 9 );
  no = 0;
  dd = re.compile( "\\d\\d" );
  while ( true ) {
    no += 1;
    board = make_game( rndGen );
    print( "Your four digits: {}\nExpression {}: ".format( board, no ) );
    line = input();
    if ( line == none ) {
      print( "\n" );
      break;
    }
    line = line.strip();
    try {
      if ( line == "q" ) {
        break;
      }
      if ( ( pos = line.find_other_than( "{}+-*/() ".format( board ) ) ) >= 0 ) {
        print( "Invalid input found at: {}, `{}`\n".format( pos, line ) );
        continue;
      }
      if ( dd.match( line ).matched() ) {
        print( "Digit concatenation is forbidden.\n" );
        continue;
      }
      res = real( line );
      if ( res == 24.0 ) {
        print( "Thats right!\n" );
      } else {
        print( "Bad answer!\n" );
      }
    } catch ( Exception e ) {
      print( "Not an expression: {}\n".format( e.what() ) );
    }
  }
  return ( 0 );
}


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


require'misc'
deal=: 1 + ? bind 9 9 9 9
rules=: smoutput bind 'see http://en.wikipedia.org/wiki/24_Game'
input=: prompt @ ('enter 24 expression using ', ":, ': '"_)

wellformed=: (' '<;._1@, ":@[) -:&(/:~)  '(+-*%)' -.&;:~ ]
is24=: 24 -: ". ::0:@]

respond=: (;:'no yes') {::~ wellformed * is24

game24=: (respond input)@[email protected]


import java.util.*;

public class Game24 {
    static Random r = new Random();

    public static void main(String[] args) {

        int[] digits = randomDigits();
        Scanner in = new Scanner(System.in);

        System.out.print("Make 24 using these digits: ");
        System.out.println(Arrays.toString(digits));
        System.out.print("> ");

        Stack<Float> s = new Stack<>();
        long total = 0;
        for (char c : in.nextLine().toCharArray()) {
            if ('0' <= c && c <= '9') {
                int d = c - '0';
                total += (1 << (d * 5));
                s.push((float) d);
            } else if ("+/-*".indexOf(c) != -1) {
                s.push(applyOperator(s.pop(), s.pop(), c));
            }
        }
        if (tallyDigits(digits) != total)
            System.out.print("Not the same digits. ");
        else if (Math.abs(24 - s.peek()) < 0.001F)
            System.out.println("Correct!");
        else
            System.out.print("Not correct.");
    }

    static float applyOperator(float a, float b, char c) {
        switch (c) {
            case '+':
                return a + b;
            case '-':
                return b - a;
            case '*':
                return a * b;
            case '/':
                return b / a;
            default:
                return Float.NaN;
        }
    }

    static long tallyDigits(int[] a) {
        long total = 0;
        for (int i = 0; i < 4; i++)
            total += (1 << (a[i] * 5));
        return total;
    }

    static int[] randomDigits() {
        int[] result = new int[4];
        for (int i = 0; i < 4; i++)
            result[i] = r.nextInt(9) + 1;
        return result;
    }
}


function twentyfour(numbers, input) {
    var invalidChars = /[^\d\+\*\/\s-\(\)]/;

    var validNums = function(str) {
        // Create a duplicate of our input numbers, so that
        // both lists will be sorted.
        var mnums = numbers.slice();
        mnums.sort();

        // Sort after mapping to numbers, to make comparisons valid.
        return str.replace(/[^\d\s]/g, " ")
            .trim()
            .split(/\s+/)
            .map(function(n) { return parseInt(n, 10); })
            .sort()
            .every(function(v, i) { return v === mnums[i]; });
    };

    var validEval = function(input) {
        try {
            return eval(input);
        } catch (e) {
            return {error: e.toString()};
        }
    };

    if (input.trim() === "") return "You must enter a value.";
    if (input.match(invalidChars)) return "Invalid chars used, try again. Use only:\n + - * / ( )";
    if (!validNums(input)) return "Wrong numbers used, try again.";
    var calc = validEval(input);
    if (typeof calc !== 'number') return "That is not a valid input; please try again.";
    if (calc !== 24) return "Wrong answer: " + String(calc) + "; please try again.";
    return input + " == 24.  Congratulations!";
};

// I/O below.

while (true) {
    var numbers = [1, 2, 3, 4].map(function() {
        return Math.floor(Math.random() * 8 + 1);
    });

    var input = prompt(
        "Your numbers are:\n" + numbers.join(" ") +
        "\nEnter expression. (use only + - * / and parens).\n", +"'x' to exit.", "");

    if (input === 'x') {
        break;
    }
    alert(twentyfour(numbers, input));
}


validexpr(ex::Expr) = ex.head == :call && ex.args[1] in [:*,:/,:+,:-] && all(validexpr, ex.args[2:end])
validexpr(ex::Int) = true
validexpr(ex::Any) = false
findnumbers(ex::Number) = Int[ex]
findnumbers(ex::Expr) = vcat(map(findnumbers, ex.args)...)
findnumbers(ex::Any) = Int[]
function twentyfour()
    digits = sort!(rand(1:9, 4))
    while true
        print("enter expression using $digits => ")
        ex = parse(readline())
        try
            validexpr(ex) || error("only *, /, +, - of integers is allowed")
            nums = sort!(findnumbers(ex))
            nums == digits || error("expression $ex used numbers $nums != $digits")
            val = eval(ex)
            val == 24 || error("expression $ex evaluated to $val, not 24")
            println("you won!")
            return
        catch e
            if isa(e, ErrorException)
                println("incorrect: ", e.msg)
            else
                rethrow()
            end
        end
    end
end


import java.util.Random
import java.util.Scanner
import java.util.Stack

internal object Game24 {
    fun run() {
        val r = Random()
        val digits = IntArray(4).map { r.nextInt(9) + 1 }
        println("Make 24 using these digits: $digits")
        print("> ")

        val s = Stack<Float>()
        var total = 0L
        val cin = Scanner(System.`in`)
        for (c in cin.nextLine()) {
            when (c) {
                in '0'..'9' -> {
                    val d = c - '0'
                    total += (1 shl (d * 5)).toLong()
                    s += d.toFloat()
                }
                else ->
                    if ("+/-*".indexOf(c) != -1) {
                        s += c.applyOperator(s.pop(), s.pop())
                    }
            }
        }

        when {
            tally(digits) != total ->
                print("Not the same digits. ")
            s.peek().compareTo(target) == 0 ->
                println("Correct!")
            else ->
                print("Not correct.")
        }
    }

    private fun Char.applyOperator(a: Float, b: Float) = when (this) {
        '+' -> a + b
        '-' -> b - a
        '*' -> a * b
        '/' -> b / a
        else -> Float.NaN
    }

    private fun tally(a: List<Int>): Long = a.reduce({ t, i -> t + (1 shl (i * 5)) }).toLong()

    private val target = 24
}

fun main(args: Array<String>) = Game24.run()


[
if(sys_listunboundmethods !>> 'randoms') => {
	define randoms()::array => {
		local(out = array)
		loop(4) => { #out->insert(math_random(9,1)) }
		return #out
	}
}
if(sys_listunboundmethods !>> 'checkvalid') => {
	define checkvalid(str::string, nums::array)::boolean => {
		local(chk = array('*','/','+','-','(',')',' '), chknums = array, lastintpos = -1, poscounter = 0)
		loop(9) => { #chk->insert(loop_count) }
		with s in #str->values do => {
			#poscounter++
			#chk !>> #s && #chk !>> integer(#s) ? return false
			integer(#s) > 0 && #lastintpos + 1 >= #poscounter ? return false
			integer(#s) > 0 ? #chknums->insert(integer(#s))
			integer(#s) > 0 ? #lastintpos = #poscounter
		}
		#chknums->size != 4 ? return false
		#nums->sort
		#chknums->sort
		loop(4) => { #nums->get(loop_count) != #chknums(loop_count) ? return false }
		return true
	}
}
if(sys_listunboundmethods !>> 'executeexpr') => {
	define executeexpr(expr::string)::integer => {
		local(keep = string)
		with i in #expr->values do => {
			if(array('*','/','+','-','(',')') >> #i) => {
				#keep->append(#i)
			else
				integer(#i) > 0 ? #keep->append(decimal(#i))
			}
		}
		return integer(sourcefile('['+#keep+']','24game',true,true)->invoke)
	}
}

local(numbers = array, exprsafe = true, exprcorrect = false, exprresult = 0)
if(web_request->param('nums')->asString->size) => {
	with n in web_request->param('nums')->asString->split(',') do => { #numbers->insert(integer(#n->trim&)) }
}
#numbers->size != 4 ? #numbers = randoms()
if(web_request->param('nums')->asString->size) => {
	#exprsafe = checkvalid(web_request->param('expr')->asString,#numbers)
	if(#exprsafe) => {
		#exprresult = executeexpr(web_request->param('expr')->asString)
		#exprresult == 24 ? #exprcorrect = true
	}
}

]
24 Game

Rules:

Enter an expression that evaluates to 24

Only multiplication, division, addition, and subtraction operators/functions are allowed.

Brackets are allowed.

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.

Numbers

[#numbers->join(', ')] (Reload)

[!#exprsafe ? '
Please provide a valid expression
']
<form><input type="hidden" value="[#numbers->join(',')]" name="nums"><input type="text" name="expr" value="[web_request->param('expr')->asString]"><input type="submit" name="submit" value="submit"></form>
[if(#exprsafe)]

Result: [#exprresult] [#exprcorrect ? 'is CORRECT!' | 'is incorrect']

[/if]


dim d(4)
dim chk(4)
print "The 24 Game"
print
print "Given four digits and using just the +, -, *, and / operators; and the"
print "possible use of brackets, (), enter an expression that equates to 24."

do
    d$=""
    for i = 1 to 4
        d(i)=int(rnd(1)*9)+1    '1..9
        chk(i)=d(i)
        d$=d$;d(i)  'valid digits, to check with Instr
    next

    print
    print "These are your four digits: ";
    for i = 1 to 4
        print d(i);left$(",",i<>4);
    next
    print

    Print "Enter expression:"
    Input "24 = ";expr$
    'check expr$ for validity

    'check right digits used
    failed = 0
    for i = 1 to len(expr$)
        c$=mid$(expr$,i,1)
        if instr("123456789", c$)<>0 then 'digit
            if instr(d$, c$)=0 then failed = 1: exit for
            if i>1 and instr("123456789", mid$(expr$,i-1,1))<>0 then failed = 2: exit for
            for j =1 to 4
                if chk(j)=val(c$) then chk(j)=0: exit for
            next
        end if
    next
    if failed=1 then
        print "Wrong digit (";c$;")"
        goto [fail]
    end if

    if failed=2 then
        print "Multiple digit numbers is disallowed."
        goto [fail]
    end if

    'check all digits used
    if chk(1)+ chk(2)+ chk(3)+ chk(4)<>0 then
        print "Not all digits used"
        goto [fail]
    end if

    'check valid operations
    failed = 0
    for i = 1 to len(expr$)
        c$=mid$(expr$,i,1)
        if instr("+-*/()"+d$, c$)=0 then failed = 1: exit for
    next
    if failed then
        print "Wrong operation (";c$;")"
        goto [fail]
    end if
    'some errors (like brackets) trapped by error handler
    Err$=""
    res=evalWithErrCheck(expr$)
    if Err$<>"" then
        print "Error in expression"
        goto [fail]
    end if
    if res = 24 then
        print "Correct!"
    else
        print "Wrong! (you got ";res ;")"
    end if
[fail]
    Input "Play again (y/n)? "; ans$
loop while ans$="y"
end

function evalWithErrCheck(expr$)
    on error goto [handler]
    evalWithErrCheck=eval(expr$)
    exit function
[handler]
end function


on mouseUp
    put empty into fld "EvalField"
    put empty into fld "AnswerField"
    put random(9) & comma & random(9) & comma & random(9) & comma & random(9) into fld "YourNumbersField"
end mouseUp


10 CLS:RANDOMIZE TIME
20 PRINT "The 24 Game"
30 PRINT "===========":PRINT
40 PRINT "Enter an arithmetic expression"
50 PRINT "that evaluates to 24,"
60 PRINT "using only the provided digits"
70 PRINT "and +, -, *, /, (, )."
80 PRINT "(Just hit Return for new digits.)"
90 ' create new digits
100 FOR i=1 TO 4:a(i)=INT(RND*9)+1:NEXT
110 PRINT
120 PRINT "The digits are";a(1);a(2);a(3);a(4)
130 PRINT
140 ' user enters solution
150 INPUT "Your solution";s$
160 IF s$="" THEN PRINT "Creating new digits...":GOTO 100
170 GOTO 300
180 ' a little hack to create something like an EVAL function
190 OPENOUT "exp.bas"
200 PRINT #9,"1000 x="s$":return"
210 CLOSEOUT
220 CHAIN MERGE "exp",240
230 ' now evaluate the expression
240 ON ERROR GOTO 530
250 GOSUB 1000
260 IF x=24 THEN PRINT "Well done!":END
270 PRINT "No, this evaluates to"x:PRINT "Please try again."
280 GOTO 150
290 ' check input for correctness
300 FOR i=1 TO LEN(s$)
310 q=ASC(MID$(s$,i,1))
320 IF q=32 OR (q>39 AND q<44) OR q=45 OR (q>46 AND q<58) THEN NEXT
330 IF i-1=LEN(s$) THEN 370
340 PRINT "Bad character in expression:"CHR$(q)
350 PRINT "Try again":GOTO 150
360 ' new numbers in solution?
370 FOR i=1 TO LEN(s$)-1
380 q=ASC(MID$(s$,i,1)):p=ASC(MID$(s$,i+1,1))
390 IF q>47 AND q<58 AND p>47 AND p<58 THEN PRINT "No forming of new numbers, please!":GOTO 150
400 NEXT
410 FOR i=1 TO 9:orig(i)=0:guess(i)=0:NEXT
420 FOR i=1 TO 4:orig(a(i))=orig(a(i))+1:NEXT
430 FOR i=1 TO LEN(s$)
440 v$=MID$(s$,i,1)
450 va=ASC(v$)-48
460 IF va>0 AND va<10 THEN guess(va)=guess(va)+1
470 NEXT
480 FOR i=1 TO 9
490 IF guess(i)<>orig(i) THEN PRINT "Only use all the provided digits!":GOTO 150
500 NEXT
510 GOTO 190
520 ' syntax error, e.g. non-matching parentheses
530 PRINT "Error in expression, please try again."
540 RESUME 150


; useful constants
make "false 1=0
make "true  1=1
make "lf char 10
make "sp char 32

; non-digits legal in expression
make "operators (lput sp [+ - * / \( \)])

; display help message
to show_help :digits
  type lf
  print sentence quoted [Using only these digits:] :digits
  print sentence quoted [and these operators:] [* / + -]
  print quoted [\(and parentheses as needed\),]
  print quoted [enter an arithmetic expression
     which evaluates to exactly 24.]
  type lf
  print quoted [Enter \"!\" to get fresh numbers.]
  print quoted [Enter \"q\" to quit.]
  type lf
end

make "digits []
make "done false
until [done] [

  if empty? digits [
    make "digits (map [(random 9) + 1] [1 2 3 4])
  ]

  (type "Solution sp "for sp digits "? sp )
  make "expression readrawline

  ifelse [expression = "?] [

    show_help digits

  ] [ifelse [expression = "q] [

    print "Bye!
    make "done true

  ] [ifelse [expression = "!] [

    make "digits []

  ] [
    make "exprchars ` expression
    make "allowed (sentence digits operators)

    ifelse (member? false (map [[c] member? c allowed] exprchars)) [
      (print quoted [Illegal character in input.])
    ] [
      catch "error [
        make "syntax_error true
        make "testval (run expression)
        make "syntax_error false
      ]
      ifelse syntax_error [
        (print quoted [Invalid expression.])
      ] [
        ifelse (testval = 24) [
          print quoted [You win!]
          make "done true
        ] [
          (print (sentence
            quoted [Incorrect \(got ] testval quoted [instead of 24\).]))
        ]
      ]
    ]
  ]]]
]
bye


local function help()
	print [[
 The 24 Game

 Given any four digits in the range 1 to 9, which may have repetitions,
 Using just the +, -, *, and / operators; and the possible use of
 brackets, (), show how to make an answer of 24.

 An answer of "q" will quit the game.
 An answer of "!" will generate a new set of four digits.

 Note: you cannot form multiple digit numbers from the supplied digits,
 so an answer of 12+12 when given 1, 2, 2, and 1 would not be allowed.

 ]]
end

local function generate(n)
	result = {}
	for i=1,n do
		result[i] = math.random(1,9)
	end
	return result
end

local function check(answer, digits)
	local adig = {}
	local ddig = {}
	local index
	local lastWasDigit = false
	for i=1,9 do adig[i] = 0 ddig[i] = 0 end
	allowed = {['(']=true,[')']=true,[' ']=true,['+']=true,['-']=true,['*']=true,['/']=true,['\t']=true,['1']=true,['2']=true,['3']=true,['4']=true,['5']=true,['6']=true,['7']=true,['8']=true,['9']=true}
	for i=1,string.len(answer) do
		if not allowed[string.sub(answer,i,i)] then
			return false
		end
		index = string.byte(answer,i)-48
		if index > 0 and index < 10 then
			if lastWasDigit then
				return false
			end
			lastWasDigit = true
			adig[index] = adig[index] + 1
		else
			lastWasDigit = false
		end
	end
	for i,digit in next,digits do
		ddig[digit] = ddig[digit]+1
	end
	for i=1,9 do
		if adig[i] ~= ddig[i] then
			return false
		end
	end
	return loadstring('return '..answer)()
end

local function game24()
	help()
	math.randomseed(os.time())
	math.random()
	local digits = generate(4)
	local trial = 0
	local answer = 0
	local ans = false
	io.write 'Your four digits:'
	for i,digit in next,digits do
		io.write (' ' .. digit)
	end
	print()
	while ans ~= 24 do
		trial = trial + 1
		io.write("Expression "..trial..": ")
		answer = io.read()
		if string.lower(answer) == 'q' then
			break
		end
		if answer == '!' then
			digits = generate(4)
			io.write ("New digits:")
			for i,digit in next,digits do
				io.write (' ' .. digit)
			end
			print()
		else
			ans = check(answer,digits)
			if ans == false then
				print ('The input '.. answer ..' was wonky!')
			else
				print (' = '.. ans)
				if ans == 24 then
					print ("Thats right!")
				end
			end
		end
	end
end
game24()


play24 := module()
	export ModuleApply;
	local cheating;
	cheating := proc(input, digits)
		local i, j, stringDigits;
		use StringTools in
			stringDigits := Implode([seq(convert(i, string), i in digits)]);
			for i in digits do
				for j in digits do
					if Search(cat(convert(i, string), j), input) > 0 then
						return true, ": Please don't combine digits to form another number."
					end if;
				end do;
			end do;
			for i in digits do
				if CountCharacterOccurrences(input, convert(i, string)) < CountCharacterOccurrences(stringDigits, convert(i, string)) then
					return true, ": Please use all digits.";
				end if;
			end do;
			for i in digits do
				if CountCharacterOccurrences(input, convert(i, string)) > CountCharacterOccurrences(stringDigits, convert(i, string)) then
					return true, ": Please only use a digit once.";
				end if;
			end do;
			for i in input do
				try
					if type(parse(i), numeric) and not member(parse(i), digits) then
						return true, ": Please only use the digits you were given.";
					end if;
				catch:
				end try;
			end do;
			return false, "";
		end use;
	end proc:

	ModuleApply := proc()
		local replay, digits, err, message, answer;
		randomize():
		replay := "":
		while not replay = "END" do
			if not replay = "YES" then
				digits := [seq(rand(1..9)(), i = 1..4)]:
			end if;
			err := true:
			while err do
				message := "";
				printf("Please make 24 from the digits: %a. Press enter for a new set of numbers or type END to quit\n", digits);
				answer := StringTools[UpperCase](readline());
				if not answer = "" and not answer = "END" then
					try
						if not type(parse(answer), numeric) then
							error;
						elif cheating(answer, digits)[1] then
							message := cheating(answer, digits)[2];
							error;
						end if;
						err := false;
					catch:
						printf("Invalid Input%s\n\n", message);
					end try;
				else
					err := false;
				end if;
			end do:
			if not answer = "" and not answer = "END" then
				if parse(answer) = 24 then
					printf("You win! Do you wish to play another game? (Press enter for a new set of numbers or END to quit.)\n");
					replay := StringTools[UpperCase](readline());
				else
					printf("Your expression evaluated to %a. Try again!\n", parse(answer));
					replay := "YES";
				end if;
			else
				replay := answer;
			end if;

			printf("\n");
		end do:
		printf("GAME OVER\n");
	end proc:
end module:

play24();


isLegal[n_List, x_String] :=
 Quiet[Check[
   With[{h = ToExpression[x, StandardForm, HoldForm]},
    If[Cases[Level[h, {2, \[Infinity]}, Hold, Heads -> True],
        Except[_Integer | Plus | _Plus | Times | _Times | Power |
          Power[_, -1]]] === {} &&
      Sort[Level[h /. Power[q_, -1] -> q, {-1}] /.
         q_Integer -> Abs[q]] === Sort[n], ReleaseHold[h]]], Null]]
Grid[{{Button[
    "new numbers", {a, b, c, d} = Table[RandomInteger[{1, 9}], {4}]],
   InputField[Dynamic[x], String]}, {Dynamic[{a, b, c, d}],
   Dynamic[Switch[isLegal[{a, b, c, d}, x], Null,
     "Sorry, that is invalid.", 24, "Congrats! That's 24!", _,
     "Sorry, that makes " <> ToString[[email protected], InputForm] <>
      ", not 24."]]}}]


  function twentyfour()
  N = 4;
  n = ceil(rand(1,N)*9);
  printf('Generate a equation with the numbers %i, %i, %i, %i and +, -, *, /, () operators ! \n',n);
  s = input(': ','s');
  t = s;
  for k = 1:N,
    [x,t] = strtok(t,'+-*/() \t');
     if length(x)~=1,
       error('invalid sign %s\n',x);
     end;
     y = x-'0';
     if ~(0 < y & y < 10)
       error('invalid sign %s\n',x);
     end;
     z(1,k) = y;
  end;
  if any(sort(z)-sort(n))
    error('numbers do not match.\n');
  end;

  val =  eval(s);
  if val==24,
    fprintf('expression "%s" results in %i.\n',s,val);
  else
    fprintf('expression "%s" does not result in 24 but %i.\n',s,val);
  end;


evalAddSub = function()
    result = evalMultDiv
    while true
        if not tokens then return result
        op = tokens[0]
        if op != "+" and op != "-" then return result
        tokens.pull  // (discard operator)
        rhs = evalMultDiv
        if result == null or rhs == null then return null
        if op == "+" then result = result + rhs
        if op == "-" then result = result - rhs
    end while
end function

evalMultDiv = function()
    result = evalAtom
    while true
        if not tokens then return result
        op = tokens[0]
        if op != "*" and op != "/" then return result
        tokens.pull  // (discard operator)
        rhs = evalAtom
        if result == null or rhs == null then return null
        if op == "*" then result = result * rhs
        if op == "/" then result = result / rhs
    end while
end function

evalAtom = function()
    if tokens[0] == "(" then
        tokens.pull
        result = evalAddSub
        if not tokens or tokens.pull != ")" then
            print "Unbalanced parantheses"
            return null
        end if
        return result
    end if
    num = val(tokens.pull)
    idx = availableDigits.indexOf(num)
    if idx == null then
        print str(num) + " is not available"
        return null
    else
        availableDigits.remove idx
    end if
    return num
end function

choices = []
for i in range(1, 4)
    choices.push ceil(rnd*9)
end for
result = null
while result != 24
    availableDigits = choices[0:]  // (clones the list)
    print "Using only the digits " + availableDigits + ","
    tokens = input("enter an expression that comes to 24: ").replace(" ","").values
    result = evalAddSub
    if availableDigits then
        print "You forgot to use: " + availableDigits
        result = null
    end if
    if result != null then print "That equals " + result + "."
end while
print "Great job!"


MODULE TwentyFour;

FROM	InOut			IMPORT WriteString, WriteLn, Write, ReadString, WriteInt;
FROM	RandomGenerator IMPORT Random;

TYPE	operator_t 		= (add, sub, mul, div);
		expr_t			= RECORD
							operand		: ARRAY[0..3] OF CARDINAL;
					  		operator 	: ARRAY[1..3] OF operator_t;
						  END;(*of RECORD*)
		numbers_t		= SET OF CHAR;

VAR		expr 	: expr_t;
		numbers	: numbers_t;
(*******************************************************************createExpr*)
(*analyse the input string                                                    *)
PROCEDURE createExpr(s: ARRAY OF CHAR);

VAR index, counter	: INTEGER;
	token 			: CHAR;
	temp_expr 		: expr_t;
	operand   		: CARDINAL;
	operator		: operator_t;

	(************************************nextToken*)
	(* returns the next CHAR that isn`t a space    *)
	PROCEDURE nextToken(): CHAR;
	BEGIN
		INC(index);
		WHILE (s[index] = ' ') DO
			INC(index);
		END;(*of WHILE*)
		RETURN(s[index]);
	END nextToken;
	(***********************************set_operand*)
	(* checks if the CHAR o inerhits a valid number*)
	(* and sets 'operand' to its value             *)
	PROCEDURE set_operand(o: CHAR);
	BEGIN
		CASE o OF
			'0'..'9':	IF o IN numbers THEN
							operand := ORD(o)-48;
							numbers := numbers - numbers_t{o};
						ELSE
							WriteString("ERROR: '");
							Write(					o);
							WriteString(				"' isn`t a available number ");
							WriteLn;
							HALT;
						END;(*of IF*)|
			0  : WriteString("ERROR: error in input ");
					WriteLn;
					HALT;
			ELSE
				WriteString("ERROR: '");
				Write(					o);
				WriteString(				"' isn`t a number ");
				WriteLn;
				HALT;
		END;(*of CASE*)
	END set_operand;
	(**********************************set_operator*)
	(* checks if the CHAR o inerhits a valid       *)
	(* operator and sets 'operator' to its value   *)
	PROCEDURE set_operator(o: CHAR);
	BEGIN
		CASE o OF
			'+' : operator := add;|
			'-' : operator := sub;|
			'*' : operator := mul;|
			'/' : operator := div;|
			0  : WriteString("ERROR: error in input ");
					WriteLn;
					HALT;
		ELSE
			WriteString("ERROR: '");
				Write(					o);
				WriteString(				"' isn`t a operator ");
				WriteLn;
				HALT;
		END;(*of CASE*)
	END set_operator;
	(************************************************)
BEGIN
	index := -1;

	token := nextToken();
	set_operand(token);
	expr.operand[0] := operand;

	token := nextToken();
	set_operator(token);
	expr.operator[1] := operator;

	token := nextToken();
	set_operand(token);
	expr.operand[1] := operand;

	token := nextToken();
	set_operator(token);
	expr.operator[2] := operator;

	token := nextToken();
	set_operand(token);
	expr.operand[2] := operand;

	token := nextToken();
	set_operator(token);
	expr.operator[3] := operator;

	token := nextToken();
	set_operand(token);
	expr.operand[3] := operand;
END createExpr;

(*****************************************************************evaluateExpr*)
(* evaluate the expresion that was createt by 'createExpr'                    *)

PROCEDURE evaluateExpr(VAR num: REAL);

VAR	index	: INTEGER;
BEGIN
	WITH expr DO
		num := VAL(REAL,operand[0]);
		FOR index := 1 TO 3 DO
			CASE operator[index] OF
				add : num := num + VAL(REAL,operand[index]);|
				sub : num := num - VAL(REAL,operand[index]);|
				mul : num := num * VAL(REAL,operand[index]);|
				div : num := num / VAL(REAL,operand[index]);
			END;(*of CASE*)
		END;(*of FOR*)
	END;(*of WITH*)
END evaluateExpr;

(**************************************************************generateNumbers*)
(* generates the 4 random numbers ond write them                              *)
PROCEDURE generateNumbers;
VAR	index,ran : INTEGER;
BEGIN
	numbers := numbers_t{};
	ran := Random(0,9);
	FOR index := 1 TO 4 DO
		WHILE (CHR(ran+48) IN numbers )DO
			ran := Random(0,9);
		END;(*of While*)
		Write(CHR(ran+48));
		WriteLn;
		numbers := numbers + numbers_t{CHR(ran+48)}
	END;(*of FOR*)
END generateNumbers;
(****************************************************************Main Programm*)
VAR	str : ARRAY[0..255] OF CHAR;
	sum : REAL;
BEGIN
	WriteString("Welcome to the 24 game in MODULA-2");
	WriteLn;
	WriteString("Here are your numbers:");
	WriteLn;
	generateNumbers;
	WriteString("Enter your equation(This implementation dosn`t support brackets yet): ");
	WriteLn;
	ReadString(str);
	createExpr(str);
	evaluateExpr(sum);
	WriteLn;
	WriteString("Result:");
	WriteLn;
	WriteInt(TRUNC(sum),0);
	WriteLn;
	CASE (TRUNC(sum) - 24) OF
		0 : WriteString("Perfect!");|
		1 : WriteString("Almost perfect.");
		ELSE
			WriteString("You loose!");
	END;(*of CASE*)
	WriteLn;
END TwentyFour.


24Game
	k number, operator, bracket
	; generate 4 random numbers each between 1 & 9
	; duplicates allowed!
	s n1=$r(9)+1, n2=$r(9)+1, n3=$r(9)+1, n4=$r(9)+1
	; save a copy of them so that we can keep restarting
	; if the user gets it wrong
	s s1=n1,s2=n2,s3=n3,s4=n4
Question
	s (numcount,opcount,lbrackcount,rbrackcount)=0
	; restart with the random numbers already found
	s n1=s1,n2=s2,n3=s3,n4=s4
	w !,"Enter an arithmetic expression that evaluates to 24 using (",
	n1," ",n2," ",n3," ",n4,"): "
	r !,expr
	q:expr=""
	; validate numbers and operators
	s error=""
	f n=1:1:$l(expr) {
		s char=$e(expr,n)
		if char?1n {
			s number($i(numcount))=char
			w !
			zw char
		}
		elseif char?1(1"*",1"/",1"+",1"-") {
			s operator($i(opcount))=char
		}
		elseif char?1"(" {
			s bracket($i(lbrackcount))=char
		}
		elseif char?1")" {
			s bracket($i(rbrackcount))=char
		}
		else {
			s error="That ain't no character I wanted to see"
			q
		}
	}
	if error'="" w error g Question
	if numcount'=4 {
		w "Does not have 4 numbers, do it again."
		g Question
	}
	s error=""
	f n=1:1:4 {
		if number(n)=n1 {
			s n1="dont use again" continue
		}
		if number(n)=n2 {
			s n2="dont use again" continue
		}
		if number(n)=n3 {
			s n3="dont use again" continue
		}
		if number(n)=n4 {
			s n4="dont use again" continue
		}
		s error="Numbers entered do not match all of the randomly generated numbers."
		q
	}
	if error'="" {
		w error
		g Question
	}
	if opcount'=3 {
		w "Does not have 3 operators."
		g Question
	}
	if lbrackcount'=rbrackcount {
		w "brackets must be in pairs."
		g Question
	}
	x "s x="_expr
	if x'=24 {
		w !,"Answer does not = 24"
		g Question
	}
	w x
	q


import Nanoquery.Lang
import Nanoquery.Util

// a function to get the digits from an arithmetic expression
def extract_digits(input)
	input = str(input)
	digits = {}

	loc = 0
	digit = ""
	while (loc < len(input))
		if input[loc] in "0123456789"
			digit += input[loc]
		else
			if len(digit) > 0
				digits.append(int(digit))
				digit = ""
			end
		end
		loc += 1
	end
	// check if there's a final digit
	if len(digit) > 0
		digits.append(int(digit))
	end

	return digits
end

// a function to duplicate a digit list
def dup(list)
	nlist = {}

	for n in list
		nlist.append(new(Integer, n))
	end

	return nlist
end

// generate four random digits and output them
random = new(Random)
randDigits = {}
for i in range(1, 4)
	randDigits.append(random.getInt(8) + 1)
end
println "Your digits are: " + randDigits + "\n"

// get expressions from the user until a valid one is found
won = false
while !won
	print "> "
	expr = input()
	tempDigits = dup(randDigits)

	// check for invalid digits in the expression
	invalid = false
	digits = extract_digits(expr)
	for (i = 0) (!invalid and (i < len(digits))) (i += 1)
		if not digits[i] in tempDigits
			invalid = true
			println "Invalid digit " + digits[i]
		else
			tempDigits.remove(tempDigits)
		end
	end

	// if there were no invalid digits, check if the expression
	// evaluates to 24
	if !invalid
		a = null
		try
			exec("a = " + expr)
		catch
			println "Invalid expression " + expr
		end
		println a

		if a = 24
			println "Success!"
			won = true
		end
	end
end


from random import randomize, rand
from strutils import Whitespace
from algorithm import sort
from sequtils import newSeqWith

randomize()

var
  problem = newSeqWith(4, rand(1..9))
  stack: seq[float]
  digits: seq[int]

echo "Make 24 with the digits: ", problem

template op(c: untyped) =
  let a = stack.pop
  stack.add c(stack.pop, a)

for c in stdin.readLine:
  case c
  of '1'..'9':
    digits.add c.ord - '0'.ord
    stack.add float(c.ord - '0'.ord)
  of '+': op `+`
  of '*': op `*`
  of '-': op `-`
  of '/': op `/`
  of Whitespace: discard
  else: raise newException(ValueError, "Wrong char: " & c)

sort digits
sort problem
if digits != problem:
  raise newException(ValueError, "Not using the given digits.")
if stack.len != 1:
  raise newException(ValueError, "Wrong expression.")
echo "Result: ", stack[0]
echo if abs(stack[0] - 24) < 0.001: "Good job!" else: "Try again."


redef class Char
	fun is_op: Bool do return "-+/*".has(self)
end

# Get `numbers` and `operands` from string `operation` collect with `gets` in `main` function
# Fill `numbers` and `operands` array with previous extraction
fun exportation(operation: String, numbers: Array[Int], operands: Array[Char]) do
	var previous_char: nullable Char = null
	var number: nullable Int = null
	var negative = false

	for i in operation.length.times do
		var current_char = operation[i]
		var current_int = current_char.to_i

		if (previous_char == null or previous_char.is_op) and current_char == '-' then
			negative = true
			continue
		end

		if current_char.is_digit then
			if number == null then
				number = current_int
			else
				number = number * 10 + current_int
			end
		end

		if negative and (current_char.is_op or i == operation.length - 1) then
			number = number - number * 2
			negative = false
		end

		if (current_char.is_op or i == operation.length - 1) and number != null then
			numbers.add(number)
			number = null
		end

		if not negative and current_char.is_op then
			operands.add(current_char)
		end
		previous_char = current_char
	end
	# Update `numbers` and `operands` array in main function with pointer
end

# Create random numbers between 1 to 9
fun random: Array[Int] do
	return [for i in 4.times do 1 + 9.rand]
end

# Make mathematical operation with `numbers` and `operands` and add the operation result into `random_numbers`
fun calculation(random_numbers, numbers: Array[Int], operands: Array[Char]) do
	var number = 0
	var temp_numbers = numbers.clone

	while temp_numbers.length > 1 do
		var operand = operands.shift
		var a = temp_numbers.shift
		var b = temp_numbers.shift

		if operand == '+' then number = a + b
		if operand == '-' then number = a - b
		if operand == '*' then number = a * b
		if operand == '/' then number = a / b

		temp_numbers.unshift(number)
	end
	if number != 0 then random_numbers.add(number)
end

# Check if used `numbers` exist in the `random_numbers` created
fun numbers_exists(random_numbers, numbers: Array[Int]): Bool do
	for number in numbers do
		if not random_numbers.count(number) >= numbers.count(number) then return false
	end
	return true
end

# Remove `numbers` when they are used
fun remove_numbers(random_numbers, numbers: Array[Int]) do
	for number in numbers do random_numbers.remove(number)
end

# Check if the mathematical `operation` is valid
fun check(operation: String): Bool do
	var previous_char: nullable Char = null
	var next_char: nullable Char = null
	var next_1_char: nullable Char = null

	for i in operation.length.times do
		var current_char = operation[i]

		if i + 1 < operation.length then
			next_char = operation[i + 1]
			if i + 2 < operation.length then
				next_1_char = operation[i + 2]
			else
				next_1_char = null
			end
		else
			next_char = null
		end

		if not current_char.is_op and not current_char.is_digit then return false
		if next_char == null and current_char.is_op then return false

		if previous_char == null  then
			if next_char == null or next_1_char == null then return false
			if current_char == '-' and not next_char.is_digit then return false
			if current_char != '-' and not current_char.is_digit then return false
		else
			if next_char != null then
				if previous_char.is_digit and current_char.is_op and
				not (next_char == '-' and next_1_char != null and
				next_1_char.is_digit or next_char.is_digit) then
					return false
				end
			end
		end
		previous_char = current_char
	end
	return true
end

var random_numbers = new Array[Int]
var operation = ""

random_numbers = random
while not random_numbers.has(24) and random_numbers.length > 1 do
	var numbers = new Array[Int]
	var operands = new Array[Char]

	print "numbers: " + random_numbers.join(", ")
	operation = gets
	if check(operation) then
		exportation(operation, numbers, operands)
		if numbers_exists(random_numbers, numbers) then
			calculation(random_numbers, numbers, operands)
			remove_numbers(random_numbers, numbers)
		else
			print "NUMBERS ERROR!"
		end
	else
		print "STRING ERROR!"
	end
end

if random_numbers.has(24) then print "CONGRATULATIONS" else print "YOU LOSE"


use Collection.Generic;
use System.Matrix;

class RPNParser {
  @stk : Stack<IntHolder>;
  @digits : List<IntHolder>;

  function : Main(args : String[]) ~ Nil {
    digits := List->New()<IntHolder>;
    "Make 24 with the digits: "->Print();
    for(i := 0; i < 4; i += 1;) {
      n : Int := Int->Random(1, 9);
        " {$n}"->Print();
        digits->AddBack(n);
    };
    '\n'->Print();

    parser := RPNParser->New();
    if(parser->Parse(System.IO.Console->ReadString(), digits)) {
      result := parser->GetResult();
      if(result = 24) {
        "Good job!"->PrintLine();
      }
      else {
        "{$result}, Try again."->PrintLine();
      };
    }
    else {
      "Invalid sequence"->PrintLine();
    };
  }

  New() {
    @stk := Stack->New()<IntHolder>;
    @digits := List->New()<IntHolder>;
  }

  method : Op(f : \Func->Calc) ~ Nil {
    if(@stk->Size() < 2) { "Improperly written expression"->ErrorLine(); Runtime->Exit(1); };
    b := @stk->Pop();
    a := @stk->Pop();
    @stk->Push(f(a, b));
  }

  method : Parse(c : Char) ~ Nil {
    if(c >= '0' & c <= '9') {
      value : Int := c - '0';
      @stk->Push(value);
      @digits->AddBack(value);
    }
    else if(c = '+') {
      Op(\Func->Calc : (a, b) => a + b);
    }
    else if(c = '-') {
      Op(\Func->Calc : (a, b) => a - b);
    }
    else if(c = '*') {
      Op(\Func->Calc : (a, b) => a * b);
    }
    else if(c = '/') {
      Op(\Func->Calc : (a, b) => { if(b <> 0) { return a / b;  } else { return 0; }; });
    };
  }

  method : GetResult() ~ Int {
    if(@stk->Size() = 1) {
      return @stk->Top();
    };

    return 0;
  }

  method : Parse(s : String, digits : List<IntHolder>) ~ Bool {
    each(i : s) {
      Parse(s->Get(i));
    };

    @digits->Rewind();
    while(@digits->More()) {
      left := @digits->Get()->Get();
      digits->Rewind();
      found := false;
      while(digits->More() & found = false) {
        right := digits->Get()->Get();
        if(left = right) {
          digits->Remove(); found := true;
        }
        else {
          digits->Next();
        };
      };
      @digits->Next();
    };

    return digits->IsEmpty();
  }
}

alias Func {
  Calc : (IntHolder, IntHolder) ~ IntHolder
}


import: mapping

: game
| l expr w n i |
   4 #[ 9 rand ] Array init ->l

   System.Out "Digits : " << l << " --> RPN Expression for 24 : " << drop
   System.Console accept ->expr

   expr words forEach: w [
      w "+" == ifTrue: [ + continue ]
      w "-" == ifTrue: [ - continue ]
      w "*" == ifTrue: [ * continue ]
      w "/" == ifTrue: [ >float / continue ]

      w >integer dup ->n  ifNull: [ System.Out "Word " << w << " not allowed " << cr break ]
      n l indexOf dup ->i ifNull: [ System.Out "Integer " << n << " is wrong " << cr break ]
      n l put(i, null)
      ]
   #null? l conform? ifFalse: [ "Sorry, all numbers must be used..." . return ]
   24 if=: [ "You won !" ] else: [ "You loose..." ] .
;


DEFINE TEMP-TABLE tt NO-UNDO FIELD ii AS INTEGER.

DEFINE VARIABLE p_deanswer    AS DECIMAL     NO-UNDO.
DEFINE VARIABLE idigits       AS INTEGER     NO-UNDO EXTENT 4.
DEFINE VARIABLE ii            AS INTEGER     NO-UNDO.
DEFINE VARIABLE Digits        AS CHARACTER   NO-UNDO FORMAT "x(7)".
DEFINE VARIABLE Answer        AS CHARACTER   NO-UNDO FORMAT "x(7)".
DEFINE VARIABLE cexpression   AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cmessage      AS CHARACTER   NO-UNDO.
DEFINE VARIABLE cchar         AS CHARACTER   NO-UNDO.

FUNCTION calculate RETURNS LOGICAL (
   i_de AS DECIMAL
):
   p_deanswer = i_de.
END FUNCTION.

/* generate problem */
DO ii = 1 TO 4:
   ASSIGN
      idigits [ii]   =  RANDOM( 1, 9 ).
      Digits         =  Digits + STRING( idigits [ii] ) + " "
      .
END.

/* ui */
DISPLAY Digits.
UPDATE Answer.

/* check valid input */
DO ii = 1 TO 7:
   cchar = SUBSTRING( Answer, ii, 1 ).
   IF cchar > "" THEN DO:
      IF ii MODULO 2 = 1 THEN DO:
         IF LOOKUP( cchar, Digits, " " ) = 0 THEN
            cmessage = cmessage + SUBSTITUTE( "Invalid digit: &1.~r", cchar ).
         ELSE
            ENTRY( LOOKUP( cchar, Digits, " " ), Digits, " " ) = "".
      END.
      ELSE DO:
         IF LOOKUP( cchar, "+,-,/,*" ) = 0 THEN
            cmessage = cmessage + SUBSTITUTE( "&1 is not a valid operator.~r", cchar ).
      END.
   END.
END.
IF TRIM( Digits ) > "" THEN
   cmessage = cmessage + SUBSTITUTE( "You did not use digits: &1":U, TRIM( Digits ) ).

IF cmessage = "" THEN DO:
   /* expressions need spacing */
   DO ii = 1 TO 7:
      cexpression = cexpression + SUBSTRING( Answer, ii, 1 ) + " ".
   END.
   /* use dynamic query to parse expression */
   TEMP-TABLE tt:DEFAULT-BUFFER-HANDLE:FIND-FIRST(
      SUBSTITUTE(
         "WHERE NOT DYNAMIC-FUNCTION( 'calculate', DECIMAL( &1 ) )",
         cexpression
      )
   ) NO-ERROR.
   IF p_deanswer <> 24 THEN
      cmessage = cmessage + SUBSTITUTE( "The expression evaluates to &1.", p_deanswer ).
   ELSE
      cmessage = "Solved!".
END.

MESSAGE cmessage VIEW-AS ALERT-BOX.


game()={
  my(v=vecsort(vector(4,i,random(8)+1)));
  print("Form 24 using */+-() and: "v);
  while(1,
    my(ans=input);
    if (!valid(s,v), next);
    trap(,
      print("Arithmetic error");
      next
    ,
      if(eval(s)==24, break, print("Bad sum"))
    )
  );
  print("You win!")
};
valid(s,v)={
  my(op=vecsort(Vec("+-*/()")),u=[]);
  s=Vec(s);
  for(i=1,#s,
    if(setsearch(op,s[i]),next);
    trap(,
      print("Invalid character "s[i]);
      return(0)
    ,
      if(setsearch(v,eval(s[i])),
        u=concat(u,eval(s[i]))
      ,
        print(s[i]" not allowed");
        return(0)
      )
    )
  );
  for(i=2,#s,
    if(!setsearch(op,s[i])&!setsearch(op,s[i-1]),
      print("Concatenating digits is not allowed!");
      return(0)
    )
  );
  if(vecsort(u)!=v,
    print("Invalid digits");
    0
  ,
    1
  )
};


#!/usr/bin/env perl
use warnings;
use strict;
use feature 'say';

print <<'EOF';
The 24 Game

Given any four digits in the range 1 to 9, which may have repetitions,
Using just the +, -, *, and / operators; and the possible use of
parentheses, (), show how to make an answer of 24.

An answer of "q" or EOF will quit the game.
A blank answer will generate a new set of four digits.
Otherwise you are repeatedly asked for an expression until it evaluates to 24.

Note: you cannot form multiple digit numbers from the supplied digits,
so an answer of 12+12 when given 1, 2, 2, and 1 would not be allowed.
EOF

my $try = 1;
while (1) {
  my @digits = map { 1+int(rand(9)) } 1..4;
  say "\nYour four digits: ", join(" ", @digits);
  print "Expression (try ", $try++, "): ";

  my $entry = <>;
  if (!defined $entry || $entry eq 'q')
    { say "Goodbye.  Sorry you couldn't win."; last; }
  $entry =~ s/\s+//g;  # remove all white space
  next if $entry eq '';

  my $given_digits = join "", sort @digits;
  my $entry_digits = join "", sort grep { /\d/ } split(//, $entry);
  if ($given_digits ne $entry_digits ||  # not correct digits
      $entry =~ /\d\d/ ||                # combined digits
      $entry =~ m|[-+*/]{2}| ||          # combined operators
      $entry =~ tr|-0-9()+*/||c)         # Invalid characters
    { say "That's not valid";  next; }

  my $n = eval $entry;

  if    (!defined $n) { say "Invalid expression"; }
  elsif ($n == 24)    { say "You win!"; last; }
  else                { say "Sorry, your expression is $n, not 24"; }
}


-- 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 error = 1 -- "digit expected"
    atom res

    if idx<=length(equation) then
        integer 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'
            integer 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 error = 5 -- "operand expected"
    if idx<=length(equation) then
        integer 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)
    for i=length(res) to 2 by -1 do
        integer 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")
                {} = wait_key()
                exit
            end if
        end if
        puts(1,"enter Q to give up and quit\n")
    end while
end procedure

play()


#!/usr/bin/env php
The 24 Game

Given any four digits in the range 1 to 9, which may have repetitions,
Using just the +, -, *, and / operators; and the possible use of
brackets, (), show how to make an answer of 24.

An answer of "q" will quit the game.
An answer of "!" will generate a new set of four digits.
Otherwise you are repeatedly asked for an expression until it evaluates to 24

Note: you cannot form multiple digit numbers from the supplied digits,
so an answer of 12+12 when given 1, 2, 2, and 1 would not be allowed.

<?php

while (true) {
    $numbers = make_numbers();

    for ($iteration_num = 1; ; $iteration_num++) {
        echo "Expresion $iteration_num: ";

        $entry = rtrim(fgets(STDIN));

        if ($entry === '!') break;
        if ($entry === 'q') exit;

        $result = play($numbers, $entry);

        if ($result === null) {
            echo "That's not valid\n";
            continue;
        }
        elseif ($result != 24) {
            echo "Sorry, that's $result\n";
            continue;
        }
        else {
            echo "That's right! 24!!\n";
            exit;
        }
    }
}

function make_numbers() {
    $numbers = array();

    echo "Your four digits: ";

    for ($i = 0; $i < 4; $i++) {
        $number = rand(1, 9);
        // The check is needed to avoid E_NOTICE from PHP
        if (!isset($numbers[$number])) {
            $numbers[$number] = 0;
        }
        $numbers[$number]++;
        print "$number ";
    }

    print "\n";

    return $numbers;
}

function play($numbers, $expression) {
    $operator = true;
    for ($i = 0, $length = strlen($expression); $i < $length; $i++) {
        $character = $expression[$i];

        if (in_array($character, array('(', ')', ' ', "\t"))) continue;

        $operator = !$operator;

        if (!$operator) {
            if (!empty($numbers[$character])) {
                $numbers[$character]--;
                continue;
            }
            return;
        }
        elseif (!in_array($character, array('+', '-', '*', '/'))) {
            return;
        }
    }

    foreach ($numbers as $remaining) {
        if ($remaining > 0) {
            return;
        }
    }

    return eval("return $expression;");
}
?>


import util.

main =>
    play.

play =>
    nl,
    Digits = [random() mod 9 + 1 : _ in 1..4].sort(),
    println("Enter \"q\" to quit"),
    println("Enter \"!\" to request a new set of four digits."),
    printf("Or enter expression using %w => ", Digits),
    Exp = read_line().strip(),
    evaluate(Digits,Exp).

evaluate(_Digits,"q") => halt.
evaluate(_Digits,"!") => play.
evaluate(Digits,Exp) =>
    Operands = [to_int(D) : D in Exp, digit(D)].sort(),
    (Digits !== Operands ->
        println("You must use the given digits:" ++ to_string(Digits))
    ;
        catch(Term = parse_term(Exp), Exception, println(Exception)),
        Res is Term,
        (Res =:= 24 ->
            println("Good work!")
        ;
            println("Wong expression")
        )
    ),
    play.


(load "@lib/frac.l")

(de random4digits ()
   (setq Numbers (make (do 4 (link (rand 1 9)))))
   Numbers )

(de prompt ()
   (prinl
      "^JPlease enter a 'Lisp' expression (integer or fractional math) that equals 24,^J"
      "using (, ), frac, + or f+, - or f-, * or f*, / or f/, and " Numbers "^J"
      "(use each number only once). Enter . to quit." ) )

(de getinput ()
   (setq Expression (catch ’NIL (in NIL (read))))
   Expression )

(de checkexpression (Numbers Expression)
   (make
      (when (diff Numbers (fish num? Expression))
         (link "Not all numbers used.") )
      (when (diff (fish num? Expression) Numbers)
         (link "Using wrong number(s)."))
      (if (sub? "f" (pack Expression))
         (when (diff (fish sym? Expression) '(frac f+ f- f* f/))
            (link "Illegal operator(s). If fractional expression, must use frac, f+, f-, f*, f/ only.") )
         (when (diff (fish sym? Expression) '(+ - * /))
            (link "Using illegal operator(s).") ) ) ) )

(de instructions ()
   (prinl "Example 'Lisp' expression: (- (* 3 9) (+ 1 2))")
   (prinl
      "Example 'Lisp' fractional expression:^J"
      "(f- (f* (frac 3 1)(frac 9 1)) (f+ (frac 1 1)(frac 2 1)))" )
   (prinl
      "Use a fractional expression 'just for fun (as above)' OR if your solution^J"
      "would lose remainder value(s) otherwise (through integer division)." ) )

(de loopuntilquit ()
   (instructions)
   (loop
      (set 'Numbers (random4digits))
      (prompt)
      (set 'Expression (getinput))
      (if (= Expression ".") (prog (prinl "bye!") (bye)))
      (set 'Check (checkexpression Numbers Expression))
      (if (car Check)
         (mapcar prinl Check)
         (prog
            (set 'Evaluated (eval Expression))
            (if (or (= Evaluated 24) (= Evaluated (24 . 1)))
               (prinl "Congratulations!")
               (prinl "That evaluated to " Evaluated " Try again!") ) ) ) ) )

(loopuntilquit)