关于三堆小球的 python 实现

关于三堆小球的 python 实现

今天看到一个比较有意思的思维题:

        游戏规则: 有三堆小球, 数量分别为3个, 5个, 7个, 如下:

        。。。

        。。。。。

        。。。。。。

                两人轮流从三堆小球里面的其中一堆拿走小球, 拿走的数量不限, 但至少拿走一个。

        胜负条件: 拿走所有球里面最后一个的玩家为输家

        要求, 请通过算法计算出如何拿小球才能保证胜率最高。


我是这样思考的: 

    既然取球可分胜负, 则一定存在某种形式的球堆, 无程序可破, 先取球者必输
    那么程序设计思路:
    step 1: 找出必败的序列
    step 2: 分析当前球堆, 找出离之最近的必败形式
    step 3: 设法将最近必败形式留给对手

    step 4: 取球的同时避免对手将必败序列留给自己

就通过封装类,实现了取球过程,希望能与您交流。



import random


class Balls(object):
    '''球堆类
    优化取球方法,尽可能取胜

    既然取球可分胜负, 则一定存在某种形式的球堆, 无程序可破, 先取球者必输
    那么程序设计思路:
    step 1: 找出必败的序列
    step 2: 分析当前球堆, 找出离之最近的必败形式
    step 3: 设法将最近必败形式留给对手
    step 4: 取球的同时避免对手将必败序列留给自己

    实现细节:
        全局变量 BAD_BALLS 记录所有的必败形式
        make_bad_balls() 寻找所有所有必败形式

    '''

    def __new__(cls, *args, **kwargs):
        if len(args) - 3:
            print('Orignal Arguments not enough!')
        elif args[0] > 3 or args[1] > 5 or args[2] > 7:
            print('({},{},{}) out of range(3, 5, 7)'.format(*args))
        else:
            return super().__new__(cls)

    def __init__(self, *args):
        self.pile_one = args[0]
        self.pile_two = args[1]
        self.pile_three = args[2]
        self.lst = list(args)
        self.set = sorted(self.lst)
        self.num = sum(args)

    def __repr__(self):
        return "Balls({},{},{})".format(
            self.pile_one, self.pile_two, self.pile_three)

    def __sub__(self, balls):
        return self.num - balls.num

    def __eq__(self, ball):
        if self.set == ball.set:
            return True
        else:
            return False

    def take_ball_once(self, ball):
        """返回取球一次后所有剩余的可能"""
        odds_lst = []
        for index in range(len(ball.set)):
            for num in range(1, ball.set[index] + 1):
                lst = list(ball.set)
                lst[index] -= num
                if not sum(lst):
                    # 如果是只有一列, 则有可能一下子取完, 这种形式是无效的
                    break
                lst.sort()
                new_balls = Balls(*lst)
                odds_lst.append(new_balls)
        # print("in take_ball_once! odds_lst:", ball, odds_lst)
        return odds_lst

    def judge(self, ball):
        """如果取球一次后所有的可能都存在至少一种可能
        一步可以达到坏球, 则这个形式也是坏球
        """
        odds_lst = self.take_ball_once(ball)
        n_odds = len(odds_lst)
        # state 标记 对应的 odd 是否可取, True表示可取
        odds_state = [True] * n_odds
        for index in range(n_odds):
            # 对于每一种剩余的形式, 再取一次, 只要存在一种可能与坏球相等, 则这种剩余不可取
            odd_odds_lst = self.take_ball_once(odds_lst[index])
            for odd_odd in odd_odds_lst:
                for bad_ball in BAD_BALLS:
                    # if odd_odd.num <= bad_ball.num:
                    #     break
                    if odd_odd == bad_ball:
                        # 某种剩余的剩余与坏球形式一样,则这种剩余不可取
                        odds_state[index] = False
                        # print(ball, odds_lst[index], odd_odd,
                        #       bad_ball, odd_odd == bad_ball)
                        break
                if not odds_state[index]:
                    break
        # 如果所有的剩余都不可取, 则这种形式是坏球
        return sum(odds_state)

    def split(self, n_balls):
        factors = []
        for n_three in range(1, min(n_balls, 7) + 1):
            for n_two in range(min([n_three, 5, n_balls - n_three]) + 1):
                n_one = n_balls - n_two - n_three
                if n_one <= 3 and n_two >= n_one:
                    factors.append(Balls(n_one, n_two, n_three))
        return factors

    def make_bad_balls(self):
        for n_balls in range(2, MAX_BALL_NUM + 1):
            factors = self.split(n_balls)
            for fac in factors:
                if not self.judge(fac):
                    # print('fac', fac)
                    BAD_BALLS.append(fac)
        print('make bad_balls OK!')

    def take_balls(self):
        """如果遇到坏球, 自己尽可能制造对手错误的机会"""
        factors = self.take_ball_once(self)
        for fac in factors:
            for bad_ball in BAD_BALLS:
                if fac == bad_ball:
                    return fac
        else:
            print("Bad luck! {} is a type of bad_ball! You'll loss!".format(self))
            if self.num == 1:
                return None
            print("You may try to make some chance that could to mislead "
                  "your Opponent!".format(self))
            index = BAD_BALLS.index(self)
            next_bad_ball = BAD_BALLS[index - 1]
            for fac in factors:
                if fac - next_bad_ball >= 2:
                    return fac
                else:
                    index -= 1
                    if index:
                        next_bad_ball = BAD_BALLS[index]
            return factors[0]


# bad_balls = [Balls(0, 0, 1), Balls(1, 1, 1), Balls(0, 2, 2),
#              Balls(1, 2, 3), Balls(0, 3, 3), Balls(0, 4, 4),
#              Balls(1, 4, 5), Balls(0, 5, 5), Balls(2, 4, 6),
#              Balls(3, 5, 6), Balls(3, 4, 7), Balls(2, 5, 7)]

MAX_BALL_NUM = 15
orign = Balls(0, 0, 1)
BAD_BALLS = [orign]

balls = Balls(3, 5, 7)
balls.make_bad_balls()
print(BAD_BALLS)
print('-----------------')

print(balls)
while balls:
    balls = balls.take_balls()
    print('take your balls: ', balls)

        测试结果: 

             Balls(3,5,7)
             take your balls:  Balls(2,5,7)
             Bad luck! Balls(2,5,7) is a type of bad_ball! You'll loss!
             You may try to make some chance that could to mislead your Opponent!
             take your balls:  Balls(2,5,6)
             take your balls:  Balls(2,4,6)
             Bad luck! Balls(2,4,6) is a type of bad_ball! You'll loss!
             You may try to make some chance that could to mislead your Opponent!
             take your balls:  Balls(2,2,6)
             take your balls:  Balls(0,2,2)
             Bad luck! Balls(0,2,2) is a type of bad_ball! You'll loss!
             You may try to make some chance that could to mislead your Opponent!
             take your balls:  Balls(0,1,2)
             take your balls:  Balls(0,0,1)
             Bad luck! Balls(0,0,1) is a type of bad_ball! You'll loss!

             take your balls:  None

最后,版权声明:

本blog是笔者根据在网络上看到的思维题编写的python代码, 谨用于关于python的学习经验交流,无作他用。如需转载请注明出处。


猜你喜欢

转载自blog.csdn.net/Mengrui_1991/article/details/80375378