Specification

Enter an expression that uses the four numbers from the four selected cards. Each number must be used once and only once. You can use the operators (addition, subtraction, multiplication, and division) and parentheses in the expression. The expression must evaluate to 24. After entering the expression, click Verify button to check whether the numbers in the expression is correct. Display the verification in a message window (see figures below).

Assume that images are stored in files named 1.png, 2.png, ..., 52.png, in the order of spades, hearts, diamonds, and clubs. So, the first 13 images are for spades 1, 2, 3, ..., 13.

Assume that no spaces are entered before the first token, in between tokens, and after the last token.

Assume that only numbers can be entered as operands.

Numbers match and the result is 24: see image.

Numbers don't match: see image.

Numbers don't match: see image.

More example,

If 2, 3, 4, 5 are selected and displayed, user can try one of the following expressions to win.

2*((3+4)+5)
2*(3+(4+5))
2*((3+5)+4)
2*(3+(5+4))
2*((4+3)+5)

Design

The following is the design diagram. Part of the design (Graphical User Interface, GUI) has been done. You need to complete the other part of the design and submit only that part of the design.

This part, Non-GUI, needs to be completed by you: see image.

Class Expression is instantiated and evaluate of the class is invoked in the GUI part. All exceptions must be handled in the Non-GUI design.

GUI Design provided:

To store the 58 images, you must create folder image in the project folder, create a subfolder card, and store all images in folder card.

The four Java programs for GUI have been provided for you to use. You are not allowed to change the GUI programs. You need to complete the other three programs, and test the project completely.

GUI Programs: see image.

Non-GUI design to be completed by you:

In this application, an expression (an infix expression) is entered according to the cards shown, and the expression is evaluated. The result is correct if it is 24. To evaluate an infix expression, convert it to an equivalent postfix expression, and evaluate the postfix. It is required to use the infix to postfix algorithm and postfix evaluation algorithm for this project.

In this part of the design, two types of objects needs to be designed: Expression and ADT Stack. An expression object contains an infix expression (a string) and operations such as evaluate and infixToPostfix. In both operations, an ADT stack is needed. Method evaluate should convert this infix expression to postfix and evaluate the postfix expression. It is required to design an ADT Stack and use it in this project. Using Stack class from Java library or others will result in zero credit for this project.

A Generic ADT Stack:

Both infixToPostfix algorithm and evaluate algorithm must use a stack to store tokens. A generic ADT Stack must be designed (GenericStackInterface) and implemented using an array list. It should have the following operations:

  • Construct an empty stack (GenericStack() {...}).
  • Return the number of objects in the stack (getSize() {...}).
  • Return a reference to the top element of this stack (peek() {...}).
  • Add an object to the top of the stack (push(E o) {...}).
  • Remove from the top of this stack (pop() {...}).
  • Indicate if this stack is empty (isEmpty() {...}).
public class GenericStack< E >{
private java.util.ArrayList< E > list;
...
}

Expression:

Define class Expression including the method infixToPostfix() and method evaluate(). Other methods such as constructors and helper methods should be included in the class. From the GUI side, Expression constructor is called with a string pased into the constructor, and an Expression object can call the evaluate method that returns the result of the expression. See the example below:

...
Expression exp = new Expression(A string literal);
.. exp.evaluate() == 24 ...

You can't change the GUI source codes. You are responsible for handling all the exceptions in the Non-GUI part.

infixToPostfix: Converts this infix expression to an equivalent postfix expression, returns a postfix expression as a list of tokens.

public ArrayList< String > infixToPostfix() {...}

Infix to postfix algorithm (using a Stack):


As long as there are more tokens, get the next token.
If the token is an operand, append it to the postfix string.
If the token is "(", push it onto the stack.
If the token is an operator, (order operators by precedence)
If the stack is empty, push the operator onto the stack.
If the stack is not empty, pop operators of greater or equal
precedence from the stack and append them to postfix string,
stop when you encounter ")" or an operator of lower precedence
or when the stack is empty. And then, push the new operator
onto the stack.
When you encounter a ")", pop operators off the stack and append
them to the end of the postfix string until you encounter a
matching "(".
When you reach the end of the infix string, append the remaining
content of the stack to the postfix String.

evaluate: This method invoke infixToPostfix to convert this infix expression to postfix, evaluate the postfix expression, and return the result.

public int evaluate() {...}

Helper methods: You may need to write helper methods such as methods for checking precedence in this class

When splitting an infix expression into tokens, you should not use charAt method and expect all operands are single-digit tokens. Instead you should split an infix expression by proper delimiters so that all operators, operands (single-digit or not), and parentheses are tokens. One way is to use operators and parentheses as delimiters to split an infix expression and also use them as regular tokens. Please check StringTokenizer class or String class for proper methods that can be used.

Code/Test/Debug:

Test the entire program by shuffling the cards to make different infix expressions.

Starter Codes

TwentyFourPointsActionListener.java

package csi213.project03.twentyfourpoints.resource;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.JOptionPane;

/**
* Process actions event triggered by a button
*
*/
public class TwentyFourPointsActionListener implements ActionListener {

/**
* A reference to a panel
*/
private JPanel panel;

/**
* Constructs an action listener with a panel.
*
* @param panel A reference to a panel
*/
public TwentyFourPointsActionListener(JPanel panel) {
this.panel = panel;
}

/**
* Invoked when an action occurs.
*
* @param e A reference to an action event object
*/
public void actionPerformed(ActionEvent e) {
if (this.panel instanceof TwentyFourPointsPanel) {
TwentyFourPointsPanel temp = (TwentyFourPointsPanel) this.panel;
if (e.getSource() == temp.getVerify()) {

// Check whether all numbers in the expression are currently selected
if (!temp.correctNumbers()) {
JOptionPane.showMessageDialog(null, "The numbers in the expression don't \nmatch the numbers in the set ");
} else {
// Check whether the expression evaluates to 24.
if (temp.evaluate()) {
JOptionPane.showMessageDialog(null, "Correct");
} else {
JOptionPane.showMessageDialog(null, "Incorrect result");
}
}
}

if (e.getSource() == temp.getRefresh()) {
temp.refresh();
}

}
}
}

TwentyFourPointsDriver.java

package csi213.project03.twentyfourpoints.resource;

/**
* Displays a 2-point game.
*
*/
public class TwentyFourPointsDriver {

/**
* Displays a 2-point game.
*
* @param args A reference to a string array
*/
public static void main(String[] args) {
new TwentyFourPointsFrame();
}
}

TwentyFourPointsFrame.java

package csi213.project03.twentyfourpoints.resource;

import javax.swing.JFrame;

/**
* Displays 24-point game.
*/
public class TwentyFourPointsFrame extends JFrame {

/**
* Constructs an interface of 24-point game.
*/
public TwentyFourPointsFrame() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("24-Point Card Game");

TwentyFourPointsPanel panel = new TwentyFourPointsPanel();
this.getContentPane().add(panel);

this.pack();
this.setVisible(true);
}
}

TwentyFourPointsPanel.java

package csi213.project03.twentyfourpoints.resource;

import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.JTextField;
import javax.swing.ImageIcon;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Dimension;
import project03.twentyfourpoints.Expression;

/**
* Display all components of 24-point game. The components are a refresh button,
* a panel with four labels with card image icons, a text field for an
* expression, and a verify button.
*/
public class TwentyFourPointsPanel extends JPanel {

/**
* The refresh button
*/
private JButton refresh;
/**
* The verify button
*/
private JButton verify;
/**
* The cards panel
*/
private JPanel cards;
/**
* The expression text field
*/
private JTextField expression;
/**
* The first card label
*/
private JLabel card1;
/**
* The second card label
*/
private JLabel card2;
/**
* The third card label
*/
private JLabel card3;
/**
* The fourth card label
*/
private JLabel card4;
/**
* The 52 image icons
*/
private ImageIcon[] cardIcons = new ImageIcon[52];
/**
* THe 52 integers from 1 to 52
*/
private ArrayList< Integer > list = new ArrayList< Integer >();

/**
* Current card values
*/
private ArrayList< Integer > currentCardValues = new ArrayList< Integer >();

/**
* Constructs a 24-point game panel.
*/
public TwentyFourPointsPanel() {
this.setPreferredSize(new Dimension(350, 180));
this.setLayout(new BorderLayout());

//Load all 52 numbers that will be shuffled.
for (int i = 0; i < 52; i++) {
this.list.add(i);
}
// Load the image icons
for (int i = 0; i < 52; i++) {
this.cardIcons[i] = new ImageIcon("image/card/" + (i + 1) + ".png");
}

// refresh panel
JPanel panel1 = new JPanel(new FlowLayout(FlowLayout.RIGHT));
this.refresh = new JButton("Refresh");
panel1.add(this.refresh);

// card panel
this.card1 = new JLabel();
this.card2 = new JLabel();
this.card3 = new JLabel();
this.card4 = new JLabel();
this.cards = new JPanel();
this.cards.add(this.card1);
this.cards.add(this.card2);
this.cards.add(this.card3);
this.cards.add(this.card4);

//expression panel
this.verify = new JButton("Verify");
this.expression = new JTextField(8);
JPanel panel3 = new JPanel(new BorderLayout());
panel3.add(new JLabel("Enter an expression: "), BorderLayout.WEST);
panel3.add(this.expression, BorderLayout.CENTER);
panel3.add(this.verify, BorderLayout.EAST);

this.add(panel1, BorderLayout.NORTH);
this.add(this.cards, BorderLayout.CENTER);
this.add(panel3, BorderLayout.SOUTH);

//Chooses the first four cards after they are shuffled. Changes card image icons accordingly.
this.refresh();

TwentyFourPointsActionListener listener = new TwentyFourPointsActionListener(this);
this.refresh.addActionListener(listener);
this.verify.addActionListener(listener);
}

/**
* Verifies if numbers of the expression matches the numbers of the cards.
*
* @return A boolean value specifying if numbers of the expression matches
* the numbers of the cards
*/
public boolean correctNumbers() {
// Constructs a string tokenizer for the specified expression.
// The delimiters are ()+-/* that are not returned as tokens.
// Only operands in the expression are returned as tokens.
StringTokenizer tokens = new StringTokenizer(this.expression.getText().trim(), "()+-/*", false);

// The array list of operands of this expression
ArrayList< Integer > valueList = new ArrayList< Integer >();

// Each token is returned as a string that can be used to make an Integer object.
while (tokens.hasMoreTokens()) {
//valueList.add(Integer.parseInt(tokens.nextToken()));
valueList.add(new Integer(tokens.nextToken()));
}

Collections.sort(valueList);
Collections.sort(this.currentCardValues);

return valueList.equals(this.currentCardValues);
}

/**
* Evaluates current expression, and return a value to indicate if the
* result is equal to 24. Converts this infix expression into a postfix
* form, and evaluates the postfix form.
*
* @return A boolean value specifying if the result is equal to 24
*/
public boolean evaluate() {
Expression exp = new Expression(this.expression.getText().trim());
return exp.evaluate() == 24;
}

/**
* Chooses the first four cards after they are shuffled. Changes card image
* icons accordingly.
*/
public void refresh() {
//Clear the expression
this.expression.setText(null);

//Shuffle the list of cards(integers)
Collections.shuffle(this.list);

//Pick the first four as the fours cards
int index1 = this.list.get(0);
int index2 = this.list.get(1);
int index3 = this.list.get(2);
int index4 = this.list.get(3);

//Change card image icons accordingly
this.card1.setIcon(this.cardIcons[index1]);
this.card2.setIcon(this.cardIcons[index2]);
this.card3.setIcon(this.cardIcons[index3]);
this.card4.setIcon(this.cardIcons[index4]);

//Clear the previous card values, and add new card values
//Card values 1 to 13 repeat
this.currentCardValues.clear();
this.currentCardValues.add(index1 % 13 + 1);
this.currentCardValues.add(index2 % 13 + 1);
this.currentCardValues.add(index3 % 13 + 1);
this.currentCardValues.add(index4 % 13 + 1);
}

/**
* Returns a reference to this refresh button.
*
* @return A reference to a button
*/
public JButton getRefresh() {
return this.refresh;
}

/**
* Returns a reference to this verify button.
*
* @return A reference to a button
*/
public JButton getVerify() {
return this.verify;
}
}
Academic Honesty!
It is not our intention to break the school's academic policy. Posted solutions are meant to be used as a reference and should not be submitted as is. We are not held liable for any misuse of the solutions. Please see the frequently asked questions page for further questions and inquiries.
Kindly complete the form. Please provide a valid email address and we will get back to you within 24 hours. Payment is through PayPal, Buy me a Coffee or Cryptocurrency. We are a nonprofit organization however we need funds to keep this organization operating and to be able to complete our research and development projects.