游戏规则
一副扑克牌一共有54张,在不考虑双王的情况下,从剩余的52张牌中每次随机抽取4张扑克牌,通过利用+、-、*、/以及()对4张牌进行数学运算,判断计算的结果是否等于24。
实现原理
在运算过程中,如果不考虑运算符号的优先级情况下,可以将凑24点游戏模型化为如下式子:
A[op1]B[op2]C[op3]D
其中A、B、C以及D代表的是操作数,op1、op2以及op3代表的是运算符。
由于运算符的优先级可以通过()操作符进行转变,因此在实际中可以通过()的不同搭配来迎合运算符的优先级,可以模型化为:
1. 第一种模型
((A[op1]B)[op2](C[op3]D))
2. 其余模型
(((A[op1]B)[op2]C)[op3]D)
((A[op1](B[op2]C))[op3]D)
(A[op1](B[op2](C[op3]D)))
通过树的形式将四种模型具现化(如下图,其中op表示运算符,叶节点表示操作数),发现可以将(2)中的三种模型合并为一种。
Java实现
扑克牌类
扑克牌类主要提供了扑克牌的花色表示、数字表示以及结合两者的表示,可通过任意一种进行直接访问。
package com.game.gather24;
public class Card
{
public final static int MAX_CARDS = 52;
public final static int MAX_SUITS = 4;
private final static String []SUITS = new String[]{"C", "D", "H", "S"}; /* 梅花, 方块, 红桃, 黑桃 */
private final static String [] CARDS = new String[]{"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
private String suit;
private String card;
private int ranking;
public Card()
{
}
public Card(int ranking)
{
this.card = getCardByRanking(ranking);
this.suit = getSuitByRanking(ranking);
this.ranking = ranking;
}
public Card(String suit, String card)
{
// this.suit = suit;
// this.card = card;
this.setCard(card);
this.setSuit(suit);
this.setRanking(suit, card);
}
private void setRanking(String suit, String card)
{
int cardIndex = 0;
int suitIndex = 0;
for (int iter = 0; iter < MAX_CARDS / MAX_SUITS; ++iter)
{
if (card.equals(CARDS[iter]))
{
cardIndex = iter;
break;
}
}
for (int iter = 0; iter < MAX_SUITS; ++iter)
{
if (suit.equals(SUITS[iter]))
{
suitIndex = iter;
break;
}
}
this.ranking = cardIndex * MAX_SUITS + suitIndex;
}
public int getRanking()
{
return this.ranking;
}
private void setSuit(String suit)
{
this.suit = suit;
}
public String getSuit()
{
return this.suit;
}
private void setCard(String card)
{
this.card = card;
}
public String getCard()
{
return this.card;
}
public String getCardByRanking(int ranking)
{
return CARDS[ranking / MAX_SUITS];
}
public String getSuitByRanking(int ranking)
{
return SUITS[ranking % MAX_SUITS];
}
public int getCardValueByRanking(int ranking)
{
String card = getCardByRanking(ranking);
int cardIndex = 0;
for (int iter = 0; iter < CARDS.length; ++iter)
{
if (card.equals(CARDS[iter]))
{
cardIndex = iter;
return cardIndex + 1;
}
}
return 0;
}
public int hashCode()
{
return this.ranking;
}
public boolean equal(Object obj)
{
if (this == obj)
return true;
if (obj != null && obj instanceof Card)
{
Card card = (Card)obj;
return (this.hashCode() == card.hashCode());
}
else
return false;
}
public String toString()
{
return (card + "" + suit);
}
}
抽象结构类
抽象结构了提供了额两种树结构的统一接口以及部分实现,用于计算结构。
package com.game.gather24;
public abstract class StructForGather24
{
protected final static double RESULT = 24.0;
protected final static double PRECISION = 0.000001;
protected final static int NUM_NUM = 4;
protected final static int NUM_OPR = 3;
protected int [] num = new int[NUM_NUM];
protected String [] opr = new String[NUM_OPR];
protected String expression;
protected double finalResult;
public StructForGather24()
{
this.expression = "";
this.finalResult = 0.0;
}
public StructForGather24(String str)
{
this.expression = str;
this.finalResult = 0.0;
}
public StructForGather24(String str, int aNum)
{
this.expression = str;
this.finalResult = Double.parseDouble(Integer.toString(aNum));
}
public StructForGather24(int [] aNum, String [] aStr)
{
setExpression("");
setResult(0);
System.arraycopy(aNum, 0, this.num, 0, NUM_NUM);
System.arraycopy(aStr, 0, this.opr, 0, NUM_OPR);
}
public void insertOpr(String opr, int index)
{
if (index < 0)
{
throw new ArrayIndexOutOfBoundsException(index);
}
else if (index >= NUM_OPR)
{
throw new ArrayIndexOutOfBoundsException(index + " >= " + NUM_OPR);
}
this.opr[index] = opr;
}
public String getOpr(int index)
{
if (index < 0)
{
throw new ArrayIndexOutOfBoundsException(index);
}
else if (index >= NUM_OPR)
{
throw new ArrayIndexOutOfBoundsException(index + " >= " + NUM_OPR);
}
return opr[index];
}
public void insertNum(int num, int index)
{
if (index < 0)
{
throw new ArrayIndexOutOfBoundsException(index);
}
else if (index >= NUM_NUM)
{
throw new ArrayIndexOutOfBoundsException(index + " >= " + NUM_NUM);
}
this.num[index] = num;
}
public int getNum(int index)
{
if (index < 0)
{
throw new ArrayIndexOutOfBoundsException(index);
}
else if (index >= NUM_NUM)
{
throw new ArrayIndexOutOfBoundsException(index + " >= " + NUM_NUM);
}
return num[index];
}
public abstract void computeResult();
protected void setExpression(String str)
{
this.expression = str;
}
public String getExpression()
{
return expression;
}
protected void setResult(int aNum)
{
this.finalResult = Double.parseDouble(Integer.toString(aNum));
}
public double getResult()
{
return finalResult;
}
public boolean equal24()
{
return (Math.abs(finalResult - RESULT) < PRECISION);
}
}
具体结构的实现
第一种模型
package com.game.gather24;
/*
* O
* / \
* O O
* / \ / \
* O O O O
* 1 2 3 4
*/
public class StrFir extends StructForGather24
{
private final int TMP_NUM = NUM_NUM - 2;
private double [] tmpResult = new double[TMP_NUM];
public StrFir()
{
super();
}
public StrFir(int [] aNum, String [] aStr)
{
super(aNum, aStr);
}
@Override
public void computeResult()
{
for (int i = 0, k = 0; i < NUM_NUM && k < TMP_NUM; i += 2, ++k)
{
if (opr[k].contains("+") || opr[k].contains("-") || opr[k].contains("*") || opr[k].contains("/"))
{
switch (opr[k])
{
case "+":
tmpResult[k] = Double.parseDouble(Integer.toString(num[i])) +
Double.parseDouble(Integer.toString(num[i + 1]));
break;
case "-":
tmpResult[k] = Double.parseDouble(Integer.toString(num[i])) -
Double.parseDouble(Integer.toString(num[i + 1]));
break;
case "*":
tmpResult[k] = Double.parseDouble(Integer.toString(num[i])) *
Double.parseDouble(Integer.toString(num[i + 1]));
break;
case "/":
tmpResult[k] = Double.parseDouble(Integer.toString(num[i])) /
Double.parseDouble(Integer.toString(num[i + 1]));
break;
}
}
else
{
System.out.println("不允许使用除(+,-,*,/)之外的操作符!");
return ;
}
}
if (opr[NUM_OPR - 1].contains("+") || opr[NUM_OPR - 1].contains("-") ||
opr[NUM_OPR - 1].contains("*") || opr[NUM_OPR - 1].contains("/"))
{
switch (opr[NUM_OPR - 1])
{
case "+":
finalResult = tmpResult[0] + tmpResult[1];
break;
case "-":
finalResult = tmpResult[0] - tmpResult[1];
break;
case "*":
finalResult = tmpResult[0] * tmpResult[1];
break;
case "/":
finalResult = tmpResult[0] / tmpResult[1];
break;
}
}
else
{
System.out.println("不允许使用除(+,-,*,/)之外的操作符!");
return ;
}
expression = "";
if (equal24())
{
expression = "(" + num[0] + opr[0] + num[1] + ")" + opr[2] + "(" + num[2] + opr[1] + num[3] + ")";
}
}
}
第二种模型
package com.game.gather24;
/*
* O
* / \
* O O
* / \
* O O
* / \
* O O
*/
public class StrSec extends StructForGather24
{
private final int TMP_NUM = NUM_NUM; /* 最后一个用于存放finalResult */
private double [] tmpResult = new double[TMP_NUM];
public StrSec()
{
super();
}
public StrSec(int [] aNum, String [] aStr)
{
super(aNum, aStr);
}
@Override
public void computeResult()
{
tmpResult[0] = Double.parseDouble(Integer.toString(num[0]));
for (int i = 1; i < NUM_NUM; ++i)
{
if (opr[i - 1].contains("+") || opr[i - 1].contains("-") || opr[i - 1].contains("*") || opr[i - 1].contains("/"))
{
switch (opr[i - 1])
{
case "+":
tmpResult[i] = tmpResult[i - 1] + Double.parseDouble(Integer.toString(num[i]));
break;
case "-":
tmpResult[i] = tmpResult[i - 1] - Double.parseDouble(Integer.toString(num[i]));
break;
case "*":
tmpResult[i] = tmpResult[i - 1] * Double.parseDouble(Integer.toString(num[i]));
break;
case "/":
tmpResult[i] = tmpResult[i - 1] / Double.parseDouble(Integer.toString(num[i]));
break;
}
}
else
{
System.out.println("不允许使用除(+,-,*,/)之外的操作符!");
return ;
}
}
finalResult = tmpResult[TMP_NUM - 1];
if (equal24())
{
expression = "(((" + num[0];
for (int i = 1; i < NUM_NUM; ++i)
{
expression += opr[i - 1] + num[i] + ")";
}
}
}
}
游戏类
涉及到洗牌、发牌以及计算等。
package com.game.gather24;
import java.util.Random;
import java.util.Scanner;
import java.util.Vector;
public class Game
{
private static final int NUM_OPR_COMBINE = 64;
private static final int NUM_NUM_COMBINE = 24;
// private static final String [] gameResult = new String[]{new String("Victory"), new String("Defeat")};
private static final String [] oper = new String[]{"*", "/", "+", "-"};
private static final int NUM_CARDS = 4;
private Vector<Card> initCards = new Vector<>(Card.MAX_CARDS);
private Vector<Card> saveCards = new Vector<>(Card.MAX_CARDS);
private Card inUseCards[] = new Card[NUM_CARDS];
private StructForGather24 treeFir;
private StructForGather24 treeSec;
public int RadomIndex(int bound)
{
Random rd = new Random();
return rd.nextInt(bound);
}
/* 洗牌 */
public void shuffleCards(Vector<Card> cards)
{
for (int iter = 0; iter < cards.size(); ++iter)
{
int pos = RadomIndex(cards.size());
Card temp = cards.elementAt(iter);
cards.setElementAt(cards.elementAt(pos), iter);
cards.setElementAt(temp, pos);
}
}
public void dealCard(Vector<Card> cards)
{
for (int iter = 0; iter < NUM_CARDS; ++iter)
{
inUseCards[iter]= cards.get(0);
cards.removeElementAt(0);
}
}
public void initGame()
{
if (initCards.isEmpty() && !saveCards.isEmpty())
{
initCards.addAll(saveCards);
saveCards.clear();
shuffleCards(initCards);
}
else
{
for (int iter = 0; iter < Card.MAX_CARDS; ++iter)
{
initCards.add(iter, new Card(iter));
}
shuffleCards(initCards);
}
}
public void compute(Card [] cardSrc)
{
int m, n;
int token = 0; /* 0 --> null, 1 --> treeFir, 2 -- > treeSec*/
int [] cardNum = new int[NUM_CARDS];
String [][] tmpOpr = new String[NUM_OPR_COMBINE][StructForGather24.NUM_OPR];
int [][] tmpNum = new int[NUM_NUM_COMBINE][StructForGather24.NUM_NUM];
for (int iter = 0; iter < NUM_CARDS; ++iter)
{
cardNum[iter] = cardSrc[iter].getCardValueByRanking(cardSrc[iter].getRanking());
}
/* 构造符号 */
m = 0;
for (int i = 0; i < oper.length; ++i)
{
for (int j = 0; j < oper.length; ++j)
{
tmpOpr[m] = new String[]{oper[i], oper[j], "*"};
tmpOpr[m + 1] = new String[]{oper[i], oper[j], "/"};
tmpOpr[m + 2] = new String[]{oper[i], oper[j], "+"};
tmpOpr[m + 3] = new String[]{oper[i], oper[j], "-"};
m += 4;
}
}
/* 构造数字 */ // 更高效的办法?????
m = 0;
boolean [] flag = new boolean[NUM_CARDS];
for (int i = 0; i < StructForGather24.NUM_NUM; ++i)
{
flag[i] = true;
for (int j = 0; j < StructForGather24.NUM_NUM; ++j)
{
if (flag[j])
continue;
flag[j] = true;
for (int k = 0; k < StructForGather24.NUM_NUM; ++k)
{
if (flag[k])
continue;
flag[k] = true;
for (int l = 0; l < StructForGather24.NUM_NUM; ++l)
{
if (flag[l])
continue;
tmpNum[m++] = new int[]{cardNum[i], cardNum[j], cardNum[k], cardNum[l]};
}
flag[k] = false;
}
flag[j] = false;
}
flag[i] = false;
}
for (m = 0, token = 0; m < NUM_NUM_COMBINE; ++m)
{
for (n = 0; n < NUM_OPR_COMBINE; ++n)
{
treeFir = new StrFir(tmpNum[m], tmpOpr[n]);
treeFir.computeResult();
if (treeFir.equal24())
{
token = 1;
break;
}
treeSec = new StrSec(tmpNum[m], tmpOpr[n]);
treeSec.computeResult();
if (treeSec.equal24())
{
token = 2;
break;
}
}
if (n < NUM_OPR_COMBINE)
{
break;
}
}
if (token == 0)
{
System.out.println("无解!");
}
else if (token == 1)
{
System.out.println(treeFir.getExpression());
}
else
{
System.out.println(treeSec.getExpression());
}
}
public void exitGame()
{
initCards.clear();
saveCards.clear();
}
public void startGame()
{
String [] select = new String[]{new String("Continue"), new String("Exit")};
String choice = "continue";
Scanner scanner = new Scanner(System.in);
int step = 0;
do
{
if (!choice.toLowerCase().equals(select[0].toLowerCase()))
{
System.out.println("Game over at :" + step);
scanner.close();
break;
}
if (initCards.isEmpty())
{
initGame();
}
dealCard(initCards);
for (int iter = 0; iter < NUM_CARDS; ++iter)
{
System.out.print(inUseCards[iter].toString());
if (iter != NUM_CARDS - 1)
{
System.out.print(" ");
}
else
{
System.out.print("\n");
}
}
compute(inUseCards);
++step;
System.out.println("Game count:" + step);
System.out.println("Please your select(\"continue\" or \"exit\"):");
} while((choice = scanner.nextLine()) != null);
exitGame();
}
}
客户端类
在此并为提供具体的Frame实现,只提供了命令行交互实现。
package com.game.gather24;
public class StartGame
{
public static void main(String [] args)
{
new Game().startGame();
}
}
资源下载地址1https://github.com/chenchen199427/CardGame
资源下载地址2http://download.youkuaiyun.com/detail/u013201628/9824768