这是一个 Python 程序,如果允许先行,它将在大于 1x1 的板上获胜(但对于大于 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!