6

I'm writing a program that evaluates the integer values of 4 playing cards (numbers 1-13), and displays a solution that equals 24. I have a large if statement that I've written for this and have realized that there are just too many solutions to add them all. I'm looking for advice on how to condense this into a more optimized version. The code is running fine with no errors, here is my entire code:

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.scene.image.ImageView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicReference;
import java.util.*;

public class Main extends Application {

   private Card card1;
   private Card card2;
   private Card card3;
   private Card card4;

   private int a;
   private int b;
   private int c;
   private int d;
   private int e=0;
   private int f=0;
   boolean par=false;


   @Override
   public void start(Stage primaryStage) {

      ArrayList<Integer> deck;
      deck = new ArrayList<>();
      int i = 1;
      while(i < 52){
         deck.add(i);
         i++;
      }
      final AtomicReference<String> result = new AtomicReference<>("");

      Collections.shuffle(deck);

      BorderPane pane = new BorderPane();

      HBox top = new HBox(10);
      Label display = new Label(result.toString());
      Button btShuffle = new Button("Shuffle");
      Button fiSolution = new Button("Find Solution");
      TextField solfield = new TextField();
      VBox bottomBox = new VBox();

      top.getChildren().add(fiSolution);
      top.getChildren().add(solfield);
      top.getChildren().add(btShuffle);

      HBox center = new HBox(10);

      card1 = new Card(deck.get(0));
      center.getChildren().add(card1);

      card2 = new Card(deck.get(1));
      center.getChildren().add(card2);

      card3 = new Card(deck.get(3));
      center.getChildren().add(card3);

      card4 = new Card(deck.get(4));
      center.getChildren().add(card4);


      //String str1 = solfield.setText();

      fiSolution.setOnAction(
            (ActionEvent e) -> {

               a = card1.CardValue();
               b = card2.CardValue();
               c = card3.CardValue(); 
               d = card4.CardValue();



              if (a+b+c+d == 24)
                  solfield.setText(a+"+"+b+"+"+c+"+"+d);
               else if (a+b+c-d == 24)
                  solfield.setText(a+"+"+b+"+"+c+"-"+d);
               else if (a-b+c+d == 24)
                  solfield.setText(a+"+-"+b+"+"+c+"+"+d);   
               else if (a+b-c+d == 24)
                  solfield.setText(a+"+"+b+"-"+c+"+"+d);
               else if ((((a+b)-c)*d)==24)
                  solfield.setText(a+"+"+b+"-"+c+"*"+d);
               else if ((((a+b)-c)/d)==24)
                  solfield.setText(a+"+"+b+"-"+c+"/"+d);
               else if ((((a+b)/c)-d)==24)
                  solfield.setText(a+"+"+b+"/"+c+"-"+d);
               else if ((((a+b)/c)*d)==24)
                  solfield.setText(a+"+"+b+"/"+c+"*"+d);
               else if ((((a+b)*c)/d)==24)
                  solfield.setText(a+"+"+b+"*"+c+"/"+d);
               else if ((((a+b)*c)-d)==24)
                  solfield.setText(a+"+"+b+"*"+c+"-"+d);
               else if ((((a-b)+c)*d)==24)
                  solfield.setText(a+"-"+b+"+"+c+"*"+d);
               else if ((((a-b)+c)/d)==24)
                  solfield.setText(a+"-"+b+"+"+c+"/"+d);
               else if ((((a-b)/c)+d)==24)
                  solfield.setText(a+"-"+b+"/"+c+"+"+d);
               else if ((((a-b)/c)*d)==24)
                  solfield.setText(a+"-"+b+"/"+c+"*"+d);
               else if ((((a-b)*c)/d)==24)
                  solfield.setText(a+"-"+b+"*"+c+"/"+d);
               else if ((((a-b)*c)+d)==24)
                  solfield.setText(a+"-"+b+"*"+c+"+"+d);
               else if ((((a*b)+c)/d)==24)
                  solfield.setText(a+"*"+b+"+"+c+"/"+d);
               else if ((((a*b)+c)-d)==24)
                  solfield.setText(a+"*"+b+"+"+c+"-"+d);
               else if ((((a*b)-c)/d)==24)
                  solfield.setText(a+"*"+b+"-"+c+"/"+d);
               else if ((((a*b)-c)+d)==24)
                  solfield.setText(a+"*"+b+"-"+c+"+"+d);
               else if ((((a*b)/c)+d)==24)
                  solfield.setText(a+"*"+b+"/"+c+"+"+d);
               else if ((((a*b)/c)-d)==24)
                  solfield.setText(a+"*"+b+"/"+c+"-"+d);
               else if ((((a/b)+c)*d)==24)
                  solfield.setText(a+"/"+b+"+"+c+"*"+d);
               else if ((((a/b)+c)-d)==24)
                  solfield.setText(a+"/"+b+"+"+c+"-"+d);
               else if ((((a/b)-c)+d)==24)
                  solfield.setText(a+"/"+b+"-"+c+"+"+d);
               else if ((((a/b)-c)*d)==24)
                  solfield.setText(a+"/"+b+"-"+c+"*"+d);
               else if ((((a/b)*c)-d)==24)
                  solfield.setText(a+"/"+b+"*"+c+"-"+d);
               else if ((((a/b)*c)+d)==24)
                  solfield.setText(a+"/"+b+"*"+c+"+"+d);


               f=c+d;
               if (((a/b)*f)==24)
                  solfield.setText(a+"/"+b+"*("+c+"+"+d+")");
               else if (((a/b)-f)==24)
                  solfield.setText(a+"/"+b+"-("+c+"+"+d+")");
               else if (((a*b)-f)==24)
                  solfield.setText(a+"*"+b+"-("+c+"+"+d+")");
               else if (((a*b)/f)==24)
                  solfield.setText(a+"*"+b+"/("+c+"+"+d+")");
               else if (((a-b)*f)==24)
                  solfield.setText(a+"-"+b+"*("+c+"+"+d+")");
               else if (((a-b)/f)==24)
                  solfield.setText(a+"-"+b+"/("+c+"+"+d+")");

               f=c-d;
               if (((a/b)*f)==24)
                  solfield.setText(a+"/"+b+"*("+c+"-"+d+")");
               else if (((a/b)+f)==24)
                  solfield.setText(a+"/"+b+"+("+c+"-"+d+")");
               else if (((a*b)+f)==24)
                  solfield.setText(a+"*"+b+"+("+c+"-"+d+")");
               else if (((a*b)/f)==24)
                  solfield.setText(a+"*"+b+"/("+c+"-"+d+")");
               else if (((a+b)*f)==24)
                  solfield.setText(a+"+"+b+"*("+c+"-"+d+")");
               else if (((a+b)/f)==24)
                  solfield.setText(a+"+"+b+"/("+c+"-"+d+")");

               f=c*d;
               if (((a/b)-f)==24)
                  solfield.setText(a+"/"+b+"*("+c+"*"+d+")");
               else if (((a/b)+f)==24)
                  solfield.setText(a+"/"+b+"+("+c+"*"+d+")");
               else if (((a-b)+f)==24)
                  solfield.setText(a+"-"+b+"+("+c+"*"+d+")");
               else if (((a-b)/f)==24)
                  solfield.setText(a+"-"+b+"/("+c+"*"+d+")");
               else if (((a+b)-f)==24)
                  solfield.setText(a+"+"+b+"-("+c+"*"+d+")");
               else if (((a+b)/f)==24)
                  solfield.setText(a+"+"+b+"/("+c+"*"+d+")");

               f=c/d;
               if (((a-b)*f)==24)
                  solfield.setText(a+"-"+b+"*("+c+"/"+d+")");
               else if (((a-b)+f)==24)
                  solfield.setText(a+"-"+b+"+("+c+"/"+d+")");
               else if (((a*b)+f)==24)
                  solfield.setText(a+"*"+b+"+("+c+"/"+d+")");
               else if (((a*b)-f)==24)
                  solfield.setText(a+"*"+b+"-("+c+"/"+d+")");
               else if (((a+b)*f)==24)
                  solfield.setText(a+"+"+b+"*("+c+"/"+d+")");
               else if (((a+b)-f)==24)
                  solfield.setText(a+"+"+b+"-("+c+"/"+d+")");

               f=b*c;
               if (((a-f)/d)==24)
                  solfield.setText(a+"-("+b+"*"+c+")/"+d);
               else if (((a-f)+d)==24)
                  solfield.setText(a+"-("+b+"*"+c+")+"+d);
               else if (((a/f)+d)==24)
                  solfield.setText(a+"/("+b+"*"+c+")+"+d);
               else if (((a/f)-d)==24)
                  solfield.setText(a+"/("+b+"*"+c+")-"+d);
               else if (((a+f)/d)==24)
                  solfield.setText(a+"+("+b+"*"+c+")/"+d);
               else if (((a+f)-d)==24)
                  solfield.setText(a+"+("+b+"*"+c+")-"+d);

               f=b-c;
               if (((a*f)/d)==24)
                  solfield.setText(a+"*("+b+"-"+c+")/"+d);
               else if (((a*f)+d)==24)
                  solfield.setText(a+"*("+b+"-"+c+")+"+d);
               else if (((a/f)+d)==24)
                  solfield.setText(a+"/("+b+"-"+c+")+"+d);
               else if (((a/f)*d)==24)
                  solfield.setText(a+"/("+b+"-"+c+")*"+d);

               f=b/c;
               if (((a-f)*d)==24)
                  solfield.setText(a+"-("+b+"/"+c+")*"+d);
               else if (((a-f)+d)==24)
                  solfield.setText(a+"-("+b+"/"+c+")+"+d);
               else if (((a*f)+d)==24)
                  solfield.setText(a+"*("+b+"/"+c+")+"+d);
               else if (((a*f)-d)==24)
                  solfield.setText(a+"*("+b+"/"+c+")-"+d);
               else if (((a+f)*d)==24)
                  solfield.setText(a+"+("+b+"/"+c+")*"+d);
               else if (((a+f)-d)==24)
                  solfield.setText(a+"+("+b+"/"+c+")-"+d);

               f=b+c;
               if (((a*f)/d)==24)
                  solfield.setText(a+"*("+b+"+"+c+")/"+d);
               else if (((a*f)-d)==24)
                  solfield.setText(a+"*("+b+"+"+c+")-"+d);
               else if (((a/f)-d)==24)
                  solfield.setText(a+"/("+b+"+"+c+")-"+d);
               else if (((a/f)*d)==24)
                  solfield.setText(a+"/("+b+"+"+c+")*"+d);






            });

      btShuffle.setOnAction(
            e -> {
               center.getChildren().clear();  
               Collections.shuffle(deck);
               card1 = new Card(deck.get(0));
               card2 = new Card(deck.get(1));
               card3 = new Card(deck.get(2));
               card4 = new Card(deck.get(3));

               center.getChildren().add(card1);
               center.getChildren().add(card2);           
               center.getChildren().add(card3);
               center.getChildren().add(card4);

            });

      HBox bottom = new HBox(10);
      Label expression = new Label("Please Enter the expression: ");

      TextField tfExpress = new TextField();

      String str = tfExpress.getText();       

      Button btVerify = new Button("Verify");
      bottom.getChildren().add(expression);
      bottom.getChildren().add(tfExpress);
      bottom.getChildren().add(btVerify);

      bottomBox.getChildren().add(bottom);
      bottomBox.getChildren().add(display);

      btVerify.setOnAction(
            (ActionEvent e) -> 
            {               
               String regex = ("[^0-9]+");
               String[] inputIntegers = tfExpress.getText().split(regex);

              // expInput.removeIf(p-> p.equals(signs));
               ArrayList<Integer> temp = new ArrayList<>();
               temp.add(new Integer(card1.CardValue()));
               temp.add(new Integer(card2.CardValue()));                    
               temp.add(new Integer(card3.CardValue()));          
               temp.add(new Integer(card4.CardValue())); 

               if(inputIntegers.length != 0)
               {
                  if (inputIntegers.length != 0) {
                     for (String s : inputIntegers) {
                        if (!s.equals(""))
                           temp.remove(new Integer(Integer.valueOf(s)));
                     }
                  }        
               }

               if(temp.isEmpty())
               {
                  if(evaluateExpression(tfExpress.getText()) == 24){
                     display.setText("Correct");
                  }
                  else
                     display.setText("Incorrect");
               }
               else
                  display.setText("The numbers in the expression don't "
                     + "match the numbers in the set.");
            });

      pane.setTop(top);
      pane.setCenter(center);
      pane.setBottom(bottomBox);


      Scene scene = new Scene(pane);
      primaryStage.setTitle("24 card game");
      primaryStage.setScene(scene);
      primaryStage.show();
   }

   public boolean Twentyfour(boolean par){
   //some people play the game with or without parentheses.
      this.par=par;

      return par;
   }

   /** Evaluate an expression */
   public static int evaluateExpression(String expression) {
    // Create operandStack to store operands
      Stack<Integer> operandStack = new Stack<Integer>();

    // Create operatorStack to store operators
      Stack<Character> operatorStack = new Stack<Character>();

    // Insert blanks around (, ), +, -, /, and *
      expression = insertBlanks(expression);

    // Extract operands and operators
      String[] tokens = expression.split(" ");

    // Phase 1: Scan tokens
      for (String token: tokens) {
         if (token.length() == 0) // Blank space
            continue; // Back to the while loop to extract the next token
         else if (token.charAt(0) == '+' || token.charAt(0) == '-') {
         // Process all +, -, *, / in the top of the operator stack 
            while (!operatorStack.isEmpty() &&
            (operatorStack.peek() == '+' || 
            operatorStack.peek() == '-' ||
            operatorStack.peek() == '*' ||
            operatorStack.peek() == '/')) {
               processAnOperator(operandStack, operatorStack);
            }

         // Push the + or - operator into the operator stack
            operatorStack.push(token.charAt(0));
         }
         else if (token.charAt(0) == '*' || token.charAt(0) == '/') {
         // Process all *, / in the top of the operator stack 
            while (!operatorStack.isEmpty() &&
            (operatorStack.peek() == '*' ||
            operatorStack.peek() == '/')) {
               processAnOperator(operandStack, operatorStack);
            }

         // Push the * or / operator into the operator stack
            operatorStack.push(token.charAt(0));
         }
         else if (token.trim().charAt(0) == '(') {
            operatorStack.push('('); // Push '(' to stack
         }
         else if (token.trim().charAt(0) == ')') {
         // Process all the operators in the stack until seeing '('
            while (operatorStack.peek() != '(') {
               processAnOperator(operandStack, operatorStack);
            }

            operatorStack.pop(); // Pop the '(' symbol from the stack
         }
         else { // An operand scanned
         // Push an operand to the stack
            operandStack.push(new Integer(token));
         }
      }

    // Phase 2: process all the remaining operators in the stack 
      while (!operatorStack.isEmpty()) {
         processAnOperator(operandStack, operatorStack);
      }

    // Return the result
      return operandStack.pop();
   }

  /** Process one operator: Take an operator from operatorStack and
   *  apply it on the operands in the operandStack */
   public static void processAnOperator(
      Stack<Integer> operandStack, Stack<Character> operatorStack) {
      char op = operatorStack.pop();
      int op1 = operandStack.pop();
      int op2 = operandStack.pop();
      if (op == '+') 
         operandStack.push(op2 + op1);
      else if (op == '-') 
         operandStack.push(op2 - op1);
      else if (op == '*') 
         operandStack.push(op2 * op1);
      else if (op == '/') 
         operandStack.push(op2 / op1);
   }

   public static String insertBlanks(String s) {
      String result = "";

      for (int i = 0; i < s.length(); i++) 
      {
         if (s.charAt(i) == '(' || s.charAt(i) == ')' || 
          s.charAt(i) == '+' || s.charAt(i) == '-' ||
          s.charAt(i) == '*' || s.charAt(i) == '/')
            result += " " + s.charAt(i) + " ";
         else
            result += s.charAt(i);
      }

      return result;
   }

   public class Card extends Pane {
      public int cardVal;
      Card(int card){
         Image cardImage;
         cardImage = new Image("card/"+ card +".png");
         getChildren().add(new ImageView(cardImage));
         cardVal = card;
      }

      public int CardValue(){
         int card = 0;

         if(cardVal <= 13){
            card = cardVal;
         }
         else if(cardVal > 13 && cardVal <= 26){
            card = cardVal - 13;
         }
         else if(cardVal > 26 && cardVal <= 39){
            card = cardVal - 26;
         }
         else if(cardVal > 39 && cardVal <= 52){
            card = cardVal - 39;
         }

         return card;
      }
   }

   public static void main(String[] args) {
      launch(args);
   }
}
John Doe
  • 107
  • 1
  • 8

3 Answers3

4

An enum could help here.

I'm not sure I have it complete as I am not sure I have implemented all of the possible bracketing but it looks complete to me now.

enum Op {

    Add("+") {

                @Override
                int op(int a, int b) {
                    return a + b;
                }

            },
    Sub("-") {

                @Override
                int op(int a, int b) {
                    return a - b;
                }

            },
    Mul("*") {

                @Override
                int op(int a, int b) {
                    return a * b;
                }

            },
    Div("/") {

                @Override
                int op(int a, int b) {
                    // Insane value for / 0 to ensure unlikely to match.
                    return b != 0 ? a / b : Integer.MAX_VALUE;
                }

            };
    final String asString;

    Op(String asString) {
        this.asString = asString;
    }

    public String toString() {
        return asString;
    }

    abstract int op(int a, int b);
}

private void tryAllOrders(int a, int b, int c, int d, Op op1, Op op2, Op op3, int target) {
    if (op3.op(op2.op(op1.op(a, b), c), d) == target) {
        System.out.println("    ((" + a + op1 + b + ")" + op2 + c + ")" + op3 + d + "=" + target);
    }
    if (op3.op(op1.op(a, op2.op(b, c)), d) == target) {
        System.out.println("    (" + a + op1 + "(" + b + op2 + c + "))" + op3 + d + "=" + target);
    }
    if (op2.op(op1.op(a, b), op3.op(c, d)) == target) {
        System.out.println("    (" + a + op1 + b + ")" + op2 + "(" + c + op3 + d + ")=" + target);
    }
    if (op1.op(a, op3.op(op2.op(b, c), d)) == target) {
        System.out.println("    " + a + op1 + "((" + b + op2 + c + ")" + op3 + d + ")=" + target);
    }
    if (op1.op(a, op2.op(b, op3.op(c, d))) == target) {
        System.out.println("    " + a + op1 + "(" + b + op2 + "(" + c + op3 + d + "))=" + target);
    }
}

private void tryAllOps(int a, int b, int c, int d, int target) {
    for (Op op1 : Op.values()) {
        for (Op op2 : Op.values()) {
            for (Op op3 : Op.values()) {
                tryAllOrders(a, b, c, d, op1, op2, op3, target);
            }
        }
    }
}

public void test() {
    int target = 24;
    for (int a = 1; a <= 13; a++) {
        for (int b = 1; b <= 13; b++) {
            for (int c = 1; c <= 13; c++) {
                for (int d = 1; d <= 13; d++) {
                    tryAllOps(a, b, c, d, target);
                }

            }

        }
    }
}

It prints:

((1+1)+1)*8=24
(1+(1+1))*8=24
(1+1)*(1+11)=24
((1+1)*1)*12=24
(1+(1*1))*12=24
(1+1)*(1*12)=24
...
7+((11*8)/5)=24
((7+11)*8)/6=24
((7-11)+8)*6=24
(7-(11-8))*6=24
((7+11)/8)*12=24
(7/(11-8))*12=24
...
((13/13)*13)+11=24
(13/13)*(13+11)=24
(13/(13/13))+11=24
((13+13)/13)*12=24
(13-(13/13))+12=24
13-((13/13)-12)=24

There's a total of 67,752 results.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • As far as I understood it, the OP had some values for A, B, C and D already and wanted to combine them. But what you have here does seem useful for that. – Simon Forsberg May 06 '15 at 16:05
  • @SimonAndréForsberg - You are correct - I generated all options more as a demonstration of the tecnique than a suggested solution to the posted problem. I have refactored to make that clearer. – OldCurmudgeon May 06 '15 at 16:30
  • So here I would need to add an option to return c and d as well? Or does and b suffice for evaluating expressions with 4 integers? – John Doe May 06 '15 at 16:57
  • `return b != 0 ? a / b : Integer.MAX_VALUE;` good catch to handle division by 0 for `int` values. – h.j.k. May 06 '15 at 23:37
  • @JohnDoe - I don't understand - you wanted combinations of 3 ops on 4 values to add up to 24 - or have I got it wrong? – OldCurmudgeon May 07 '15 at 07:26
2

Ah, combinatorics!

First of all, do one calculation by hand: How many combinations are there?

  • Each variable can only be used once.
  • The order of the variables matter (this can be seen as the parenthesis)
  • Between each variable is a mathematical operation, one of + - * /, each operation can be used more than once.

Four variables, ordered, creates \$4! = 4 * 3 * 2 * 1 = 24\$ combinations.

The different mathematical operations creates \$4^3 = 64\$ combinations.

This makes a total of \$64 * 24 = 1536\$ combinations. That's not too much, so brute-forcing is acceptable.

One approach:

This is a recursive way of choosing a variable and an operator, and adding it to the result.

Note that this is just an example approach, and has not been tested, it will require some adjustment by you to get it to working correctly.

void result(List<Variable> remaining, List<Variable> used, List<Operator> operatorsUsed, int current) {
    for (Variable variable : remaining) {
        for (Operator operation : operators) {
            int newValue = operation.perform(current, variable.getValue());
            List<Variable> copy = new ArrayList<>(variables);
            copy.remove(variable);
            List<Operator> usedOps = new ArrayList<>(operatorsUsed);
            usedOps.add(operation);
            int result = result(copy, newValue);
            if (result == 24) {
                 solfield.setText(createString(used, operatorsUsed));
            }
        }
    }
}
Simon Forsberg
  • 13,086
  • 10
  • 64
  • 108
  • Simon this looks great! so if the variables a, b, c, d are set to my card values, where could I insert them into this? I'm a little confused since I have four values and not just one that encompasses the whole expression if that makes sense. – John Doe May 06 '15 at 15:55
  • 1
    @JohnDoe So instead of `Variable`, use `Integer`. And for the `Operator` part, use the `enum` that OldCurmudgeon recommended. Also consider Java 8's `IntBinaryOperator` – Simon Forsberg May 06 '15 at 16:04
  • so something like for(Integer variable: remaining)? And will I have to actually add values to the List like my a, b, c, d? I posted full code above. – John Doe May 06 '15 at 16:58
0

9216 different equations - 6 spots for parentheses (given a 1 b 2 c 3 d, operations happen in 6 different orders), 64 (4x4x4) different cases of operators and 24 different number orderings.

Clearly you can't write these by hand.

What you can do, however, is manually define each of these classes and have three for-loops, each iterating through their list of operations.

For the actual implementation, I think strategy pattern would be best as it allows you to divide the problem into small chunks.

... This is a brute force solution though, I think there must be something better...

Pimgd
  • 5,983
  • 1
  • 30
  • 45