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

cockroachhz2012 2013-06-25 08:04:09
加精
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;
}
}
...全文
9356 36 打赏 收藏 转发到动态 举报
写回复
用AI写文章
36 条回复
切换为时间正序
请发表友善的回复…
发表回复
weakling2016 2016-03-07
  • 打赏
  • 举报
回复
路过
jsjdtb356 2016-03-02
  • 打赏
  • 举报
回复
好厉害的样子
qq_33248024 2016-02-29
  • 打赏
  • 举报
回复
好厉害的样子
  • 打赏
  • 举报
回复
这是属于机器算法,不是平均效率最高的。
7记 2016-02-28
  • 打赏
  • 举报
回复
好东东, 学习,学习 支持,支持
walkuere 2016-02-26
  • 打赏
  • 举报
回复
我问个问题,如果N2与N4先操作呢,或者N1与N4先操作呢?
BitCoffee 2016-02-25
  • 打赏
  • 举报
回复
cj956686286 2016-02-25
  • 打赏
  • 举报
回复
顶一个,最怕算法,智商碾压
line_us 2016-02-25
  • 打赏
  • 举报
回复
看看程序实现代码
李海华 2016-02-19
  • 打赏
  • 举报
回复
我也用java写了个24点游戏,目前在群 207224939 里可以玩,做为qq机器人的插件,大家可去体验。有好的想法一起开发
超级大笨狼 2016-02-19
  • 打赏
  • 举报
回复
namespace Calc24 { using System; using System.Collections.Generic; using System.Data; using System.Numerics; /// <summary> /// TODO: Update summary. /// </summary> public class Input { public Input(int[] arr, bool IsBreak) { for (int i = 0; i < arr.Length; i++) { input.Add(new Num(arr[i], 1 << i)); } isBreak = IsBreak; } private bool isBreak = false; private List<Num> input = new List<Num>(); private List<string> result = new List<string>(); private bool ok = false; public List<string> Output() { Calc(input[0]); if (result.Count == 0) { result.Add("NA"); } return result; } private void Calc(Num A) { if (A.Omit == (1 << input.Count) - 1) { if (A.Equals(24)) { A.Expression = A.Expression.Substring(1, A.Expression.Length - 2); result.Add(A.Expression); if (isBreak) ok = true; } } else { for (int j = 0; j < input.Count; j++) { if ((A.Omit & (1 << j)) == 0) { Num B = input[j]; if (ok) return; Calc(B + A); if (ok) return; Calc(B - A); if (ok) return; Calc(A - B); if (ok) return; Calc(B * A); if (ok) return; Calc(B / A); if (ok) return; Calc(A / B); } } } } public class Num { public Num(int n, int omit) { if (n < 0) { Expression = "(" + n.ToString() + ")"; } else { Expression = n.ToString(); } Numerator = new BigInteger(n); Denominator = BigInteger.One; Omit = omit; } public Num() { } public BigInteger Numerator; public BigInteger Denominator; public string Expression; public int Omit; public static Num operator +(Num A, Num B) { return new Num() { Numerator = BigInteger.Add(BigInteger.Multiply(A.Numerator, B.Denominator), BigInteger.Multiply(A.Denominator, B.Numerator)), Denominator = BigInteger.Multiply(A.Denominator, B.Denominator), Expression = string.Format("({0}+{1})", A.Expression, B.Expression), Omit = A.Omit | B.Omit }; } public static Num operator -(Num A, Num B) { return new Num() { Numerator = BigInteger.Subtract(BigInteger.Multiply(A.Numerator, B.Denominator), BigInteger.Multiply(A.Denominator, B.Numerator)), Denominator = BigInteger.Multiply(A.Denominator, B.Denominator), Expression = string.Format("({0}-{1})", A.Expression, B.Expression), Omit = A.Omit | B.Omit }; } public static Num operator *(Num A, Num B) { return new Num() { Numerator = BigInteger.Multiply(A.Numerator, B.Numerator), Denominator = BigInteger.Multiply(A.Denominator, B.Denominator), Omit = A.Omit | B.Omit, Expression = string.Format("({0}*{1})", A.Expression, B.Expression), }; } public static Num operator /(Num A, Num B) { return new Num() { Numerator = BigInteger.Multiply(A.Numerator, B.Denominator), Denominator = BigInteger.Multiply(A.Denominator, B.Numerator), Omit = A.Omit | B.Omit, Expression = string.Format("({0}/{1})", A.Expression, B.Expression), }; } public bool Equals(int x) { if (this.Denominator.IsZero) { return false; } else { BigInteger re = BigInteger.Divide(this.Numerator, this.Denominator); return (this.Numerator % this.Denominator).IsZero && re.Equals(24); } } } } }
IT--小生 2015-12-28
  • 打赏
  • 举报
回复
在做24,点的数据结构的课程设计,参考了一下楼主的,,很好,希望遇到问题时楼主能帮忙和我讨论下,谢谢咯!
大数据小白 2015-09-21
  • 打赏
  • 举报
回复
楼主,搞得不错,我自己跑跑玩玩。
DY1201 2015-09-21
  • 打赏
  • 举报
回复
引用 16 楼 SmallYamateh 的回复:
这个算法依然有一些局限性: 1、对于(4+5)+7+8,和4+5+(7+8),等等,lz的程序会认为他们是不同的表达式,但这两个表达式的后缀表示形式其实是一样的。 2、全排列的算法似乎有待改进,搜一下“全排列”的帖子,主要有“广度思想”和“深度思想”两种方法。 3、插括号的方法很容易遗漏,不排除一些客户有“用3张、5张或其他任意张数的牌算24”的蛋疼想法;这时,就不能再去插括号了,最好的办法是用表达式二叉树来做。 4、之前一直想散“广义24点---客户想要算出实数M,他给你N张牌”的程序的分数,后来不小心给忘了。。。。看来得找个时间散下分,这样诸位再碰到“24点”之类的问题之后,就可以少走弯路。
求扩散24点源码
lifeiplus 2015-09-18
  • 打赏
  • 举报
回复
我最近偶然看到了这个题目,觉得挺有意思。想了一想,觉得可以采用如下的解法。 1.任意两个不相等的有序数字可以有六种计算方式:加、减、乘、除、逆减、逆除。若两个数字相等则可以有四种计算方式:加、减、乘、除。 2.从四个数中任取两个数字,运算得一个新的数字。从三个数中任取两个数字,运算得一个新的数字。两数运算得结果。 3.从四个数中任取两个数字,运算得一个新的数字。剩余两数运算得一个新的数字。两数运算得结果。
kosora曹 2013-06-30
  • 打赏
  • 举报
回复
这个算法依然有一些局限性: 1、对于(4+5)+7+8,和4+5+(7+8),等等,lz的程序会认为他们是不同的表达式,但这两个表达式的后缀表示形式其实是一样的。 2、全排列的算法似乎有待改进,搜一下“全排列”的帖子,主要有“广度思想”和“深度思想”两种方法。 3、插括号的方法很容易遗漏,不排除一些客户有“用3张、5张或其他任意张数的牌算24”的蛋疼想法;这时,就不能再去插括号了,最好的办法是用表达式二叉树来做。 4、之前一直想散“广义24点---客户想要算出实数M,他给你N张牌”的程序的分数,后来不小心给忘了。。。。看来得找个时间散下分,这样诸位再碰到“24点”之类的问题之后,就可以少走弯路。
cockroachhz2012 2013-06-30
  • 打赏
  • 举报
回复
我都玩飞车和NBA玩了N年啦,还玩这种游戏,哈哈。
cockroachhz2012 2013-06-30
  • 打赏
  • 举报
回复
引用 16 楼 SmallYamateh 的回复:
这个算法依然有一些局限性: 1、对于(4+5)+7+8,和4+5+(7+8),等等,lz的程序会认为他们是不同的表达式,但这两个表达式的后缀表示形式其实是一样的。 2、全排列的算法似乎有待改进,搜一下“全排列”的帖子,主要有“广度思想”和“深度思想”两种方法。 3、插括号的方法很容易遗漏,不排除一些客户有“用3张、5张或其他任意张数的牌算24”的蛋疼想法;这时,就不能再去插括号了,最好的办法是用表达式二叉树来做。 4、之前一直想散“广义24点---客户想要算出实数M,他给你N张牌”的程序的分数,后来不小心给忘了。。。。看来得找个时间散下分,这样诸位再碰到“24点”之类的问题之后,就可以少走弯路。
对于第一个问题,我已经测试出来,但没有很好的解决办法,你想到了没有?第二个问题有待研究。对于第三、第四个问题,那是一部分人的想法,有这样想法的玩家估计不会玩这种小游戏了。
逍遥jc 2013-06-28
  • 打赏
  • 举报
回复
引用 12 楼 cockroachhz2012 的回复:
[quote=引用 10 楼 u010111184 的回复:] [quote=引用 9 楼 cockroachhz2012 的回复:] [quote=引用 7 楼 u010111184 的回复:] 打算用穷举法么?
是啊,想不出什么好办法出来,你有没有什么好办法?[/quote] 穷举法是个不错的想法,我之前做过计算器,也用的穷举法。不知道效率怎么样。光是4!就有24种了。乘以3!然后还得乘以你所列举的表达式种类。[/quote]效率应该比用StringBuffer和StringBuilder好,因为是用字符数组。[/quote] I/O不频繁的话,其实还是效率还是挺高的。等先做出来再优化吧。
cockroachhz2012 2013-06-27
  • 打赏
  • 举报
回复
引用 10 楼 u010111184 的回复:
[quote=引用 9 楼 cockroachhz2012 的回复:] [quote=引用 7 楼 u010111184 的回复:] 打算用穷举法么?
是啊,想不出什么好办法出来,你有没有什么好办法?[/quote] 穷举法是个不错的想法,我之前做过计算器,也用的穷举法。不知道效率怎么样。光是4!就有24种了。乘以3!然后还得乘以你所列举的表达式种类。[/quote]效率应该比用StringBuffer和StringBuilder好,因为是用字符数组。
加载更多回复(10)

62,610

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧