游戏介绍
黑白棋,又称翻转棋(Reversi)、奥赛罗棋(Othello)或苹果棋,是一种两人对弈的策略棋类游戏。以下是其基本规则:
- 棋盘: 使用标准的 8x8 棋盘。起始时在中间放置四枚棋子,形成对角线交叉的模样。
- 玩家身份: 一方执黑棋,另一方执白棋。
- 开局: 游戏开始时,中间的四颗棋子按照黑白相间的方式排列。
- 落子: 轮流由黑方和白方落子。每一步,玩家在一个空格上放置自己的棋子,使对方的一个或多个棋子被夹在新放置的棋子和已有的棋子之间。
- 翻转对方棋子: 落子后,夹在新放置的棋子和已有的棋子之间的对方棋子会被翻转变成己方棋子。
- 无子可落时: 如果一方无法进行合法的落子,则该方放弃该轮,对方继续行动。
- 游戏结束: 游戏结束时,棋盘被填满或者双方都无法进行合法的落子。胜者是拥有更多棋子的一方。
- 胜负判定: 胜者是拥有最多棋子的一方。
完整代码和资源
- 简单版本:黑白棋简单版本.zip
- 封装版本:黑白棋封装版本.zip
- 图片资源:img.zip
实现思路
画背景和棋盘
def paintEvent(self, event: QPaintEvent): # 绘图事件# 调用父类同名方法super().paintEvent(event)# 定义画家(请了一个画家来画画),画在主窗口上painter = QPainter(self)# 以窗口大小画背景图painter.drawPixmap(0, 0, self.width(), self.height(),QPixmap('./img/board.jpg') )# 窗口宽度分12份self.grid_width = self.width() // 12# 窗口高度分12份self.grid_height = self.height() // 12# 棋盘起点坐标为grid_width, grid_heightself.start_x = self.grid_width*2self.start_y = self.grid_height*2# 设置线宽line_width = 4painter.setPen(QPen(Qt.black, line_width))# 取中间8份画棋盘for i in range(9):# 画横线painter.drawLine(self.start_x, self.start_y + self.grid_height * i,self.start_x + self.grid_width * 8, self.start_y + self.grid_height * i)# 画竖线painter.drawLine(self.start_x + self.grid_width * i, self.start_y,self.start_x + self.grid_width * i, self.start_y + self.grid_height * 8)
标志棋盘棋子状态
# 标志棋子状态EMPTY = 0BLACK = 1WHITE = 2# 创建一个8x8的二维数组# EMPTY 0表示没有棋子,BLACK 1表示黑棋,WHITE 2表示白棋self.chess = [[EMPTY for _ in range(8)] for _ in range(8)]# 设置棋盘中间4个棋子状态self.chess[3][3] = BLACKself.chess[3][4] = WHITEself.chess[4][3] = WHITEself.chess[4][4] = BLACK
吃子规则算法
def judge_rule(x, y, chess, current_role, eat_chess = True):"""功能:吃子的规则:param x: 棋盘x下标:param y: 棋盘y下标:param chess: 棋子状态数组:param current_role: 棋子角色:param eat_chess: 默认为True,代表改变原来的数组, False不改变数组内容"""# 棋盘的八个方向directions = [(1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0), (-1, 1), (0, 1), (1, 1)]temp_x, temp_y = x, yeat_num = 0if chess[temp_x][temp_y] != EMPTY:return 0for dx, dy in directions:temp_x, temp_y = x + dx, y + dy # 准备判断相邻棋子if 0 <= temp_x < len(chess) and 0 <= temp_y < len(chess) and \chess[temp_x][temp_y] != current_role and chess[temp_x][temp_y] != EMPTY:temp_x, temp_y = x + dx, y + dy # 继续判断下一个,向前走一步while 0 <= temp_x < len(chess) and 0 <= temp_y < len(chess):if chess[temp_x][temp_y] == EMPTY: # 遇到空位跳出breakif chess[temp_x][temp_y] == current_role: # 找到自己的棋子,代表可以吃子if eat_chess: # 确定吃子chess[x][y] = current_role # 开始点标志为自己的棋子# 添加范围检查,确保不越界while 0 <= temp_x < len(chess) and 0 <= temp_y < len(chess) and \(temp_x, temp_y) != (x, y):chess[temp_x][temp_y] = current_role # 标志为自己的棋子temp_x, temp_y = temp_x - dx, temp_y - dy # 继续后退一步eat_num += 1 # 累计else: # 不吃子,只是判断这个位置能不能吃子temp_x, temp_y = temp_x - dx, temp_y - dy # 后退一步# 添加范围检查,确保不越界while 0 <= temp_x < len(chess) and 0 <= temp_y < len(chess) and \(temp_x, temp_y) != (x, y):temp_x, temp_y = temp_x - dx, temp_y - dy # 继续后退一步eat_num += 1break # 跳出循环temp_x, temp_y = temp_x + dx, temp_y + dy # 向前走一步# 如果这个方向不能吃子,就换一个方向temp_x, temp_y = x, yreturn eat_num # 返回能吃子的个数
切换角色
def switch_role(self):"""切换下子角色"""if self.current_role == BLACK:self.current_role = WHITE# 显示黑,隐藏白self.ui.labelBlack.hide()self.ui.labelWhite.show()else:self.current_role = BLACK# 显示白,隐藏黑self.ui.labelBlack.show()self.ui.labelWhite.hide()
用户点击落子
def mousePressEvent(self, event: QMouseEvent): # 鼠标按下事件# 调用父类同名方法super().mousePressEvent(event)# 获取 x, y 坐标x = event.x()y = event.y()# 范围判断,点击范围不要超过棋盘if (x >= self.start_xand x <= self.start_x + self.grid_width * 8and y >= self.start_yand y <= self.start_y + self.grid_height * 8):self.press_i = (x - self.start_x) // self.grid_widthself.press_j = (y - self.start_y) // self.grid_heightprint(self.press_i, self.press_j)# self.chess[self.press_i][self.press_j] = self.current_role# 吃子eat_num = judge_rule(self.press_i, self.press_j, self.chess, self.current_role)if eat_num > 0: # 成功吃子后再操作self.switch_role() # 切换下子角色self.update() # 更新绘图区域
机器落子
def machineTimerSlot(self):flag = False# 找到能吃子的位置for i in range(8):for j in range(8):num = judge_rule(i, j, self.chess, self.current_role, False)if num > 0:self.press_i = i # 保存能吃子的第一个位置self.press_j = jflag = Truebreak;if flag:breakif flag == False: # 说明机器不能吃子self.switch_role() # 切换角色return# 吃子judge_rule(self.press_i, self.press_j, self.chess, self.current_role)# 更新绘图self.update()self.switch_role() # 切换角色
判断输赢
def judge_result(self):"""判断输赢:双方都不能吃子即可判断输赢"""black_count = 0white_count = 0is_over = True # 结束标志位,默认为True,代表结束for i in range(8):for j in range(8):if self.chess[i][j] == BLACK:black_count += 1elif self.chess[i][j] == WHITE:white_count += 1# 判断黑白子在i, j位置,能否吃子,没有改变chess的数组# > 0,说明能吃子,游戏还没结束if judge_rule(i, j, self.chess, BLACK, False) > 0 \or judge_rule(i, j, self.chess, WHITE, False) > 0:is_over = False # 游戏还没结束# lcd显示黑白棋个数self.ui.lcdBlack.display(black_count)self.ui.lcdWhite.display(white_count)if is_over == False: # 游戏还没结束,不往下执行returnself.ui.labelBlack.show()self.ui.labelWhite.show()# 判断输赢,弹出对话框显示谁赢if black_count > white_count:QMessageBox.information(self, '黑棋胜利', '黑棋胜利')elif black_count < white_count:QMessageBox.information(self, '白棋胜利', '白棋胜利')else:QMessageBox.information(self, '平局', '平局')

