当前位置:  首页>> 技术小册>> Python编程轻松进阶(五)

15.5 非OOP 和OOP 的例子:井字棋

在Python编程的旅途中,理解面向对象编程(OOP)与非面向对象编程(非OOP)的区别与优势是至关重要的。井字棋(Tic-Tac-Toe),作为一种简单却经典的两人游戏,为我们提供了一个绝佳的实践平台,用以对比和展示这两种编程范式。本章节将通过实现井字棋游戏的两个版本——一个基于非OOP方式,另一个采用OOP方式,来深入探讨这两种编程风格的特点。

15.5.1 井字棋游戏概述

井字棋,也被称为三子棋或圈圈叉叉,是一种在3x3格子上进行的两人回合制游戏。游戏双方轮流在格子中放置自己的标记(通常是X和O),首先连成一线(横、竖、斜)的玩家获胜。若所有格子都被填满且无人获胜,则游戏平局。

15.5.2 非OOP版本的井字棋

步骤1:初始化棋盘

非OOP版本的井字棋通常使用列表(或其他数据结构)来直接表示棋盘。例如,可以使用一个二维列表来存储每个格子的状态(空、X、O)。

  1. def initialize_board():
  2. return [[' ' for _ in range(3)] for _ in range(3)]
  3. board = initialize_board()

步骤2:打印棋盘

定义一个函数来打印当前的棋盘状态。

  1. def print_board(board):
  2. for row in board:
  3. print('|'.join(row))
  4. print('-' * 7)

步骤3:玩家输入与棋盘更新

通过接收玩家输入的行和列号来更新棋盘。

  1. def update_board(board, player, row, col):
  2. if board[row][col] == ' ':
  3. board[row][col] = player
  4. else:
  5. print("该位置已被占用,请重新输入!")
  6. def get_move(player):
  7. while True:
  8. try:
  9. row = int(input(f"{player}的回合,请输入行号(1-3): ")) - 1
  10. col = int(input(f"请输入列号(1-3): ")) - 1
  11. if 0 <= row < 3 and 0 <= col < 3:
  12. return row, col
  13. else:
  14. print("输入的行号或列号超出范围,请重新输入!")
  15. except ValueError:
  16. print("请输入有效的数字!")
  17. # 游戏循环(简化版,未包含胜负判断)
  18. while True:
  19. # 玩家X和玩家O轮流输入
  20. row, col = get_move('X')
  21. update_board(board, 'X', row, col)
  22. print_board(board)
  23. # ... 类似地处理玩家O的回合 ...

注意:上述代码片段未包含完整的游戏逻辑,如检查胜负、处理平局等,仅用于展示非OOP的基本结构。

15.5.3 OOP版本的井字棋

在OOP版本中,我们会定义一些类来表示游戏中的不同实体,如Board(棋盘)、Player(玩家)以及Game(游戏)等。

步骤1:定义棋盘类

  1. class Board:
  2. def __init__(self):
  3. self.grid = [[' ' for _ in range(3)] for _ in range(3)]
  4. def update(self, row, col, player):
  5. if self.grid[row][col] == ' ':
  6. self.grid[row][col] = player
  7. else:
  8. print("该位置已被占用,请重新输入!")
  9. def print_board(self):
  10. for row in self.grid:
  11. print('|'.join(row))
  12. print('-' * 7)
  13. # 实例化棋盘
  14. board = Board()

步骤2:定义玩家类(可选)

虽然对于井字棋来说,玩家类可能不是必需的,但为了演示OOP,我们可以简单定义一个。

  1. class Player:
  2. def __init__(self, marker):
  3. self.marker = marker
  4. # 示例用法:player_x = Player('X')

步骤3:游戏逻辑

游戏逻辑通常不直接封装在类中,但可以作为一个单独的函数或另一个类(如Game)的方法。这里我们继续使用函数来管理游戏流程。

  1. def play_game():
  2. board = Board()
  3. players = ['X', 'O']
  4. current_player = 0
  5. while True:
  6. # 获取玩家输入并更新棋盘
  7. row, col = get_move(players[current_player]) # 假设get_move已修改以返回正确值
  8. board.update(row, col, players[current_player])
  9. board.print_board()
  10. # 检查胜负或平局(此处省略具体实现)
  11. # 切换玩家
  12. current_player = 1 - current_player
  13. # 调用游戏函数
  14. play_game()

15.5.4 比较与总结

非OOP的优势

  • 对于小型、简单的项目,非OOP代码可能更直观、易于理解。
  • 减少了类的定义和维护成本。

OOP的优势

  • 提高了代码的可重用性、可维护性和可扩展性。
  • 通过封装、继承和多态等特性,促进了更好的代码组织和模块化。
  • 使得复杂系统的构建和管理变得更加容易。

在井字棋这个例子中,虽然非OOP版本已经足够处理游戏的基本逻辑,但当我们考虑更复杂的游戏或需要更多游戏功能的扩展时,OOP版本将展现出其显著的优势。通过定义清晰的类和对象,我们能够更灵活地添加新功能、调整游戏规则,并更容易地实现代码的重用和测试。

因此,在实际项目开发中,选择OOP还是非OOP,应基于项目的具体需求、团队的技术栈以及预期的维护和发展需求来综合考虑。