大爽Python入门教程 6-9 实例演示三 控制台——井字棋游戏(Tic-Tac-Toe)
大爽Python入门公开课教案 点击查看教程总目录
1 游戏介绍
实现一个控制台版本的井字棋小游戏, 英文名叫Tic-Tac-Toe。
代码量:100行左右。
面板展示效果
| |
---+---+---
| |
---+---+---
| |
两种棋子,
一种用"X"
来表示,
另一种用"O"
表示。
流程说明
觉得流程描述罗嗦,可以直接看运行效果部分。
- 进入游戏,展示欢迎语,
- 展示井字棋面板,
提醒当前玩家输入落子位置。 - 检查输入,是否符合位置格式,且对应位置是否为空,
不满足则展示原因,并再次读取输入。
满足则落子成功。 - 落子成功后,检查是否胜利(有三个字连成一条线),
未胜利则切换到另一个玩家,
然后重复上述步骤123 - 落子成功后,如果胜利,则直接展示面板,恭喜当前玩家,然后结束游戏。
运行效果
运行示例中的输入为
1, 1
1,0
0, 0
1,1
123
2,2
0,2
0,1
2,0
运行的效果为
Welcome to Tic-Tac-Toe!
| |
---+---+---
| |
---+---+---
| |
X's turn. Enter row index and column index to place (ri, ci):
1, 1
| |
---+---+---
| X |
---+---+---
| |
O's turn. Enter row index and column index to place (ri, ci):
1,0
| |
---+---+---
O | X |
---+---+---
| |
X's turn. Enter row index and column index to place (ri, ci):
0, 0
X | |
---+---+---
O | X |
---+---+---
| |
O's turn. Enter row index and column index to place (ri, ci):
1,1
The place is already occupied.
X | |
---+---+---
O | X |
---+---+---
| |
O's turn. Enter row index and column index to place (ri, ci):
123
Invalid command.
X | |
---+---+---
O | X |
---+---+---
| |
O's turn. Enter row index and column index to place (ri, ci):
2,2
X | |
---+---+---
O | X |
---+---+---
| | O
X's turn. Enter row index and column index to place (ri, ci):
0,2
X | | X
---+---+---
O | X |
---+---+---
| | O
O's turn. Enter row index and column index to place (ri, ci):
0,1
X | O | X
---+---+---
O | X |
---+---+---
| | O
X's turn. Enter row index and column index to place (ri, ci):
2,0
X | O | X
---+---+---
O | X |
---+---+---
X | | O
X win!
2 初步分析
大概步骤,在流程说明中,其实就分析的差不多了。
所以可以直接思考如何设计函数。
同时本章第四节部分已经实现了井字棋的胜利的检测。
这里可以直接拿来用(简单修改下更好)。
函数初步设计
需要微调的函数
check_bunch_match(bunch, symbol)
: 检查一串棋子bunch
是否全都都是symbol
对应的棋get_ttt_winner(board):
: 得出谁是赢家
对应的,需要以下几个函数
generate_board
: 生成井字格棋盘(3x3二维列表)show_board
: 展示井字格棋盘(以可读性高的形式)parse_rc
: 从用户输入中解析出行列位置坐标。main
: 主函数或者说主逻辑
分析与调整
check_bunch_match(bunch, symbol)
不用调整
微调
get_ttt_winner(board)
调整成check_win(board, symbol)
, 检查symbol
对应的棋子是否胜利。
因为每次落子后只需要检查当前棋子是否能够练成一条线。
其他新的函数需要哪些参数
generate_board()
: 无需参数show_board(board)
:board
为井字格棋盘(3x3二维列表)parse_rc(cmd)
:cmd
为用户输入的落子坐标(字符串)。main()
: 无需参数
3 逐步实现
字符串定义
先在代码开头定义要用的的字符串。
WELCOME = "Welcome to Tic-Tac-Toe!"
ENTER = "%s's turn. Enter row index and column index to place (ri, ci):\n"
Invalid = "Invalid command."
Used = "The place is already occupied."
函数声明
把要用到的函数先声明
def generate_board():
return
def show_board(board):
pass
def parse_rc(cmd):
return -1, -1
def check_bunch_match(bunch, symbol):
return False
def check_win(board, symbol):
return False
def main():
pass
这里先实现generate_board
,然后再去实先main
generate_board
代码如下
def generate_board():
board = [
[" " for ci in range(3)] for ri in range(3)
]
return board
main
代码如下
def main():
board = generate_board()
print(WELCOME)
turn = "X"
while True:
show_board(board)
cmd = input(ENTER % turn)
ri, ci = parse_rc(cmd) # 解析出命令中的行列位置
if ri >= 0: # 检查输入是否有效
if board[ri][ci] == " ": # 检查是否是空格子
board[ri][ci] = turn # 直接放入棋子
# 检查是否胜利,胜利则展示面板与结果并退出
if check_win(board, turn):
show_board(board)
print("%s win!" % turn)
return
# 切换玩家
if turn == "X":
turn = "O"
else:
turn = "X"
else:
print(Used)
else:
print(Invalid)
show_board
代码如下
def show_board(board):
for ri, row in enumerate(board):
row_str = ""
for ci, cell in enumerate(row):
row_str += " %s " % cell
if ci != 2:
row_str += "|"
print(row_str)
if ri != 2:
print("---+---+---")
parse_rc
代码如下
def parse_rc(cmd):
rc = cmd.split(",")
if len(rc) == 2:
ri, ci = rc
ri = ri.strip()
ci = ci.strip()
if ci.isdigit() and ri.isdigit():
ci = int(ci)
ri = int(ri)
if 0 <= ci < 3 and 0 <= ri < 3:
return ri, ci
return -1, -1
check_bunch_match
check_bunch_match
直接复制过来就行
代码如下
def check_bunch_match(bunch, symbol):
res = True
for one in bunch:
if one != symbol:
res = False
break
return res
check_win
微调后代码如下
def check_win(board, symbol):
r, c = len(board), len(board[0])
for ri in range(r):
row = board[ri]
if check_bunch_match(row, symbol):
return True
for ci in range(c):
column = [board[ri][ci] for ri in range(r)]
if check_bunch_match(column, symbol):
return True
diagonal_1 = [board[ri][ri] for ri in range(r)]
diagonal_2 = [board[ri][c-ri-1] for ri in range(r)]
if check_bunch_match(diagonal_1, symbol):
return True
if check_bunch_match(diagonal_2, symbol):
return True
return False
调用代码
main()
4 最终代码
最终代码如下(运行效果第一部分已展示,不再额外展示)。
WELCOME = "Welcome to Tic-Tac-Toe!"
ENTER = "%s's turn. Enter row index and column index to place (ri, ci):\n"
Invalid = "Invalid command."
Used = "The place is already occupied."
def generate_board():
board = [
[" " for ci in range(3)] for ri in range(3)
]
return board
def show_board(board):
for ri, row in enumerate(board):
row_str = ""
for ci, cell in enumerate(row):
row_str += " %s " % cell
if ci != 2:
row_str += "|"
print(row_str)
if ri != 2:
print("---+---+---")
def parse_rc(cmd):
rc = cmd.split(",")
if len(rc) == 2:
ri, ci = rc
ri = ri.strip()
ci = ci.strip()
if ci.isdigit() and ri.isdigit():
ci = int(ci)
ri = int(ri)
if 0 <= ci < 3 and 0 <= ri < 3:
return ri, ci
return -1, -1
def check_bunch_match(bunch, symbol):
res = True
for one in bunch:
if one != symbol:
res = False
break
return res
def check_win(board, symbol):
r, c = len(board), len(board[0])
for ri in range(r):
row = board[ri]
if check_bunch_match(row, symbol):
return True
for ci in range(c):
column = [board[ri][ci] for ri in range(r)]
if check_bunch_match(column, symbol):
return True
diagonal_1 = [board[ri][ri] for ri in range(r)]
diagonal_2 = [board[ri][c-ri-1] for ri in range(r)]
if check_bunch_match(diagonal_1, symbol):
return True
if check_bunch_match(diagonal_2, symbol):
return True
return False
def main():
board = generate_board()
print(WELCOME)
turn = "X"
while True:
show_board(board)
cmd = input(ENTER % turn)
ri, ci = parse_rc(cmd)
if ri >= 0:
if board[ri][ci] == " ":
board[ri][ci] = turn
if check_win(board, turn):
show_board(board)
print("%s win!" % turn)
return
if turn == "X":
turn = "O"
else:
turn = "X"
else:
print(Used)
else:
print(Invalid)
main()