一个数学游戏
2009-02-08 12:53
218 查看
一个古老的数学游戏,很早就知道了,但一直编程没编正确。今天终于正确了。
有1、2、3、4、5、6这6种牌,每种牌有3张。开始总点和记为24,然后两个人轮流选一张牌,然后从总点和里减去牌面值,直到总点和非正。谁取完牌后,总点和为0,则胜;若为负,则败。
例如:
24
A:3
21
B:6
15
A:1
14
B:2
12
A:5
7
B:3
4
A:4
A胜
问先取是否有必胜策略,或后取是否有?
下面是一段人和电脑玩的代码
下面是加强的版本,表征胜败不是用1,0简单表达,而是用了-1.0~1.0之间的实数,符号表示胜败,绝对值大小表示胜或败的强度,这样可以让电脑选择败强度低的局面(复杂,人能获胜的支路相对整个博弈树来要小,需要仔细选择),这样人可能容易出错。
另外在选择方案时,加入了一点随机性,以免每次博弈都是同样选择顺序(当,然胜败应该相同)。
有1、2、3、4、5、6这6种牌,每种牌有3张。开始总点和记为24,然后两个人轮流选一张牌,然后从总点和里减去牌面值,直到总点和非正。谁取完牌后,总点和为0,则胜;若为负,则败。
例如:
24
A:3
21
B:6
15
A:1
14
B:2
12
A:5
7
B:3
4
A:4
A胜
问先取是否有必胜策略,或后取是否有?
下面是一段人和电脑玩的代码
#include <cstdio> #include <cstdlib> #include <ctime> const int N = 6; const int initV = 3; int A ; int maxDepth=0; int lastChoice= -1; void status() { printf("Remains : "); for (int i=0;i<N;++i) printf("%3d %2d | ", i+1, A[i]); printf("/n"); } // play 返回和为 sum 时的最佳策略和最大概率 double play(int sum, int depth) { if (depth>maxDepth) { maxDepth = depth; //status(); } if (sum==0) return 0.0; if (sum<0) return 1.0; double minHope=1; for (int i=0;i<N;++i) if (A[i]>0) { --A[i]; double hope = play(sum-1-i, depth+1); ++A[i]; if (hope<minHope) { minHope = hope; if (depth==0) lastChoice = i; if (minHope< 1E-6) break; } } return 1-minHope; } int main() { srand((unsigned)time(0)); for (int i=0;i<N;++i) A[i] = initV; int humFirst = 0; printf("Hum first(0,1) ? "); scanf("%d", &humFirst); humFirst %= 2; int sum = 24; while(sum>0) { printf("sum=%d /n", sum); int c=0; if (humFirst>0) { status(); while(!(c>=1 && c<=N && A[c-1]>0)) { if (c>=1 && c<=N) printf("%d leaves nothing!/n", c); printf("choose (1..%d) : ", N); scanf("%d", &c); } --c; } else { play(sum, 0); c = lastChoice; while(!(c>=0 && c<N && A[c]>0)) { c = rand()%N; } printf("Computer choose %d/n", c+1); } sum -= (c+1); --A[c]; humFirst = (humFirst+1)%2; } if ((sum==0) ^ (humFirst==0)) printf("YOU LOSE!/n"); else printf("YOU WIN!/n"); return 0; }
下面是加强的版本,表征胜败不是用1,0简单表达,而是用了-1.0~1.0之间的实数,符号表示胜败,绝对值大小表示胜或败的强度,这样可以让电脑选择败强度低的局面(复杂,人能获胜的支路相对整个博弈树来要小,需要仔细选择),这样人可能容易出错。
另外在选择方案时,加入了一点随机性,以免每次博弈都是同样选择顺序(当,然胜败应该相同)。
#include <cstdio> #include <cstdlib> #include <ctime> #include <cmath> const int N = 6; const int initV = 3; int A ; int maxDepth=0; int lastChoice= -1; void status() { printf("Remains : "); for (int i=0;i<N;++i) printf("%3d %2d | ", i+1, A[i]); printf("/n"); } // play 返回和为 sum 时的最佳策略和最大概率 double play(int sum, int depth) { if (depth>maxDepth) { maxDepth = depth; //status(); } if (sum==0) return -1.0; if (sum<0) return 1.0; const double T = 0.001; double minHope=1.0; double negHope=0.0; double posHope=0.0; for (int i=0;i<N;++i) if (A[i]>0) { --A[i]; double hope = play(sum-1-i, depth+1); ++A[i]; if (hope>0) posHope += hope; else negHope += hope; if (depth==0) { //printf("%d hope = %lf/n", i+1, hope); double chance=0.0; if (minHope>0 && hope<0) { minHope = hope; lastChoice = i; } else if (hope-minHope<-0.01 || ( fabs(hope-minHope)<0.01 && (chance=static_cast<double>(rand()%1024)/1024) > 0.6) ) { minHope = hope; lastChoice = i; } //printf("minhope %lf dhope %lf chance %lf/n", minHope, hope-minHope,chance); } } if (negHope < 0.0 && posHope> 0.0) { return -negHope/(posHope-negHope+1E-6); } else{ return -posHope/(posHope-negHope+1E-6); } } int main(int argc, char* argv[]) { srand((unsigned)time(0)); for(int i=rand()%13 + 3;i>0; --i) rand(); for (int i=0;i<N;++i) A[i] = initV; int humFirst = 0; printf("Hum first(0,1) ? "); scanf("%d", &humFirst); humFirst %= 2; int sum = 24; if (argc==2) { sscanf(argv[1],"%d", &sum); } while(sum>0) { printf("sum=%d /n", sum); int c=0; if (humFirst>0) { status(); while(!(c>=1 && c<=N && A[c-1]>0)) { if (c>=1 && c<=N) printf("%d leaves nothing!/n", c); printf("choose (1..%d) : ", N); scanf("%d", &c); } --c; } else { play(sum, 0); c = lastChoice; while(!(c>=0 && c<N && A[c]>0)) { c = rand()%N; } printf("Computer choose %d/n", c+1); } sum -= (c+1); --A[c]; humFirst = (humFirst+1)%2; } if ((sum==0) ^ (humFirst==0)) printf("YOU LOSE!/n"); else printf("YOU WIN!/n"); return 0; }
相关文章推荐
- 尽管是一个CS专业的学生,小B的数学基础很好并对数值计算有着特别的兴趣,喜欢用计算机程序来解决数学问题。现在,她正在玩一个数值变换的游戏。她发现计算机中经常用不同的进制表示同一个数,如十进制数123表达为16进制时只包含两位数7、11(B),用八进制表示时为三位数1、7、3。按不同进制表达时,各个位数的和也不同,如上述例子中十六进制和八进制中各位数的和分别是18和11。
- 日本著名数学游戏专家中村义作教授提出这样一个问题:将2520个桔子分给六个儿子
- 日本著名数学游戏专家中村义作教授提出这样一个问题:将2520个桔子分给六个儿子
- 使用pygame制作一个简单的游戏
- 一个扑克牌网络游戏的例子(C++)
- 【MFC三天一个游戏】之 局域网黑白棋
- 探讨下一步的网游(三)一个玩弄朋友关系的游戏《朋友战争》
- 从硬件的底层开始,构建一个FlappyBird游戏
- Python(17)python使用tkinter实现一个简单的CSGO幸运转盘抽奖游戏
- 机械迷城中的一个游戏—— C#代码
- [入门教程]使用Cocos2d-html5游戏引擎编写一个简单的游戏 第一回合: 搭建开发环境
- 一个最简单GAL游戏资源文件黑盒分析(一)
- 分享自己写的一个贪吃蛇的游戏(Linux)
- 写了一个C#的俄罗斯方块的游戏,包含人工智能部分,源码可下载
- 看了GAL游戏资源文件黑盒分析,想起以前做的一个提取器,发来大家共享一下
- java 做一个能在局域网内对战的游戏
- 自定义View实践-一个简单的棋类游戏
- 如何用JavaScript做一个贪吃蛇游戏,贪吃蛇系列_02自动走
- Unity游戏开发的数学与物理 2 ( 通过键盘控制物体的运动 )
- 5、使用Libgdx设计一个简单的游戏------雨滴