z24点纸牌游戏是从52张牌中挑选任意4张牌,如下图所示。 请注意,Jokers被排除在外。 每张卡代表一个号码。 Ace,King,Queen和Jack分别代表1,13,12和11。 您可以单击“刷新”按钮以获得四张新卡。
输入一个表达式,该表达式使用四张选定卡片中的四个数字。 每个号码必须使用一次且只能使用一次。 您可以在表达式中使用运算符(加法,减法,乘法和除法)和括号。 表达式必须求值为24.输入表达式后,单击“验证”按钮以检查表达式中的数字是否正确。 在消息窗口中显示验证(参见下图)。
假设图像以黑桃,红桃,方块和梅花的顺序存储在名为1.png,2.png,...,52.png的文件中。 因此,前13张图片适用于黑桃1,2,3 ... 13。
假设在第一个令牌之前,令牌之间和最后一个令牌之后没有输入空格。 假设只能输入数字作为操作数。
在src包下放一个image包,里面图片以黑桃♠️、红桃♥️、方块♦️、梅花♣️的顺序存储在名为1.png,2.png,...,52.png的文件中,在生成jar包时可以包含在内
Driver.java
package TwentyFourPoints;
/**
* Displays a 2-point game.
* @author Qi Wang
* @version 1.0
*/
public class Driver {
/**
* Displays a 2-point game.
* @param args A reference to a string array
*/
public static void main(String[] args) {
new TwentyFourPointsFrame();
}
}
Expression.java
package TwentyFourPoints;
import java.util.*;
/**
* filename: SyntaxErrorException.java
* package:project03.twentyfourpoints
* author:XuWanxiang
* date:2018
* description: this class show expression.
* @version 1.0
*/
public class Expression {
/**
* The content of this expression
*/
private String infix;
public Expression(){
this.infix = null;
}
public Expression(String infix){
this.infix = infix;
}
/**
* description: convert a string from infix to postfix
* @return - arrayListPostfix
* @throws Exception
*/
public ArrayList<String> infixToPostfix(){
ArrayList<String> arrayListPostfix = new ArrayList<String>();
String stringPostfix = null;
InfixToPostfixConvertor ITPC = new InfixToPostfixConvertor();
try {
stringPostfix = ITPC.convert(this.infix);
}catch (Exception e) {
e.printStackTrace();
}
StringTokenizer PostfixTokens = new StringTokenizer(stringPostfix);
while (PostfixTokens.hasMoreTokens()) {
arrayListPostfix.add(PostfixTokens.nextToken());
}
return arrayListPostfix;
}
/**
* description: evaluate Postfix to find out the value of the input equation.
* @return - the postfix string
* @throws Exception
*/
public int evaluatePostfix(){
ArrayList<String> postfix = infixToPostfix();
Stack<Double> s = new Stack<Double>();
Double a, b, result=0.0;
//a\b is Intermediate variables of the calculation process,result is variable that push in to the stack.
boolean isNumber;
for (int index = 0;index < postfix.size();index++){
try
{
isNumber = true;
result = Double.parseDouble(postfix.get(index));
//if the element is not number ,set isNumber false.
}
catch (Exception e)
{
isNumber = false;
}
if (isNumber)
//push the element in to the stack if the element is number.
s.push(result);
else
{ //pop two number and do the calculus and push the result in to the stack.
switch (postfix.get(index).charAt(0))
{
case '+':
a = s.pop();
b = s.pop();
s.push(b+a);
break;
case '-':
a = s.pop();
b = s.pop();
s.push(b-a);
break;
case '*':
a = s.pop();
b = s.pop();
s.push(b*a);
break;
case '/':
a = s.pop();
b = s.pop();
s.push(b/a);
break;
case '^':
a = s.pop();
b = s.pop();
s.push(Math.exp(a*Math.log(b)));//it's equal to "b^a".
break;
}
}
}
//after the loop ,there are only one element left in the stack.
double doubleRes = s.peek();
//return the peek element,but not delete it.
int integerRes = (int) doubleRes;
return integerRes;
}
//Other methods
/**
* description: get the value of the infix.
* @return - infix
*/
public String getInfix(){
return infix;
}
/**
* description: set the value of the infix.
* @param str
*/
public void setInfix(String str){
this.infix = str;
}
//Helper methods
}
InfixToPostfixConvertor
package TwentyFourPoints;
import java.util.*;
/**
* filename: InfixtoPostfixConvertor.java
* @package:InfixtoPostfixConvertor
* @author:XuWanxiang
* date:2018/11/7
* @description: this class crate a convertor that turn infix to postfix.
* @version 1.0
*/
public class InfixToPostfixConvertor {
/**
* The list of objects of this stack
*/
// the operator stack
private Stack<Character> operatorStack;
// the operators
private static final String OPERATORS = "+-*/()" ;
// the precedence of the operators
private static final int [] PRECEDENCE = { 1 , 1 , 2 , 2 , - 1 , - 1 };
// the postfix string
private StringBuilder postfix;
/**
* description: the default constructor
*/
public InfixToPostfixConvertor() {
// TODO Auto-generated constructor stub*/
postfix = new StringBuilder();
}
/**
* description: convert a string from infix to postfix
* @param infix - the infix string to input
* @return - the postfix string
* @throws Exception
*/
public String convert(String infix) throws Exception{
operatorStack = new Stack<Character>();
StringTokenizer infixTokens = new StringTokenizer(addSpace(infix));
try {
// process each token in the infix string
while (infixTokens.hasMoreTokens()){
String nextToken = infixTokens.nextToken();
char firstChar = nextToken.charAt(0);
// determine if it is an operator
if (Character.isJavaIdentifierStart(firstChar) || Character.isDigit(firstChar)) {
postfix.append(nextToken);
postfix.append(' ');
} else if (isOperator(firstChar)){
processOperator(firstChar);
} else {
throw new SyntaxErrorException("syntax error!");
}
}
// pop any remaining operators and append them to postfix
while (!operatorStack.isEmpty()){
Character op = operatorStack.pop();
// any '(' on the stack is not matched
if (op.charValue() == '('){
throw new SyntaxErrorException("syntax error!");
}
postfix.append(op);
postfix.append(' ');
}
return postfix.toString();
} catch (EmptyStackException e){
throw new SyntaxErrorException("the stack is empty!");
}
}
/**
* description: process operators
* @param op - the operator
*/
private void processOperator(char op){
if (operatorStack.isEmpty() || op == '(' ){
operatorStack.push(op);
} else {
// peek the operator stack
char topOp = operatorStack.peek().charValue();
if (precedence(op) > precedence(topOp)){
operatorStack.push(op);
} else {
// pop all stacked operators with equal or higher precedence than op
while (!operatorStack.isEmpty() && precedence(op) <= precedence(topOp)) {
operatorStack.pop();
if (topOp == '(' ){
break ;
}
postfix.append(topOp);
postfix.append( ' ' );
if (!operatorStack.isEmpty()){
// reset topOp
topOp = operatorStack.peek().charValue();
}
}
if (op != ')' ) {
operatorStack.push(op);
}
}
}
}
/**
* description: determine the precedence of an operator
* @param op - the operator
* @return - the precedence
*/
private int precedence(char op){
return PRECEDENCE[OPERATORS.indexOf(op)];
}
/**
* description: determine whether a char is an operator
* @param op - the given char
* @return - true if the char is an operator
*/
private boolean isOperator(char op){
return OPERATORS.indexOf(op) != -1;
}
/**
* description: add space between every element of a string
* @param str - the infix string to add Space
* @return - sb.toString()
* @throws Exception
*/
public String addSpace(String str){
StringTokenizer infixTokens = new StringTokenizer(str,OPERATORS,true);
StringBuilder sb = new StringBuilder();
while (infixTokens.hasMoreTokens()){
String nextToken = infixTokens.nextToken();
sb.append(nextToken);
if (infixTokens.hasMoreTokens()){
sb.append(" ");
}
}
return sb.toString();
}
}
SyntaxErrorException
package TwentyFourPoints;
/**
* filename: SyntaxErrorException.java
* package:project03.twentyfourpoints
* author:Xu Wanxiang
* date:2018/11/7
* description: this exception shows a syntax error.
*/
public class SyntaxErrorException extends Exception {
private static final long serialVersionUID = 1L;
public SyntaxErrorException( final String message) {
super(message);
}
}
TwentyFourPointsActionListener
package TwentyFourPoints;
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
* @author Qi Wang
* @version 1.0
*/
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();
}
}
}
}
TwentyFourPointsFrame
package TwentyFourPoints;
import javax.swing.JFrame;
/**
* Displays 24-point game.
* @author Qi Wang
* @version 1.0
*/
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
package TwentyFourPoints;
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;
/**
* 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.
* @author Qi Wang
* @version 1.0
*/
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(TwentyFourPointsPanel.class.getResource("/image/" + (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() {
//Create an expression with the user-entered expression.
Expression exp = new Expression(expression.getText().trim());
//Evaluate the expression, and check if the result is 24.
return exp.evaluatePostfix() == 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;
}
}