(封面图片来源于网络侵权删)
关于灵感
小学时不知道同学从哪里带回来的游戏,规则是这样的:
两个玩家首先比出两个1的手势,情况是这样的:
a玩家:左手:1,右手:1
b玩家:左手:1,右手:1
然后决定先后顺序,依次在每个自己的回合做出一下行动:使用自己的左手或右手碰一下对方的左手或右手,然后将碰到的两只手所表示的数值相加,自己碰的那只手就变为该数值。如:
原局面:
a玩家:左手:1,右手:1
b玩家:左手:1,右手:1
a玩家使用左手碰了对方的右手后:
a玩家:左手:1+1=2,右手:1
b玩家:左手:1,右手:1
到b玩家时,b玩家可以用左手碰对方的右手:
a玩家:左手:2,右手:1
b玩家:左手:3,右手:1
谁先将两只手都到达目标数字(一般是10)就赢了
我在一次某讲座上和同学玩这个游戏时突然想到,这个游戏有没有必胜的策略?遂作此程序。(不知道这算不算博弈论了反正先当标题党写上吧)
思路
那当然是!dfs直接打爆力求解!
这部分没什么好说的,不会的可以去我的专栏或B站
于是我们得到以下代码:
#include "bits/stdc++.h"
#include <conio.h>
#include <windows.h>
using namespace std;
bool handMarker[20][20][20][20];
int endNumber, aLeftHand, aRightHand, bLeftHand, bRightHand;
int aMayWin = 0, bMayWin = 0;
void color(int m) {
HANDLE consolehend;
consolehend = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(consolehend, m);
}
void dfs(int aLeftHand, int aRightHand, int bLeftHand, int bRightHand, int step) {
// 成功后输出
if ((aLeftHand == endNumber && aRightHand == endNumber) || (bLeftHand == endNumber && bRightHand == endNumber)) {
if ((step - 1) % 2 == 0) {
aMayWin++;
} else {
bMayWin++;
}
// color(0x02);
for (int i = 1; i <= step - 1; i++) {
printf(u8"│ ");
}
printf(u8"├─");
printf(u8"%c:Win!:)\n", (step - 1) % 2 + 'a');
for (int i = 1; i <= step - 1; i++) {
printf(u8"│ ");
}
printf(u8"└─");
printf(u8"Used Step: %d.\n", step);
// color(0x0F);
return ;
}
handMarker[aLeftHand][aRightHand][bLeftHand][bRightHand] = 1;
// 输出
if (step >= 1) {
for (int i = 1; i <= step - 1; i++) {
printf(u8"│ ");
}
printf(u8"├─");
printf(u8"%c:My turn!\n", (step + 1) % 2 + 'a');
for (int i = 1; i <= step - 1; i++) {
printf(u8"│ ");
}
printf(u8"├─");
printf(u8"back:a:%d %d b:%d %d\n", aLeftHand, aRightHand, bLeftHand, bRightHand);
}
int newHand;
if (step % 2 == 0) {
if (aLeftHand != endNumber) {
if (bLeftHand != endNumber) {
newHand = (aLeftHand + bLeftHand) % (endNumber + 1);
if (newHand == 0) {
newHand = 1;
}
if (!handMarker[newHand][aRightHand][bLeftHand][bRightHand]) {
dfs(newHand, aRightHand, bLeftHand, bRightHand, step + 1);
}
}
if (bRightHand != endNumber) {
newHand = (aLeftHand + bRightHand) % (endNumber + 1);
if (newHand == 0) {
newHand = 1;
}
if (!handMarker[newHand][aRightHand][bLeftHand][bRightHand]) {
dfs(newHand, aRightHand, bLeftHand, bRightHand, step + 1);
}
}
}
if (aRightHand != endNumber) {
if (bLeftHand != endNumber) {
newHand = (aRightHand + bLeftHand) % (endNumber + 1);
if (newHand == 0) {
newHand = 1;
}
if (!handMarker[aLeftHand][newHand][bLeftHand][bRightHand]) {
dfs(aLeftHand, newHand, bLeftHand, bRightHand, step + 1);
}
}
if (bRightHand != endNumber) {
newHand = (aRightHand + bRightHand) % (endNumber + 1);
if (newHand == 0) {
newHand = 1;
}
if (!handMarker[aLeftHand][newHand][bLeftHand][bRightHand]) {
dfs(aLeftHand, newHand, bLeftHand, bRightHand, step + 1);
}
}
}
}
if (step % 2 == 1) {
if (bLeftHand != endNumber) {
if (aLeftHand != endNumber) {
newHand = (bLeftHand + aLeftHand) % (endNumber + 1);
if (newHand == 0) {
newHand = 1;
}
if (!handMarker[aLeftHand][aRightHand][newHand][bRightHand]) {
dfs(aLeftHand, aRightHand, newHand, bRightHand, step + 1);
}
}
if (aRightHand != endNumber) {
newHand = (bLeftHand + aRightHand) % (endNumber + 1);
if (newHand == 0) {
newHand = 1;
}
if (!handMarker[aLeftHand][aRightHand][newHand][bRightHand]) {
dfs(aLeftHand, aRightHand, newHand, bRightHand, step + 1);
}
}
}
if (bRightHand != endNumber) {
if (aLeftHand != endNumber) {
newHand = (bRightHand + aLeftHand) % (endNumber + 1);
if (newHand == 0) {
newHand = 1;
}
if (!handMarker[aLeftHand][aRightHand][bLeftHand][newHand]) {
dfs(aLeftHand, aRightHand, bLeftHand, newHand, step + 1);
}
}
if (aRightHand != endNumber) {
newHand = (bRightHand + aRightHand) % (endNumber + 1);
if (newHand == 0) {
newHand = 1;
}
if (!handMarker[aLeftHand][aRightHand][bLeftHand][newHand]) {
dfs(aLeftHand, aRightHand, bLeftHand, newHand, step + 1);
}
}
}
}
}
int main() {
printf("Change to which number will win?");
scanf("%d", &endNumber); // 输入
printf("Beginning number?");
scanf("%d%d%d%d", &aLeftHand, &aRightHand, &bLeftHand, &bRightHand);
freopen("out.txt", "w", stdout);
printf(u8"back:a:%d %d b:%d %d\n", aLeftHand, aRightHand, bLeftHand, bRightHand);
dfs(aLeftHand, aRightHand, bLeftHand, bRightHand, 0);
printf(u8"Total Win: a:%d b:%d\n", aMayWin, bMayWin);
printf(u8"The possibility of a's winning: %.2f%%\n", 1.0 * aMayWin / (aMayWin + bMayWin) * 100);
printf(u8"The possibility of b's winning: %.2f%%\n", 1.0 * bMayWin / (aMayWin + bMayWin) * 100);
return 0;
}
(做了一点输出优化)
如果你想要在控制台看的话就把main()里的freopen注释掉再把dfs里的color函数取消注释。
结果
很神奇
似乎是一个区间一个区间来的
首先,如果目标数是2的话,a玩家完胜:
Total Win: a:4 b:0
The possibility of a's winning: 100.00%
The possibility of b's winning: 0.00%
目标数在3到7的区间里(含3和7)的话,b玩家的胜率先是一下提高,然后缓降:
3:
Total Win: a:8 b:11
The possibility of a's winning: 42.11%
The possibility of b's winning: 57.89%
4:
Total Win: a:21 b:22
The possibility of a's winning: 48.84%
The possibility of b's winning: 51.16%
5:
Total Win: a:32 b:32
The possibility of a's winning: 50.00%
The possibility of b's winning: 50.00%
6:
Total Win: a:48 b:42
The possibility of a's winning: 53.33%
The possibility of b's winning: 46.67%
7:
Total Win: a:86 b:67
The possibility of a's winning: 56.21%
The possibility of b's winning: 43.79%
随后在8到11的区间内又缓升:
8:
Total Win: a:107 b:98
The possibility of a's winning: 52.20%
The possibility of b's winning: 47.80%
9:
Total Win: a:140 b:136
The possibility of a's winning: 50.72%
The possibility of b's winning: 49.28%
10:
Total Win: a:163 b:169
The possibility of a's winning: 49.10%
The possibility of b's winning: 50.90%
11:
Total Win: a:197 b:211
The possibility of a's winning: 48.28%
The possibility of b's winning: 51.72%
而后又降:
12:
Total Win: a:244 b:248
The possibility of a's winning: 49.59%
The possibility of b's winning: 50.41%
(后面没算了有人跑出来的话发一下评论区谢谢了)
(我猜会不会到后面双方胜率无限接近于50%)
(所以这个游戏最好去重玩,保证不会出现重复局面)
总结
我真无聊,写这个破玩意写到现在,明天还要月考(啊我靠1点了???