您的位置:首页 > 其它

一个数学游戏

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胜

问先取是否有必胜策略,或后取是否有?

下面是一段人和电脑玩的代码

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐