如何赢得这场比赛?

时间:2009-12-11 08:34:48

标签: algorithm game-theory

支持我们有一个n * m桌子,两个玩家玩这个游戏。他们排除细胞依次。玩家可以选择一个单元格(i,j)并排除从(i,j)到(n,m)的所有单元格,并排除最后一个单元格丢失游戏。

例如,在3 * 5板上,玩家1排除小区(3,3)到(3,5),玩家2排除(2,5)到(3,5),当前小组是像这样:( O表示不排除单元格,x表示排除单元格)

3 O O x x x
2 O O O O x
1 O O O O O
  1 2 3 4 5

并且在玩家1排除从(2,1)到(3,5)的单元格之后,该板变为

3 x x x x x
2 x x x x x
1 O O O O O
  1 2 3 4 5

现在玩家2排除了从(1,2)到(3,5)的单元格,只留下了(1,1)清洁:

3 x x x x x
2 x x x x x
1 O x x x x
  1 2 3 4 5

因此,玩家1必须排除唯一的(1,1)单元格,因为一个玩家必须在一个回合中排除至少一个单元格,并且他将失去游戏。

很明显,在n * n,1 * n和2 * n(n> = 2)的情况下,第一个获胜的人将获胜。

我的问题是,在所有情况下,是否有任何策略让玩家赢得比赛?他应该先上场吗?

P.S

我认为这与动态编程或分而治之等策略有关,但尚未形成一个想法。所以我在这里发布。

答案

感谢sdcwc's link。对于大于1 * 1的表,第一个玩家将获胜。证据如下:(从维基页面借来)

  

事实证明,对于任何矩形   起始位置大于1×1   第一名球员可以获胜。这可以   使用策略窃取显示   论点:假设第二个玩家   对任何人都有一个成功的策略   最初的第一名球员移动那么假设,   第一名选手只参加比赛   右下方。靠我们的   假设,第二位玩家有一个   对此的反应将迫使   胜利。但如果这样的胜利   响应存在,第一名球员可以   把它作为他的第一步和   因此被迫胜利。第二名球员   因此无法获胜   策略。

Zermelo's theorem确保了这种获胜策略的存在。

4 个答案:

答案 0 :(得分:8)

这款游戏被称为Chomp。第一个玩家获胜,看到他的战略链接(非建设性)。

答案 1 :(得分:1)

如果允许首先使用大于1x1的电路板,这将是一个Python程序(但对于大于10x10的电路板来说速度相当慢):

class State(object):
    """A state is a set of spaces that haven't yet been ruled out.
    Spaces are pairs of integers (x, y) where x and y >= 1."""

    # Only winnable states in this dictionary:
    _next_moves = {}
    # States where any play allows opponent to force a victory:
    _lose_states = set()

    def __init__(self, spaces):
        self._spaces = frozenset(spaces)

    @classmethod
    def create_board(cls, x, y):
        """Create a state with all spaces for the given board size."""
        return State([(r+1, c+1) for r in xrange(x) for c in xrange(y)])

    def __eq__(self, other):
        return self._spaces == other._spaces

    def __hash__(self):
        return hash(self._spaces)

    def play(self, move):
        """Returns a new state where the given move has been played."""
        if move not in self._spaces:
            raise ValueError('invalid move')
        new_spaces = set()
        for s in self._spaces:
            if s[0] < move[0] or s[1] < move[1]:
                new_spaces.add(s)
        return State(new_spaces)

    def winning_move(self):
        """If this state is winnable, return a move that guarantees victory."""
        if self.is_winnable() and not self.is_empty():
            return State._next_moves[self]
        return None

    def random_move(self):
        import random
        candidates = [m for m in self._spaces if m[0] > 1 and m[1] > 1]
        if candidates: return random.choice(candidates)
        candidates = [m for m in self._spaces if m[0] > 1 or m[1] > 1]
        if candidates: return random.choice(candidates)
        return (1,1)

    def minimal_move(self):
        """Return a move that removes as few pieces as possible."""
        return max(self._spaces, key=lambda s:len(self.play(s)._spaces))

    def is_winnable(self):
        """Return True if the current player can force a victory"""
        if not self._spaces or self in State._next_moves:
            return True
        if self in State._lose_states:
            return False

        # Try the moves that remove the most spaces from the board first
        plays = [(move, self.play(move)) for move in self._spaces]
        plays.sort(key=lambda play:len(play[1]._spaces))
        for move, result in plays:
            if not result.is_winnable():
                State._next_moves[self] = move
                return True
        # No moves can guarantee victory
        State._lose_states.add(self)
        return False

    def is_empty(self):
        return not self._spaces

    def draw_board(self, rows, cols):
        string = []
        for r in xrange(rows, 0, -1):
            row = ['.'] * cols
            for c in xrange(cols):
                if (r, c+1) in self._spaces:
                    row[c] = 'o'
            string.append(('%2d ' % r) + ' '.join(row))
        string.append('  ' + ''.join(('%2d' % c) for c in xrange(1, cols+1)))
        return '\n'.join(string)

    def __str__(self):
        if not self._spaces: return '.'
        rows = max(s[0] for s in self._spaces)
        cols = max(s[1] for s in self._spaces)
        return self.draw_board(rows, cols)

    def __repr__(self):
        return 'State(%r)' % sorted(self._spaces)

def run_game(x, y):
    turn = 1
    state = State.create_board(x, y)
    while True:
        if state.is_empty():
            print 'Player %s wins!' % turn
            return
        if state.is_winnable():
            move = state.winning_move()
        else:
            move = state.random_move()
        state = state.play(move)
        print 'Player %s plays %s:' % (turn, move)
        print state.draw_board(x, y)
        print
        turn = 3 - turn

def challenge_computer(x, y):
    state = State.create_board(x, y)
    print "Your turn:"
    print state.draw_board(x, y)
    while True:
        # Get valid user input
        while True:
            try:
                move = input('Enter move: ')
                if not (isinstance(move, tuple) and len(move) == 2):
                    raise SyntaxError
                state = state.play(move)
                break
            except SyntaxError, NameError:
                print 'Enter a pair of integers, for example: 1, 1'
            except ValueError:
                print 'Invalid move!'
            except (EOFError, KeyboardInterrupt):
                return
        print state.draw_board(x, y)
        if state.is_empty():
            print 'Computer wins!'
            return
        if state.is_winnable():
            move = state.winning_move()
        else:
            move = state.minimal_move()
        state = state.play(move)
        print
        print 'Computer plays %s:' % (move,)
        print state.draw_board(x, y)
        print
        if state.is_empty():
            print 'You win!'
            return

if __name__ == '__main__':
    challenge_computer(8, 9)

样本的输出运行:

$ python -c 'import game; game.run_game(8, 9)'
Player 1 plays (6, 7):
 8 o o o o o o . . .
 7 o o o o o o . . .
 6 o o o o o o . . .
 5 o o o o o o o o o
 4 o o o o o o o o o
 3 o o o o o o o o o
 2 o o o o o o o o o
 1 o o o o o o o o o
   1 2 3 4 5 6 7 8 9

Player 2 plays (4, 8):
 8 o o o o o o . . .
 7 o o o o o o . . .
 6 o o o o o o . . .
 5 o o o o o o o . .
 4 o o o o o o o . .
 3 o o o o o o o o o
 2 o o o o o o o o o
 1 o o o o o o o o o
   1 2 3 4 5 6 7 8 9

Player 1 plays (5, 1):
 8 . . . . . . . . .
 7 . . . . . . . . .
 6 . . . . . . . . .
 5 . . . . . . . . .
 4 o o o o o o o . .
 3 o o o o o o o o o
 2 o o o o o o o o o
 1 o o o o o o o o o
   1 2 3 4 5 6 7 8 9

Player 2 plays (3, 7):
 8 . . . . . . . . .
 7 . . . . . . . . .
 6 . . . . . . . . .
 5 . . . . . . . . .
 4 o o o o o o . . .
 3 o o o o o o . . .
 2 o o o o o o o o o
 1 o o o o o o o o o
   1 2 3 4 5 6 7 8 9

Player 1 plays (4, 1):
 8 . . . . . . . . .
 7 . . . . . . . . .
 6 . . . . . . . . .
 5 . . . . . . . . .
 4 . . . . . . . . .
 3 o o o o o o . . .
 2 o o o o o o o o o
 1 o o o o o o o o o
   1 2 3 4 5 6 7 8 9

Player 2 plays (2, 3):
 8 . . . . . . . . .
 7 . . . . . . . . .
 6 . . . . . . . . .
 5 . . . . . . . . .
 4 . . . . . . . . .
 3 o o . . . . . . .
 2 o o . . . . . . .
 1 o o o o o o o o o
   1 2 3 4 5 6 7 8 9

Player 1 plays (1, 5):
 8 . . . . . . . . .
 7 . . . . . . . . .
 6 . . . . . . . . .
 5 . . . . . . . . .
 4 . . . . . . . . .
 3 o o . . . . . . .
 2 o o . . . . . . .
 1 o o o o . . . . .
   1 2 3 4 5 6 7 8 9

Player 2 plays (2, 2):
 8 . . . . . . . . .
 7 . . . . . . . . .
 6 . . . . . . . . .
 5 . . . . . . . . .
 4 . . . . . . . . .
 3 o . . . . . . . .
 2 o . . . . . . . .
 1 o o o o . . . . .
   1 2 3 4 5 6 7 8 9

Player 1 plays (1, 4):
 8 . . . . . . . . .
 7 . . . . . . . . .
 6 . . . . . . . . .
 5 . . . . . . . . .
 4 . . . . . . . . .
 3 o . . . . . . . .
 2 o . . . . . . . .
 1 o o o . . . . . .
   1 2 3 4 5 6 7 8 9

Player 2 plays (2, 1):
 8 . . . . . . . . .
 7 . . . . . . . . .
 6 . . . . . . . . .
 5 . . . . . . . . .
 4 . . . . . . . . .
 3 . . . . . . . . .
 2 . . . . . . . . .
 1 o o o . . . . . .
   1 2 3 4 5 6 7 8 9

Player 1 plays (1, 2):
 8 . . . . . . . . .
 7 . . . . . . . . .
 6 . . . . . . . . .
 5 . . . . . . . . .
 4 . . . . . . . . .
 3 . . . . . . . . .
 2 . . . . . . . . .
 1 o . . . . . . . .
   1 2 3 4 5 6 7 8 9

Player 2 plays (1, 1):
 8 . . . . . . . . .
 7 . . . . . . . . .
 6 . . . . . . . . .
 5 . . . . . . . . .
 4 . . . . . . . . .
 3 . . . . . . . . .
 2 . . . . . . . . .
 1 . . . . . . . . .
   1 2 3 4 5 6 7 8 9

Player 1 wins!

答案 2 :(得分:0)

让人想起的事情是:如果董事会是2x2,那么第一个玩家就输了:事实上,从这个董事会开始:

O O 
O O

有两种变体(a和b):

A.1)

1 1
O O

a.2)第一名球员输了

1 1
O 2

或,b.1)

1 O
O O

b.2)第一名球员输了

1 2
O 2
在这一点上,策略归结为迫使董事会成为2x2平方;然后,进入该董事会的第一个将失去它。这将引导您进入策略的第二步,主要是:

如何确保您不是进入此类配置的人?

或,

有多少配置可以让我从更大的配置开始获得这样的配置?例如,从3x3板开始:

O O O
O O O
O O O

有几种策略,取决于谁首先开始,有多少是无效的;我建议,在这一点上,使用遗传算法探索最佳解决方案(这很有趣!相信我):)

答案 3 :(得分:0)

这类似于经常玩火柴的游戏(不记得名字)

无论如何,我认为这取决于获胜的董事会的形状。对于第二个玩家而言,2 * 2通常是输掉,而2 * N通过将棋盘减少到2 * 2并迫使其他玩家进行游戏而轻微地输掉。我认为所有方板都是第二名球员获胜,而矩形是第一名球员获胜,但尚未证明它

编辑:

好的我认为这是一个方板p1总是选择2,2然后平衡行和列 确保p2失去

与sdcwc的评论一样,rectangluar董事会也是第一个赢得比赛的球员。只有堕落板1 * 1才是第二名球员获胜