265 lines
7.7 KiB
Java
265 lines
7.7 KiB
Java
import java.util.*;
|
|
|
|
public class Game24Player {
|
|
final String[] patterns = {"nnonnoo", "nnonono", "nnnoono", "nnnonoo",
|
|
"nnnnooo"};
|
|
final String ops = "+-*/^";
|
|
|
|
String solution;
|
|
List<Integer> digits;
|
|
|
|
public static void main(String[] args) {
|
|
new Game24Player().play();
|
|
}
|
|
|
|
void play() {
|
|
digits = getSolvableDigits();
|
|
|
|
Scanner in = new Scanner(System.in);
|
|
while (true) {
|
|
System.out.print("Make 24 using these digits: ");
|
|
System.out.println(digits);
|
|
System.out.println("(Enter 'q' to quit, 's' for a solution)");
|
|
System.out.print("> ");
|
|
|
|
String line = in.nextLine();
|
|
if (line.equalsIgnoreCase("q")) {
|
|
System.out.println("\nThanks for playing");
|
|
return;
|
|
}
|
|
|
|
if (line.equalsIgnoreCase("s")) {
|
|
System.out.println(solution);
|
|
digits = getSolvableDigits();
|
|
continue;
|
|
}
|
|
|
|
char[] entry = line.replaceAll("[^*+-/)(\\d]", "").toCharArray();
|
|
|
|
try {
|
|
validate(entry);
|
|
|
|
if (evaluate(infixToPostfix(entry))) {
|
|
System.out.println("\nCorrect! Want to try another? ");
|
|
digits = getSolvableDigits();
|
|
} else {
|
|
System.out.println("\nNot correct.");
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
System.out.printf("%n%s Try again.%n", e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
void validate(char[] input) throws Exception {
|
|
int total1 = 0, parens = 0, opsCount = 0;
|
|
|
|
for (char c : input) {
|
|
if (Character.isDigit(c))
|
|
total1 += 1 << (c - '0') * 4;
|
|
else if (c == '(')
|
|
parens++;
|
|
else if (c == ')')
|
|
parens--;
|
|
else if (ops.indexOf(c) != -1)
|
|
opsCount++;
|
|
if (parens < 0)
|
|
throw new Exception("Parentheses mismatch.");
|
|
}
|
|
|
|
if (parens != 0)
|
|
throw new Exception("Parentheses mismatch.");
|
|
|
|
if (opsCount != 3)
|
|
throw new Exception("Wrong number of operators.");
|
|
|
|
int total2 = 0;
|
|
for (int d : digits)
|
|
total2 += 1 << d * 4;
|
|
|
|
if (total1 != total2)
|
|
throw new Exception("Not the same digits.");
|
|
}
|
|
|
|
boolean evaluate(char[] line) throws Exception {
|
|
Stack<Float> s = new Stack<>();
|
|
try {
|
|
for (char c : line) {
|
|
if ('0' <= c && c <= '9')
|
|
s.push((float) c - '0');
|
|
else
|
|
s.push(applyOperator(s.pop(), s.pop(), c));
|
|
}
|
|
} catch (EmptyStackException e) {
|
|
throw new Exception("Invalid entry.");
|
|
}
|
|
return (Math.abs(24 - s.peek()) < 0.001F);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
List<Integer> randomDigits() {
|
|
Random r = new Random();
|
|
List<Integer> result = new ArrayList<>(4);
|
|
for (int i = 0; i < 4; i++)
|
|
result.add(r.nextInt(9) + 1);
|
|
return result;
|
|
}
|
|
|
|
List<Integer> getSolvableDigits() {
|
|
List<Integer> result;
|
|
do {
|
|
result = randomDigits();
|
|
} while (!isSolvable(result));
|
|
return result;
|
|
}
|
|
|
|
boolean isSolvable(List<Integer> digits) {
|
|
Set<List<Integer>> dPerms = new HashSet<>(4 * 3 * 2);
|
|
permute(digits, dPerms, 0);
|
|
|
|
int total = 4 * 4 * 4;
|
|
List<List<Integer>> oPerms = new ArrayList<>(total);
|
|
permuteOperators(oPerms, 4, total);
|
|
|
|
StringBuilder sb = new StringBuilder(4 + 3);
|
|
|
|
for (String pattern : patterns) {
|
|
char[] patternChars = pattern.toCharArray();
|
|
|
|
for (List<Integer> dig : dPerms) {
|
|
for (List<Integer> opr : oPerms) {
|
|
|
|
int i = 0, j = 0;
|
|
for (char c : patternChars) {
|
|
if (c == 'n')
|
|
sb.append(dig.get(i++));
|
|
else
|
|
sb.append(ops.charAt(opr.get(j++)));
|
|
}
|
|
|
|
String candidate = sb.toString();
|
|
try {
|
|
if (evaluate(candidate.toCharArray())) {
|
|
solution = postfixToInfix(candidate);
|
|
return true;
|
|
}
|
|
} catch (Exception ignored) {
|
|
}
|
|
sb.setLength(0);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
String postfixToInfix(String postfix) {
|
|
class Expression {
|
|
String op, ex;
|
|
int prec = 3;
|
|
|
|
Expression(String e) {
|
|
ex = e;
|
|
}
|
|
|
|
Expression(String e1, String e2, String o) {
|
|
ex = String.format("%s %s %s", e1, o, e2);
|
|
op = o;
|
|
prec = ops.indexOf(o) / 2;
|
|
}
|
|
}
|
|
|
|
Stack<Expression> expr = new Stack<>();
|
|
|
|
for (char c : postfix.toCharArray()) {
|
|
int idx = ops.indexOf(c);
|
|
if (idx != -1) {
|
|
|
|
Expression r = expr.pop();
|
|
Expression l = expr.pop();
|
|
|
|
int opPrec = idx / 2;
|
|
|
|
if (l.prec < opPrec)
|
|
l.ex = '(' + l.ex + ')';
|
|
|
|
if (r.prec <= opPrec)
|
|
r.ex = '(' + r.ex + ')';
|
|
|
|
expr.push(new Expression(l.ex, r.ex, "" + c));
|
|
} else {
|
|
expr.push(new Expression("" + c));
|
|
}
|
|
}
|
|
return expr.peek().ex;
|
|
}
|
|
|
|
char[] infixToPostfix(char[] infix) throws Exception {
|
|
StringBuilder sb = new StringBuilder();
|
|
Stack<Integer> s = new Stack<>();
|
|
try {
|
|
for (char c : infix) {
|
|
int idx = ops.indexOf(c);
|
|
if (idx != -1) {
|
|
if (s.isEmpty())
|
|
s.push(idx);
|
|
else {
|
|
while (!s.isEmpty()) {
|
|
int prec2 = s.peek() / 2;
|
|
int prec1 = idx / 2;
|
|
if (prec2 >= prec1)
|
|
sb.append(ops.charAt(s.pop()));
|
|
else
|
|
break;
|
|
}
|
|
s.push(idx);
|
|
}
|
|
} else if (c == '(') {
|
|
s.push(-2);
|
|
} else if (c == ')') {
|
|
while (s.peek() != -2)
|
|
sb.append(ops.charAt(s.pop()));
|
|
s.pop();
|
|
} else {
|
|
sb.append(c);
|
|
}
|
|
}
|
|
while (!s.isEmpty())
|
|
sb.append(ops.charAt(s.pop()));
|
|
|
|
} catch (EmptyStackException e) {
|
|
throw new Exception("Invalid entry.");
|
|
}
|
|
return sb.toString().toCharArray();
|
|
}
|
|
|
|
void permute(List<Integer> lst, Set<List<Integer>> res, int k) {
|
|
for (int i = k; i < lst.size(); i++) {
|
|
Collections.swap(lst, i, k);
|
|
permute(lst, res, k + 1);
|
|
Collections.swap(lst, k, i);
|
|
}
|
|
if (k == lst.size())
|
|
res.add(new ArrayList<>(lst));
|
|
}
|
|
|
|
void permuteOperators(List<List<Integer>> res, int n, int total) {
|
|
for (int i = 0, npow = n * n; i < total; i++)
|
|
res.add(Arrays.asList((i / npow), (i % npow) / n, i % n));
|
|
}
|
|
}
|