偷懒玩游戏 | Smash The Code

这是一个玩游戏发现脑子不够用然后悄咪咪给自己写个“外挂”的故事_(:з」∠)_

About the game

Name:Smash The Code
Producer:Piotr Sochalewski
Do you like the classic MastermindTM game?Then you’ll love the brand new Smash the Code.
You have ten tries to guess a secret 4-digit number. After each try you receive a hint to see how many digits you have guessed correctly.
This game has the best features from classic board game and paper-and-pencil Bulls and cows.That means you can play Smash the Code on your own or against a friend sitting next to you.
Features:
·play single or multiplayer in hot-seat mode or simultaneously on iPads
·supports all devices
·lets you have fun with Game Center:unlock achievements and gain high scores

- copy from AppStore.

Rules of the game

  • 游戏目标是用最少的次数解出一个各位不重复的四位数字密码。
  • 每个黑色的点是指猜对了密码中的一个数,且在正确的位置。
  • 每个白色的点是指猜对了密码中的一个数,但在错误的位置。

Basic solution

导入需要用到的库

1
2
3
import pandas as pd
import numpy as np
import random

创建可能的Code

要求是四位不重复的数字,每位可取的范围是1~9。

1
2
3
4
5
6
7
num = []
for a in range(1,10):
for b in range(1,10):
for c in range(1,10):
for d in range(1,10):
if a!=b and a!=c and a!=d and b!=c and b!=d and c!=d:
num.append(a*1000+b*100+c*10+d)
1
num.__len__()
3024

一共有3024(9×8×7×6)种不同组合的答案。

创建一个空表

建立一个“答案表”,行索引表示尝试猜测的数字,列索引表示正确答案。用于存放游戏中所有可能的结果。

1
df = pd.DataFrame(columns=num,index=num)

填充表格内容

思路是用1表示黑点,用0表示白点。对每一个内容格,先循环遍历行和列数字的各个位,判断“猜”中了几个对的数(即:是正确的数但不在正确的位置),有几个就append几个0。然后判断行和列的数字有几个是对应相等的(即:是正确的数且在正确的位置),有几个就将前多少位置1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for i in num:                             #填写df的内容
for j in num:
stri = '%d'%i #数字转字符串
strj = '%d'%j
ans = []
ap = 0
for k in range(4): #判断有几个正确的数
for p in range(4):
if(strj[k]==stri[p]):
ans.append(0)
for q in range(4): #判断有几个数在正确的位置
if(strj[q]==stri[q]):
ans[ap]=1
ap+=1
stra = ''.join('%s' %id for id in ans) #数组转字符串
df.loc[i,j] = stra

代码运行

第一次先运行一遍以下代码(默认第一轮都输入1234):

1
2
3
thetry1 = num
thetry2 = []
mytry = 1234

将得到的“提示”(即几个黑点几个白点)赋值给theAns。输出的第一个数是经过筛选后还剩下可能的答案数(理论最大值是3024),第二个数是从剩下可能的答案中 随机 选取的一个数,作为下一轮猜测的数值。

1
2
3
4
5
6
7
8
9
theAns = '10'
for i in thetry1:
if dff.loc[mytry,i]==theAns:
thetry2.append(i)
mytry = random.choice(thetry2)
print(thetry2.__len__())
print(mytry)
thetry1 = thetry2
thetry2 = []
180
3247

结果分析

平均解题轮次为4.9轮。(自己纯人工玩的时候猜中大概需要6~8轮)

这是记录每次猜测后剩余可能答案数量的表格,✅的是猜中的轮次。可以看到由于下一轮猜测的数是随机取的,所以会出现第②次、第⑨次这样波动比较大的结果(初始范围大但猜中所用轮次少/初始范围小但猜中所用轮次较多)。

第1轮 第2轮 第3轮 第4轮 第5轮 第6轮
180 5 1
840 206 38
720 218 52 6 1
480 80 12 2
720 62 19 1
480 80 22 1
480 119 6 1
720 122 3
120 24 6 2 1
840 174 26 2

Improved solution

并没有눈_눈

本来想写个改进的方法,看看猜数字的时候有没有办法可以更准一点。可以看到答案表中一共有14种情况。
然而…

1111 1100 1000 0000 111 110 100 000 11 10 00 1 0 NaN
1234 1 6 8 9 20 60 180 220 120 480 840 240 720 120
9876 1 6 8 9 20 60 180 220 120 480 840 240 720 120

每个数的情况是一样的눈_눈
做了筛选之后剩下的数情况也是一样的(●—●)
所以就是只能随机猜_(´ཀ`」 ∠)_
over.