当前位置:编程学习 > JAVA >>

24点扑克牌游戏编程算法详解

   24点扑克牌游戏,简单点来说就是:一副扑克牌去掉大小王,任意发四张牌给玩家,玩家要将这四张牌做加减乘除运算,规则是四张牌的顺序不定但不可以重复,计算结果如果是24则玩家赢。本程序实现让计算机自动派牌,并判断扑克牌组合是否可以计算成24点,并输出所有计算方法。
    游戏的算法可描述如下:随机产生4个1-13的数字,然后生成表达式,表达式包括:四个1-13的数字、加减乘除运算符、成对出现的括号。这些表达式必须是合法的,什么是合法的表达式?举个例子,如(2+10)*(6-4)就是个合法的、并且计算结果是24的表达式,如2+4()*38+就是非法的表达式,(2+8)/5*2是个合法的表达式但计算结果不是24。然后根据运算法则计算这个表达式是否等于24。
    算法可逐步求解如下:
    现有三个集合,
    集合1={N1, N2, N3, N4},
   集合2={op1, op2, op3},
   集合3={"(", ")"}
   N1,N2,N3,N4可以是1-13中的任意一个数字,op1,op2,op3可以是"+","-","*","/"中的任意一个操作符
    (1)首先从集合1中随机抽取一个元素,并删除抽取出来的元素,然后再从集合2中随机抽取一个元素组成字符数组;
    (2)重复第一步操作,直到集合1为空,集合1为空时不再从集合2中抽取元素,最后得到的表达式表示如下所示:
     expression = N1 op1 N2 op2 N3 op3 N4
   (3)将集合3的元素组合插入到表达式expression中,得到的合法的表达式可以表示如下:
      (N1 op1 N2) op2 N3 op3 N4
     N1 op1 (N2 op2 N3) op3 N4
     N1 op1 N2 op2 (N3 op3 N4)
     (N1 op1 N2 op2 N3) op3 N4
     N1 op1 (N2 op2 N3 op3 N4)
     ((N1 op1 N2) op2 N3) op3 N4
     (N1 op1 (N2 op2 N3)) op3 N4
     N1 op1 ((N2 op2 N3) op3 N4)
     N1 op1 (N2 op2 (N3 op3 N4))
     (N1 op1 N2) op2 (N3 op3 N4)
     以上三步在Get24PointExpression.java中实现。
    (4)这样的话,根据排列和组合原理,将会得到C41*C31*C21*C11*C41*C41*C41*10=4!*4*4*4*10个合法的表达式,然后逐一分析这些表达式是否已经重复出现,然后再逐一将这些表达式转换成实际的数学运算式,并计算结果,判断计算结果是否等于24。
   这一步在Cancal24.java中实现。
   算法实现部分代码:
package PokerGame24Point;
import com.Allen.*;
public class Get24PointExpression {
/**
 * 求任意扑克组合的计算24点的表达式
 * @haram array 扑克组合
 * @return 计算24点的表达式
 */
static String[] get24CalExpression(int[] array) {
String[] op = {"+", "-", "*", "/"};
String[] ch = {"(", ")"};
String[] sentence = new String[array.length+op.length-1];

int opNumIndex = PublicFunction.getRandom(0, array.length);
sentence[0] = Integer.toString(array[opNumIndex]); 
array = PublicFunction.deleteOneElement(array, array[opNumIndex]);
int opIndex = PublicFunction.getRandom(0, op.length);
sentence[1] = op[opIndex];
opNumIndex = PublicFunction.getRandom(0, array.length);
sentence[2] =  Integer.toString(array[opNumIndex]); 
array = PublicFunction.deleteOneElement(array, array[opNumIndex]);
opIndex = PublicFunction.getRandom(0, op.length);
sentence[3] = op[opIndex];
opNumIndex = PublicFunction.getRandom(0, array.length);
sentence[4] = Integer.toString(array[opNumIndex]); 
array = PublicFunction.deleteOneElement(array, array[opNumIndex]);
opIndex = PublicFunction.getRandom(0, op.length);
sentence[5] = op[opIndex];
opNumIndex = PublicFunction.getRandom(0, array.length);
sentence[6] =  Integer.toString(array[opNumIndex]);

//用数组标识括号可能出现的位置
int[][] chindex1 = {{0, 4}, {2, 6},  {4, 8}, {0, 6}, {2, 8}};
int[][][] chindex2 = {{{0, 6}, {1, 5}}, {{0, 6}, {3, 7}}, 
{{2, 8}, {3, 7}}, {{2, 8}, {5, 9}}, {{0, 4}, {6, 10}}};
//随机产生括号位置组合,插入括号
int selectedArray = PublicFunction.getRandom(1, 3);
if (selectedArray == 1)
{
int index = PublicFunction.getRandom(0, 5);
sentence = PublicFunction.insertOneElement(
sentence, chindex2[index][0][0], ch[0]);
sentence = PublicFunction.insertOneElement(
sentence, chindex2[index][0][1], ch[1]);
sentence = PublicFunction.insertOneElement(
sentence, chindex2[index][1][0], ch[0]);
sentence = PublicFunction.insertOneElement(
sentence, chindex2[index][1][1], ch[1]);
}
else
{
int index = PublicFunction.getRandom(0, 5);
sentence = PublicFunction.insertOneElement(
sentence, chindex1[index][0], ch[0]);
sentence = PublicFunction.insertOneElement(
sentence, chindex1[index][1], ch[1]);
}
return sentence;
}
}


PublicFunction.java

package com.allen;
import java.util.Random;

public class PublicFunction {
/**
 * 获取指定范围的随机数
 * @param from 指定范围最小值
 * @param to   指定范围最大值
 * @return     指定范围的随机数
 */
public static int getRandom(int from, int to) {
return new Random().nextInt(to)%(to-from) + from;
}
/**
 * 求阶乘
 * @param n 阶数
 * @return 阶乘
 */
public static int getFabonacci(int n) {
if (n==0 | n == 1) return 1;
else return n*getFabonacci(n-1);
}

/**
 * 删除整型数组中的一个元素
 * @param 整型数组
 * @param 删除的元素
 * @return 删除后的整型数组
 */
public static int[] deleteOneElement(int[] array, int element) {
int[] result = new int[array.length-1];
int mark = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] == element) {
mark = i;
break;
}
}
int index = 0;
for (int i = 0; i < mark; i++) {
result[index] = array[i]; 
index++;
}
for (int i = mark + 1; i < array.length;  i++) {
result[index] = array[i];
index++;
}
return result;
}

/**
 * 判断字符数组array中是否存在元素element
 * @param array   字符数组
 * @param element 元素
 * @return        数组array中是否存在元素element
 */
public static boolean isExistsElement(String[] array, String element) {
boolean result = false;
if (array == null) return false;
for (int i = 0; i < array.length; i++) {
if (array[i] == null) break;
if (array[i].equals(element)) 
result = true;
}
return result;
}

/**
 * 字符串数组插入一个元素
 * @param array 字符串数组
 * @param insertIndex 插入位置
 * @param element 插入元素
 * @return 插入后的字符串数组
 */
public static String[] insertOneElement(
String[] array, int insertIndex, String element) {
String[] result = new String[array.length+1];
int index = 0;
for (int i = 0; i < insertIndex; i++) {
result[index] = array[i];
index++;
}
result[index] = element;
index++;
if (insertIndex == (array.length-1))
result[index] = array[insertIndex];
for (int i = insertIndex; i < array.length; i++) {
result[index] = array[i];
index++;
}
return result;
}
}
--------------------编程问答-------------------- 顶一个~ --------------------编程问答-------------------- 路过瞧瞧 --------------------编程问答-------------------- 路过求粉....写得看起来很厉害的样子  --------------------编程问答--------------------
引用 2 楼 shadowsick 的回复:
路过瞧瞧
贴出来的目的是为了看下大家有没有什么好建议的。 --------------------编程问答--------------------
帮顶下! --------------------编程问答-------------------- 多谢支持,其实只是想运用高中和大学学到的数学知识来解决一下实际问题,Swing我还没有开始研究,很快就会将这个游戏改成GUI的了。 --------------------编程问答-------------------- 打算用穷举法么? --------------------编程问答--------------------
引用 6 楼 cockroachhz2012 的回复:
多谢支持,其实只是想运用高中和大学学到的数学知识来解决一下实际问题,Swing我还没有开始研究,很快就会将这个游戏改成GUI的了。

是啊,想不出什么好办法出来,你有没有什么好办法? --------------------编程问答--------------------
引用 7 楼 u010111184 的回复:
打算用穷举法么?

是啊,想不出什么好办法出来,你有没有什么好办法? --------------------编程问答--------------------
引用 9 楼 cockroachhz2012 的回复:
Quote: 引用 7 楼 u010111184 的回复:

打算用穷举法么?

是啊,想不出什么好办法出来,你有没有什么好办法?


穷举法是个不错的想法,我之前做过计算器,也用的穷举法。不知道效率怎么样。光是4!就有24种了。乘以3!然后还得乘以你所列举的表达式种类。 --------------------编程问答-------------------- 顶一个 --------------------编程问答--------------------
引用 10 楼 u010111184 的回复:
Quote: 引用 9 楼 cockroachhz2012 的回复:

Quote: 引用 7 楼 u010111184 的回复:

打算用穷举法么?

是啊,想不出什么好办法出来,你有没有什么好办法?


穷举法是个不错的想法,我之前做过计算器,也用的穷举法。不知道效率怎么样。光是4!就有24种了。乘以3!然后还得乘以你所列举的表达式种类。
效率应该比用StringBuffer和StringBuilder好,因为是用字符数组。 --------------------编程问答--------------------
引用 12 楼 cockroachhz2012 的回复:
Quote: 引用 10 楼 u010111184 的回复:

Quote: 引用 9 楼 cockroachhz2012 的回复:

Quote: 引用 7 楼 u010111184 的回复:

打算用穷举法么?

是啊,想不出什么好办法出来,你有没有什么好办法?


穷举法是个不错的想法,我之前做过计算器,也用的穷举法。不知道效率怎么样。光是4!就有24种了。乘以3!然后还得乘以你所列举的表达式种类。
效率应该比用StringBuffer和StringBuilder好,因为是用字符数组。


I/O不频繁的话,其实还是效率还是挺高的。等先做出来再优化吧。 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 这个算法依然有一些局限性:
1、对于(4+5)+7+8,和4+5+(7+8),等等,lz的程序会认为他们是不同的表达式,但这两个表达式的后缀表示形式其实是一样的。
2、全排列的算法似乎有待改进,搜一下“全排列”的帖子,主要有“广度思想”和“深度思想”两种方法。
3、插括号的方法很容易遗漏,不排除一些客户有“用3张、5张或其他任意张数的牌算24”的蛋疼想法;这时,就不能再去插括号了,最好的办法是用表达式二叉树来做。
4、之前一直想散“广义24点---客户想要算出实数M,他给你N张牌”的程序的分数,后来不小心给忘了。。。。看来得找个时间散下分,这样诸位再碰到“24点”之类的问题之后,就可以少走弯路。 --------------------编程问答--------------------
引用 16 楼 SmallYamateh 的回复:
这个算法依然有一些局限性:
1、对于(4+5)+7+8,和4+5+(7+8),等等,lz的程序会认为他们是不同的表达式,但这两个表达式的后缀表示形式其实是一样的。
2、全排列的算法似乎有待改进,搜一下“全排列”的帖子,主要有“广度思想”和“深度思想”两种方法。
3、插括号的方法很容易遗漏,不排除一些客户有“用3张、5张或其他任意张数的牌算24”的蛋疼想法;这时,就不能再去插括号了,最好的办法是用表达式二叉树来做。
4、之前一直想散“广义24点---客户想要算出实数M,他给你N张牌”的程序的分数,后来不小心给忘了。。。。看来得找个时间散下分,这样诸位再碰到“24点”之类的问题之后,就可以少走弯路。

对于第一个问题,我已经测试出来,但没有很好的解决办法,你想到了没有?第二个问题有待研究。对于第三、第四个问题,那是一部分人的想法,有这样想法的玩家估计不会玩这种小游戏了。 --------------------编程问答-------------------- 我都玩飞车和NBA玩了N年啦,还玩这种游戏,哈哈。
补充:Java ,  Java SE
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,