猜数字游戏Bulls and Cows的解法

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

猜数字(又称 Bulls and Cows )是一种古老的的密码破译类益智类小游戏,起源于20世纪中期,一般由两个人或多人玩,也可以由一个人和电脑玩。查了网上很多关于这个游戏的帖子,大多都是计算机出题,人来猜。根据百度百科的介绍,有非常多种方法可以实现七步之内猜出答案。下面我就介绍其中最简单的一种也是我唯一会的一种方法。


一、游戏规则

首先需要说明游戏规则,因为这个游戏网上还有很多变种。就像麻将一样,玩的人多了,就玩出花来了。

  1. 出题人写一个四位数的密码,每一位数都可以从0到9选一个,但四位数字不能有重复的。如4415这个密码就是不行的。0可以放到开头,如0123这样也是可以的。当然这个密码不能给猜题人看到,这是答案,猜题人要猜的。
  2. 猜题人写一个自己猜测的密码,格式同上。写完后展示给出题人观看。
  3. 出题人根据猜题人的数字回答几A几B,其中A的个数表示猜题人猜测的数字位置正确的数的个数,B表示数字正确而位置不对的数的个数。例如出题人写下的密码是1564,猜题人写下的是7514,这时出题人就要告诉猜题人2A1B。
  4. 猜题人根据出题人提供的提示继续猜一个密码,出题人后续也要给出相应提示。
  5. 直到猜题人猜中出题人写下的数字,游戏结束。统计一下猜题人猜了多少次,猜的次数越少越厉害。

规则到此结束,建议刚接触这个游戏的人去体验一下,游玩链接点这里。毕竟玩才是学习的最佳方式。

二、电脑出题你来猜

1.引入库

代码如下:

import random

2.生成随机四位数密码

代码如下:

li = [0,1,2,3,4,5,6,7,8,9]
random.shuffle(li)
secret = li[:4]

3.接收玩家猜测数字

代码如下:

guess = []#接收列表
while True:#确保玩家输入的数字格式正确
    test = input('输入猜测的四位数:\n')
    if str.isdigit(test) == 1 and len(test) == 4:
        for i in test:
            guess.append(i)
        break
    else:
        print('输入的格式不正确,请重新输入')  

str.isdigit(test) == 1来判断输入的是否为数字,len(test) == 4来判断是否为4位数。

4.生成A和B的个数

代码如下:

A = 0
B = 0
for a,b in enumerate(guess):
    if secret[a] == int(b):
        A += 1
    elif int(b) in secret:
        B += 1
    else:
        pass
print(f'{A}A{B}B')
if A == 4 and B == 0:
    print('猜对了')

5.全部代码

代码如下:

li = [0,1,2,3,4,5,6,7,8,9]
random.shuffle(li)
secret = li[:4]
for item in range(0,10):
    guess = []
    while True:
        test = input('输入猜测的四位数:\n')
        if str.isdigit(test) == 1 and len(test) == 4:
            for i in test:
                guess.append(i)
            break
        else:
            print('输入的格式不正确,请重新输入')           
    A = 0
    B = 0
    for a,b in enumerate(guess):
        if secret[a] == int(b):
            A += 1
        elif int(b) in secret:
            B += 1
        else:
            pass
    print(f'{A}A{B}B,还剩{9-item}次机会')
    if A == 4 and B == 0:
        print('猜对了')
        break
else:
    print(f'游戏结束,答案是{secret}')

加入了for循环,给了10次机会,如果觉得太少可以加。

三、你来出题电脑猜

1.代码思路

百度百科给出了很多高端解法,如下:
在这里插入图片描述
数学不学好的我流下痛苦的眼泪,我都看不懂。不过我们还有一种最简单易懂的解决方式。具体思路如下:

  • 0到9不重复的四位数组合方式有1098*7=5040种,对计算机来说,区区五千个四位数很好遍历。我们生成一个装有这5040个密码的列表。
  • 先从列表中按顺序取一个四位数密码。这时会得到几A几B的提示。
  • 这是最关键的一步,非常关键!!通过第二步给出的提示,将列表中不可能正确的密码移除。
  • 从修改过的列表中继续遍历四位数密码,重复二、三步,直到得出猜出正确答案结束运行。

2.筛选步骤

那么如何尽可能多的移除列表中不可能正确的密码,就是接下来需要做的。

  • 如果是0A0B,则说明这四个数字都不可能出现在正确答案中,那么可以将备选列表中5040个四位数密码中含有这四个数字的都移除。
  • 如果是0A几B,则说明这四个数字中虽然存在正确的数字,但四个数字的位置没一个对的,那么可以将列表中含有上述位置的密码移除。比如给的是1234,结果是0A1B,那么将列表中第一个数字是1的密码,第二个数字是2的密码,第三个数字是3的密码,第四个数字是4的密码,都移除。如1489、4537、0298、7814这些数字都移除。
  • 如果A>0。则说明这四个数字中有数字位置正确,那么可以将列表中相同位数小于A的密码去除。比如给的1234,结果是2A0B,那么就可以将列表中与1234相同位数小于2的去除,如1452、7814这些数字都移除。
  • 如果A+B>0。则说明这四个数字中有A+B个数字存在与正确密码中,那么去除列表内与猜测数字包含正确个数位数不符的密码。比如给的1234,结果提示1A1B,说明1234里面只有两个数字是在正确密码中的,那么1789、4567这些含有1、2、3、4的个数小于2的可以移除,1235、3541这些含有1、2、3、4的个数大于2的也可以移除。
  • 如果A=4。则说明这个密码是正确的,输出正确答案。

3.全部代码

while True:#接收猜测数字
    secret = []
    pre_secret = input('输入一个没有重复数字的四位数:\n')
    if str.isdigit(pre_secret) == 1 and len(pre_secret) == 4 and len(set(pre_secret)) == len(pre_secret):
        for i in pre_secret:
            secret.append(i)
        break
    else:
        print('输入的格式不正确,请重新输入')
guessli = []
li = [0,1,2,3,4,5,6,7,8,9]
for i in li:#生成所有可能答案,装填入备选列表
    for u in li:
        if u == i:
            continue
        for o in li:
            if u == o or o == i:
                continue
            for p in li:
                if p == o or p == u or p == i:
                    continue
                else:
                    four = p
                    guessli.append([i,u,o,p])
total = 0#初始化计数变量
while True:
    for guess in guessli:
        total += 1#猜测次数加一
        A = 0
        B = 0
        command = 0#初始化完成退出信号
        time.sleep(1)
        print(f'电脑猜:{guess}')
        guessli.remove(guess)
        for a,b in enumerate(guess):#计算几A几B
            if secret[a] == str(b):
                A += 1
            elif str(b) in secret:
                B += 1
        if A == 0 and B == 0 :#当A和B均为零时,去除备选列表内相同数字的选项
            guesslis = guessli.copy()
            for item in guesslis:
                if guess[0] in item or guess[1] in item or guess[2] in item or guess[3] in item:
                    guessli.remove(item)
        elif A == 0:#当A为零B不为零时,去除备选列表内对应位数数字相同的选项
            guesslia = guessli.copy()
            for item in guesslia:
                if item[0] == guess[0] :
                    guessli.remove(item)
                elif item[1] == guess[1]:
                    guessli.remove(item)
                elif item[2] == guess[2]:
                    guessli.remove(item)
                elif item[3] == guess[3]:
                    guessli.remove(item)
        if A + B > 0:#当A和B均不为零时,去除备选列表内与猜测数字包含正确个数位数不符的选项
            guesslib = guessli.copy()
            for item in guesslib:
                count = 0
                if guess[0] in item :
                    count += 1
                if guess[1] in item:
                    count += 1
                if guess[2] in item:
                    count += 1
                if guess[3] in item:
                    count += 1
                if count < A + B or count > A + B:
                    guessli.remove(item)
        if A > 0:#当A大于零的时候,去除相同位数小于A的选项
            guesslie = guessli.copy()
            for item in guesslie:
                count = 0
                if guess[0] == item[0]:
                    count += 1
                if guess[1] == item[1]:
                    count += 1
                if guess[2] == item[2] :
                    count += 1
                if guess[3] == item[3]:
                    count += 1
                if count < A:
                    guessli.remove(item)
        if A == 4:
            print(f'{A}A{B}B,总共猜了{total}次')
            command = 1
            break
        else:
            print(f'{A}A{B}B')
    if command == 1:
        break

4.手动输入AB值

上面是输入一个密码,可以给你自动生成AB值给电脑进行判断。当然我们也可以手动输入AB值,做到更公平,防止电脑偷看答案。代码如下:

li = [0,1,2,3,4,5,6,7,8,9]
guessli = []
for i in li:#生成所有可能答案,装填入备选列表
    for u in li:
        if u == i:
            continue
        for o in li:
            if u == o or o == i:
                continue
            for p in li:
                if p == o or p == u or p == i:
                    continue
                else:
                    four = p
                    guessli.append([i,u,o,p])
total = 0#初始化计数变量
print('想好一个数,电脑要开始猜了')
time.sleep(1)
prepare = input('按任意键开始游戏')
while True:
    for guess in guessli:
        total += 1#猜测次数加一
        command = 0#初始化完成退出信号
        time.sleep(1)
        print(f'电脑猜:{guess}')
        guessli.remove(guess)
        #print(guessli)
        while True:
            A = input('请告诉电脑四个数中存在且位置正确的有几个?')
            B = input('请告诉电脑四个数中存在但位置错误的有几个?')
            if str.isdigit(A) == 1 and len(A) == 1 and str.isdigit(B) == 1 and len(B) == 1:
                if int(A)+int(B) <= 4 :
                    A = int(A)
                    B = int(B)
                    break
                else:
                    print('你这个数字没开玩笑吧?')
            else:
                print('请输入正确的数字。')
        if A == 0 and B == 0 :#当A和B均为零时,去除备选列表内相同数字的选项
            guesslis = guessli.copy()
            for item in guesslis:
                if guess[0] in item or guess[1] in item or guess[2] in item or guess[3] in item:
                    guessli.remove(item)
        elif A == 0:#当A为零B不为零时,去除备选列表内对应位数数字相同的选项
            guesslia = guessli.copy()
            for item in guesslia:
                if item[0] == guess[0] :
                    guessli.remove(item)
                elif item[1] == guess[1]:
                    guessli.remove(item)
                elif item[2] == guess[2]:
                    guessli.remove(item)
                elif item[3] == guess[3]:
                    guessli.remove(item)
        if A + B > 0:#当A和B均不为零时,去除备选列表内与猜测数字包含正确个数位数不符的选项
            guesslib = guessli.copy()
            for item in guesslib:
                count = 0
                if guess[0] in item :
                    count += 1
                if guess[1] in item:
                    count += 1
                if guess[2] in item:
                    count += 1
                if guess[3] in item:
                    count += 1
                if count < A + B or count > A + B:
                    guessli.remove(item)
        if A > 0:#当A大于零的时候,去除相同位数小于A的选项
            guesslie = guessli.copy()
            for item in guesslie:
                count = 0
                if guess[0] == item[0]:
                    count += 1
                if guess[1] == item[1]:
                    count += 1
                if guess[2] == item[2] :
                    count += 1
                if guess[3] == item[3]:
                    count += 1
                if count < A:
                    guessli.remove(item)
        if A == 4:
            print(f'{A}A{B}B,总共猜了{total}次')
            command = 1
            break
        else:
            print(f'{A}A{B}B')
            if guessli == []:
                print('这不可能,请检查一下你写的A和B是否正确')
                command = 1
    if command == 1:
        break

总结

在游戏规则介绍里有这个游戏的网页版,如果被虐了,可以运用上面代码和电脑进行pk。可以保证七次内必出结果。噢,对了,链接的游戏不包含0,可以在生成列表时将0去掉。

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值