The 24 Game tests one's mental arithmetic.



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

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

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

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


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


Related tasks
 24 game/Solve


Reference
 The 24 Game on h2g2.


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();