井字棋盘看起来像一个大的井字符号(#),有 9 个空格,可以包含 X、O 或空要用字典表示棋盘,可以为每个空格分配一个字符串键,如图 5-2 所示。
可以用字符串值来表示,棋盘上每个空格有什么:'X'、'O'或' '(空格字符)。因此,需要存储 9
个字符串。可以用一个字典来做这事。带有键'top-R'的字符串表示右上角,带有键'low-L'的字符串表示左下角,带有键'mid-M'的字符串表示中间,以此类推。
这个字典就是表示井字棋盘的数据结构。将这个字典表示的棋盘保存在名为 theBoard 的变量中。打开一个文件编辑器窗口, 输入以下代码, 并保存为 ticTacToe.py:
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
保存在 theBoard 变量中的数据结构,表示了图 5-3 中的井字棋盘。
因为 theBoard 变量中每个键的值都是单个空格字符,所以这个字典表示一个完全干净的棋盘。如果玩家 X 选择了中间的空格,就可以用下面这个字典来表示棋盘:
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': 'X', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
theBoard 变量中的数据结构现在表示图 5-4 中的井字棋盘。
一个玩家O 获胜的棋盘上,他将 O 横贯棋盘的顶部,看起来像这样:
theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O',
'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': 'X'}
theBoard 变量中的数据结构现在表示图 5-5 中的井字棋盘。
当然,玩家只看到打印在屏幕上的内容,而不是变量的内容。让我们创建一个
函数,将棋盘字典打印到屏幕上。将下面代码添加到 ticTacToe.p(y
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
def printBoard(board):
新代码是黑体的):
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) print('-+-+-')
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R']) printBoard(theBoard)
运行这个程序时,printBoard()将打印出空白井字棋盘。
| |
-+-+-
| |
-+-+-
| |
printBoard()函数可以处理传入的任何井字棋数据结构。尝试将代码改成以下的样子:
theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O', 'mid-L': 'X', 'mid-M':
'X', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': 'X'}
def printBoard(board):
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) print('-+-+-')
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R']) printBoard(theBoard)
现在运行该程序,新棋盘将打印在屏幕上。
O|O|O
-+-+-
X|X|
-+-+-
| |X
因为你创建了一个数据结构来表示井字棋盘,编写了
printBoard()中的代码来解释该数据结构,所以就有了一个程序,对井字棋盘进行了“建模”。也可以用不同的方式组织数据结构(例如,使用'TOP-LEFT'这样的键来代替'top-L'),但只要代码
能处理你的数据结构,就有了正确工作的程序。
例如,printBoard()函数预期井字棋数据结构是一个字典,包含所有 9 个空格的键。假如传入的字典缺少'mid-L'键,程序就不能工作了。
O|O|O
-+-+-
Traceback (most recent call last):
File "ticTacToe.py", line 10, in <module> printBoard(theBoard)
File "ticTacToe.py", line 6, in printBoard
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) KeyError: 'mid-L'
现在让我们添加代码,允许玩家输入他们的着法。修改 ticTacToe.py 程序如下所示:
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 'mid-L': ' ', 'mid-M': '
', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
def printBoard(board):
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) print('-+-+-')
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])
turn = 'X'
for i in range(9):
➊ printBoard(theBoard)
print('Turn for ' + turn + '. Move on which space?')
➋ move = input()
➌ theBoard[move] = turn
➍ if turn == 'X': turn = 'O'
else:
turn = 'X'
printBoard(theBoard)
新的代码在每一步新的着法之前,打印出棋盘➊,获取当前棋手的着法➋,相应地更新棋盘➌,然后改变当前棋手➍,
进入到下一着。
运行该程序,它看起来像这样:
| |
-+-+-
| |
-+-+-
| |
Turn for X. Move on which space?
mid-M
| |
-+-+-
|X|
-+-+-
| |
Turn for O. Move on which space?
low-L
| |
-+-+-
|X|
-+-+-
O| |
--snip-- O|O|X
-+-+-
X|X|O
-+-+-
O| |X
Turn for X. Move on which space?
low-M
O|O|X
-+-+-
X|X|O
-+-+-
O|X|X
这不是一个完整的井字棋游戏(例如,它并不检查玩家是否获胜),但这已足够展示如何在程序中使用数据结构。
注意 如果你很好奇, 完整的井字棋程序的源代码在网上有介绍, 网址是
http://nostarch.com/automatestuff/。