The 3, 3, 8, 8 Puzzle
I was inspired by this puzzle to create a program which could solve the problem for me. I figured I'd use F# because it's starting to look really cool.
Here's my F# solution code as a script:
// Light F# syntax.
#light
// Definition of the binary operator type.
type BinOp = (Math.BigNum -> Math.BigNum -> Math.BigNum)
// Terms in the postfix expression are either numbers or operators.
type Term =
| Number of Math.BigNum
| Operator of BinOp
// The result of an operation is either a number or Invalid.
type Result =
| Answer of Math.BigNum
| Invalid
// Array of possible numbers and operators.
let postfix_terms =
[|Number 3N;
Number 8N;
Operator (fun (x:Math.BigNum) (y:Math.BigNum) -> x + y);
Operator (fun (x:Math.BigNum) (y:Math.BigNum) -> x - y);
Operator (fun (x:Math.BigNum) (y:Math.BigNum) -> x * y);
Operator (fun (x:Math.BigNum) (y:Math.BigNum) -> x / y)|]
// Increments a number in base 6. The number is represented as a string of
// integers. Overflow is not accounted for (i.e. the length of the number
// doesn't change). The base is 6 because the digits of the number are used
// to index the postfix_terms array and there are 6 elements in the array.
// Example (in base 6):
// [5;5;3;5;5;2;1]
// +1
// ---------------
// [0;0;4;5;5;2;1]
let rec inc_indexer indexer =
match indexer with
| hd::tl ->
match hd with
| 5 -> 0::(inc_indexer tl)
| _ -> (hd+1)::tl
| _ -> []
// Uses the indexer to generate a possible expression based on the char_vec.
let rec gen_string indexer =
match indexer with
| hd::tl -> postfix_terms.(hd)::(gen_string tl)
| _ -> []
// Evaluates a postfix expression and returns the resulting number or Invalid.
let rec evaluator expr acc =
match expr with
// Numbers go on the accumulator stack.
| Number hd::tl -> evaluator tl (hd::acc)
// Operators are evaluated with the top two numbers on the stack.
| Operator hd::tl ->
match acc.Length with
// First make sure there are enough elements in the accumulator.
| 0 -> Invalid
| 1 -> Invalid
// Now apply the operator and evaluate recursively.
| _ -> (evaluator tl ((hd acc.Tail.Head acc.Head)::(acc.Tail.Tail)))
// When the stack is empty, the accumulator should hold the result.
| _ ->
match acc with
| hd::[] -> Answer hd
| _ -> Invalid
// Prints a list of integers. This is used to print the indexer list which
// in turn is used to generate the postfix expression.
let rec print_list lst =
match lst with
| hd::tl ->
printf "%i " hd
print_list tl
| [] -> printf "-> "
// Returns true if the list of terms contains two 3's and two 8's and false
// otherwise.
let rec validate terms eights threes =
match terms with
| Number a::tl ->
match (a.ToString()) with
| "3" -> validate tl eights (threes+1)
| "8" -> validate tl (eights+1) threes
| _ -> validate tl eights threes
| [] ->
match eights with
| 2 ->
match threes with
| 2 -> true
| _ -> false
| _ -> false
| _::tl -> validate tl eights threes
// Evaluates all possible postfix expressions and halts if it finds '24'
let rec process index =
let new_index = (inc_indexer index)
let new_strng = (gen_string new_index)
match (validate new_strng 0 0) with
| true ->
match (evaluator new_strng []) with
| Invalid ->
process new_index
| Answer a ->
match (a.ToString()) with
| "24" ->
print_list new_index
printf "%s\n" (a.ToString())
| _ ->
print_list new_index
printf "%s\n" (a.ToString())
process new_index
| false -> process new_index
process [0;0;0;0;0;0;0]
System.Console.ReadKey()
The answer that it halts on is: 1 0 1 0 5 3 5 → 24
Which, according to the mapping, is, in postfix notation: 8 3 8 3 / - /
Which is, in infix notation: (8 / (3 - (8 / 3)))
Which is the correct answer :^)