尼姆游戏(人机对战)-Python

1.实验要求

编写程序设计和实现聪明的尼姆游戏(人机对战)。尼姆游戏是个著名的游戏,有很多变种玩法。两个玩家轮流从一堆物品中拿走一部分。在每一步中,玩家可以自由选择拿走多少物品,但是必须至少拿走一个并且最多只能拿走一半物品,然后轮到下一个玩家。拿走最后一个物品的玩家输掉游戏。

在聪明模式中,计算机每次拿走足够多的物品使得堆的大小是2 的幂次方减1——也就是3,7,15,31 或63。除了堆的大小已经是2 的幂次方减1,在其他情况下这样走都是符合游戏规则的。如果已是2的幂次方减1,计算机就按游戏规则随机拿走一些。

2.实验原理

2.1玩家回合

要确保玩家拿取的数要大于等于1,并且小于等于当前物品数的一半,可以使用while循环来完成。不过如果当前物品数为1时,会进入死循环,所以需要专门考虑:使用if语句来进行判断。拿取后,把更新的物品数返回即可。

其中用到的函数有player_turn。

def player_turn(heap_number):
    print(f'当前堆的大小为{heap_number},', end='')
    take_number = 0
    while take_number < 1 or take_number > heap_number // 2:
        take_number = eval(input('请输入你要拿取的数量(至少为1,至多为当前堆大小的一半):'))
        if heap_number == 1 and take_number == 1:  # 边界问题
            break
    return heap_number - take_number

2.2电脑回合

根据传入的参数,来判断当前的模式。

如果是傻瓜模式,就随机拿取[1,当前堆的大小的一半],不过要注意这个区间有可能是非法的,当堆大小为1时,当前堆大小的一半为0,所以非法区间为[1,0]。

如果是聪明模式,就先找到比此时物品数小的最大2的幂次方减1。如果这个2的幂次方减1恰好等于此时的物品数,就进行随机拿取;如果不等于,就拿取***此时物品数-2的幂次方减1***。

记得最后更新一下堆大小(返回当前的物品数)

其中用到的函数有computer_turn。

def computer_turn(heap_number, game_mode):
    print(f'堆的大小为{heap_number},', end='')
    if game_mode == 1: # 傻瓜模式:随机拿取
        if heap_number > 1:
            take_number = random.randint(1, heap_number // 2) # 如果heap_number <= 1,那么随机区间非法
        else:
            take_number = 1
        print(f'对手拿取了{take_number}个物品,还剩下{heap_number - take_number}个物品。')
        return heap_number - take_number
    else:
        x = 0
        while True:  # 找到此时比物品数小的 最大的2的幂次方减1
            if pow(2, x) <= heap_number:
                x = x + 1
            else:
                break
        x = x - 1
        if pow(2, x) == heap_number:  # 恰好等于物品数
            if heap_number > 1: # 如果heap_number <= 1,那么随机区间非法
                take_number = random.randint(1, heap_number // 2)
            else:
                take_number = 1
            print(f'对手拿取了{take_number}个物品,还剩下{heap_number - take_number}个物品。')
            return heap_number - take_number
        else:
            take_number = heap_number - (pow(2, x) - 1)
            print(f'对手拿取了{take_number}个物品,还剩下{heap_number - take_number}个物品。')
            return heap_number - take_number

2.3实现交替拿取

仅需定义一个变量now_play,用它来表示当前拿取的是谁,0代表电脑,1代表玩家,然后每有人拿取后,把now_play更新为表示对手的值就行了,这样就可以进行轮流进行拿取物品。

其中用到的函数有change_play。

def change_play(now_play):  # 改变玩家回合
    if now_play == 0:
        return 1
    return 0

2.4判断游戏结束

仅需判断当前物品数是否为0即可,一个玩家拿取后,如果此时物品数为0,那么游戏就可以结束了,这个玩家输了。

用到的函数有is_over。

def is_over(heap_number):  # 判断游戏是否结束
    if heap_number == 0:
        return True
    else:
        return False

3.全部代码

import random


def game():
    heap_number = eval(input('请输入堆的初始大小:'))
    game_mode = eval(input("请选择游戏模式:1.傻瓜模式 2.聪明模式"))
    now_play = eval(input('请输入谁先进行拿取:1.自己 0.对手'))  # 此时轮到谁进行游戏
    while True:
        if now_play == 0:
            heap_number = computer_turn(heap_number, game_mode)
            now_play = change_play(now_play)
            if is_over(heap_number):
                print('恭喜你,赢得了比赛!')
                break
        else:
            heap_number = player_turn(heap_number)
            now_play = change_play(now_play)
            if is_over(heap_number):
                print('很可惜呢,希望下一次能胜利!')
                break


def player_turn(heap_number):
    print(f'当前堆的大小为{heap_number},', end='')
    take_number = 0
    while take_number < 1 or take_number > heap_number // 2:
        take_number = eval(input('请输入你要拿取的数量(至少为1,至多为当前堆大小的一半):'))
        if heap_number == 1 and take_number == 1:  # 边界问题
            break
    return heap_number - take_number


def computer_turn(heap_number, game_mode):
    print(f'堆的大小为{heap_number},', end='')
    if game_mode == 1: # 傻瓜模式:随机拿取
        if heap_number > 1: 
            take_number = random.randint(1, heap_number // 2) # 如果heap_number <= 1,那么随机区间非法
        else:
            take_number = 1
        print(f'对手拿取了{take_number}个物品,还剩下{heap_number - take_number}个物品。')
        return heap_number - take_number
    else:
        x = 0
        while True:  # 找到此时比物品数小的 最大的2的幂次方减1
            if pow(2, x) <= heap_number:
                x = x + 1
            else:
                break
        x = x - 1
        if pow(2, x) == heap_number:
            if heap_number > 1: # 如果heap_number <= 1,那么随机区间非法
                take_number = random.randint(1, heap_number // 2)
            else:
                take_number = 1
            print(f'对手拿取了{take_number}个物品,还剩下{heap_number - take_number}个物品。')
            return heap_number - take_number
        else:
            take_number = heap_number - (pow(2, x) - 1)
            print(f'对手拿取了{take_number}个物品,还剩下{heap_number - take_number}个物品。')
            return heap_number - take_number


def change_play(now_play):  # 改变玩家回合
    if now_play == 0:
        return 1
    return 0


def is_over(heap_number):  # 判断游戏是否结束
    if heap_number == 0:
        return True
    else:
        return False


if __name__ == '__main__':
    game()

4.运行结果

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值