python实现翻转棋游戏(othello)


Posted in Python onJuly 29, 2019

利用上一篇的框架,再写了个翻转棋的程序,为了调试minimax算法,花了两天的时间。

几点改进说明:

  • 拆分成四个文件:board.py,player.py,ai.py,othello.py。使得整个结构更清晰,更通用,更易于维护。
  • AI 的水平跟 minimax 的递归深度,以及评价函数有关。基于此,我把 minimax 和评价函数都放到 AI 类里面
  • AIPlayer 使用了多重继承。继承了 Player 与 AI 两个类
  • Game 类中把原run函数里的生成两个玩家的部分提出来,写成一个函数make_two_players,使得 run函数结构更清晰
  • AI 玩家等级不要选择 0:beginer。会报错,还没调试好

board.py

'''
作者:hhh5460
时间:2017年7月1日
'''

class Board(object):
 def __init__(self):
  self.empty = '.'
  self._board = [[self.empty for _ in range(8)] for _ in range(8)] # 规格:8*8
  self._board[3][4], self._board[4][3] = 'X', 'X'
  self._board[3][3], self._board[4][4] = 'O', 'O'
  
 # 增加 Board[][] 索引语法
 def __getitem__(self, index):
  return self._board[index]
 
 # 打印棋盘
 def print_b(self):
  board = self._board
  print(' ', ' '.join(list('ABCDEFGH')))
  for i in range(8):
   print(str(i+1),' '.join(board[i]))
   
 # 棋局终止
 def teminate(self):
  list1 = list(self.get_legal_actions('X'))
  list2 = list(self.get_legal_actions('O'))
  return [False, True][len(list1) == 0 and len(list2) == 0]
  
 # 判断赢家
 def get_winner(self):
  s1, s2 = 0, 0
  for i in range(8):
   for j in range(8):
    if self._board[i][j] == 'X':
     s1 += 1
    if self._board[i][j] == 'O':
     s2 += 1
  if s1 > s2:
   return 0 # 黑胜
  elif s1 < s2:
   return 1 # 白胜
  elif s1 == s2:
   return 2 # 平局
 # 落子
 def _move(self, action, color):
  x,y = action
  self._board[x][y] = color
  
  return self._flip(action, color)
  
 
  
 
 # 翻子(返回list)
 def _flip(self, action, color):
  flipped_pos = []
  
  for line in self._get_lines(action):
   for i,p in enumerate(line):
    if self._board[p[0]][p[1]] == self.empty: 
     break
    elif self._board[p[0]][p[1]] == color:
     flipped_pos.extend(line[:i])
     break
  
  for p in flipped_pos:
   self._board[p[0]][p[1]] = color
   
  return flipped_pos
  
 # 撤销
 def _unmove(self, action, flipped_pos, color):
  self._board[action[0]][action[1]] = self.empty
  
  uncolor = ['X', 'O'][color=='X']
  for p in flipped_pos:
   self._board[p[0]][p[1]] = uncolor
   
 # 生成8个方向的下标数组,方便后续操作
 def _get_lines(self, action):
  '''说明:刚开始我是用一维棋盘来考虑的,后来改为二维棋盘。偷懒,不想推倒重来,简单地修改了一下'''
  board_coord = [(i,j) for i in range(8) for j in range(8)] # 棋盘坐标
  
  r,c = action
  ix = r*8 + c
  r, c = ix//8, ix%8
  left = board_coord[r*8:ix] # 要反转
  right = board_coord[ix+1:(r+1)*8]
  top = board_coord[c:ix:8] # 要反转
  bottom = board_coord[ix+8:8*8:8]
  
  if r <= c:
   lefttop = board_coord[c-r:ix:9] # 要反转
   rightbottom = board_coord[ix+9:(7-(c-r))*8+7+1:9]
  else:
   lefttop = board_coord[(r-c)*8:ix:9] # 要反转
   rightbottom = board_coord[ix+9:7*8+(7-(c-r))+1:9]
  
  if r+c<=7:
   leftbottom = board_coord[ix+7:(r+c)*8:7]
   righttop = board_coord[r+c:ix:7] # 要反转
  else:
   leftbottom = board_coord[ix+7:7*8+(r+c)-7+1:7]
   righttop = board_coord[((r+c)-7)*8+7:ix:7] # 要反转
  
  # 有四个要反转,方便判断
  left.reverse()
  top.reverse()
  lefttop.reverse()
  righttop.reverse()
  lines = [left, top, lefttop, righttop, right, bottom, leftbottom, rightbottom]
  return lines
  
 # 检测,位置是否有子可翻
 def _can_fliped(self, action, color):
  flipped_pos = []
  
  for line in self._get_lines(action):
   for i,p in enumerate(line):
    if self._board[p[0]][p[1]] == self.empty: 
     break
    elif self._board[p[0]][p[1]] == color:
     flipped_pos.extend(line[:i])
     break
  return [False, True][len(flipped_pos) > 0]
  
 # 合法走法
 def get_legal_actions(self, color):
  uncolor = ['X', 'O'][color=='X']
  uncolor_near_points = [] # 反色邻近的空位
  
  board = self._board
  for i in range(8):
   for j in range(8):
    if board[i][j] == uncolor:
     for dx,dy in [(-1,0),(-1,1),(0,1),(1,1),(1,0),(1,-1),(0,-1)]:
      x, y = i+dx, j+dy
      if 0 <= x <=7 and 0 <= y <=7 and board[x][y] == self.empty and (x, y) not in uncolor_near_points:
       uncolor_near_points.append((x, y))
  for p in uncolor_near_points:
   if self._can_fliped(p, color):
    yield p

# 测试
if __name__ == '__main__':
 board = Board()
 board.print_b()
 print(list(board.get_legal_actions('X')))

player.py

from ai import AI

'''
作者:hhh5460
时间:2017年7月1日
'''

# 玩家
class Player(object):
 def __init__(self, color):
  self.color = color
  
 # 思考
 def think(self, board):
  pass
  
 # 落子
 def move(self, board, action):
  flipped_pos = board._move(action, self.color)
  return flipped_pos
  
 # 悔子
 def unmove(self, board, action, flipped_pos):
  board._unmove(action, flipped_pos, self.color)


# 人类玩家
class HumanPlayer(Player):
 def __init__(self, color):
  super().__init__(color)
 
 def think(self, board):
  while True:
   action = input("Turn to '{}'. \nPlease input a point.(such as 'A1'): ".format(self.color)) # A1~H8
   r, c = action[1], action[0].upper()
   if r in '12345678' and c in 'ABCDEFGH': # 合法性检查1
    x, y = '12345678'.index(r), 'ABCDEFGH'.index(c)
    if (x,y) in board.get_legal_actions(self.color): # 合法性检查2
     return x, y


# 电脑玩家(多重继承)
class AIPlayer(Player, AI):
 
 def __init__(self, color, level_ix=0):
  super().__init__(color)    # init Player
  super(Player, self).__init__(level_ix) # init AI
  
 
 def think(self, board):
  print("Turn to '{}'. \nPlease wait a moment. AI is thinking...".format(self.color))
  uncolor = ['X','O'][self.color=='X']
  opfor = AIPlayer(uncolor) # 假想敌,陪练
  action = self.brain(board, opfor, 4)
  return action

ai.py

import random

'''
作者:hhh5460
时间:2017年7月1日
'''

class AI(object):
 '''
 三个水平等级:初级(beginner)、中级(intermediate)、高级(advanced)
 '''
 def __init__(self, level_ix =0):
  # 玩家等级
  self.level = ['beginner','intermediate','advanced'][level_ix]
  # 棋盘位置权重,参考:https://github.com/k-time/ai-minimax-agent/blob/master/ksx2101.py
  self.board_weights = [
   [120, -20, 20, 5, 5, 20, -20, 120],
   [-20, -40, -5, -5, -5, -5, -40, -20],
   [ 20, -5, 15, 3, 3, 15, -5, 20],
   [ 5, -5, 3, 3, 3, 3, -5, 5],
   [ 5, -5, 3, 3, 3, 3, -5, 5],
   [ 20, -5, 15, 3, 3, 15, -5, 20],
   [-20, -40, -5, -5, -5, -5, -40, -20],
   [120, -20, 20, 5, 5, 20, -20, 120]
  ]
  
 # 评估函数(仅根据棋盘位置权重)
 def evaluate(self, board, color):
  uncolor = ['X','O'][color=='X']
  score = 0
  for i in range(8):
   for j in range(8):
    if board[i][j] == color:
     score += self.board_weights[i][j]
    elif board[i][j] == uncolor:
     score -= self.board_weights[i][j]
  return score

 # AI的大脑
 def brain(self, board, opponent, depth):
  if self.level == 'beginer':   # 初级水平
   _, action = self.randomchoice(board)
  elif self.level == 'intermediate': # 中级水平
   _, action = self.minimax(board, opponent, depth)
  elif self.level == 'advanced':  # 高级水平
   _, action = self.minimax_alpha_beta(board, opponent, depth)
  assert action is not None, 'action is None'
  return action
 
 # 随机选(从合法走法列表中随机选)
 def randomchoice(self, board):
  color = self.color
  action_list = list(board.get_legal_actions(color))
  return None, random.choice(action_list)
 
 # 极大极小算法,限制深度
 def minimax(self, board, opfor, depth=4): # 其中 opfor 是假想敌、陪练
  '''参考:https://github.com/k-time/ai-minimax-agent/blob/master/ksx2101.py'''
  color = self.color
  
  if depth == 0:
   return self.evaluate(board, color), None
  
  action_list = list(board.get_legal_actions(color))
  if not action_list:
   return self.evaluate(board, color), None
  
  best_score = -100000
  best_action = None

  for action in action_list:
   flipped_pos = self.move(board, action) # 落子
   score, _ = opfor.minimax(board, self, depth-1) # 深度优先,轮到陪练
   self.unmove(board, action, flipped_pos) # 回溯
   
   score = -score
   if score > best_score:
    best_score = score
    best_action = action

  return best_score, best_action
  
 # 极大极小算法,带alpha-beta剪枝
 def minimax_alpha_beta(self, board, opfor, depth=8, my_best=-float('inf'), opp_best=float('inf')):
  '''参考:https://github.com/k-time/ai-minimax-agent/blob/master/ksx2101.py'''
  color = self.color
  
  if depth == 0:
   return self.evaluate(board, color), None
  
  action_list = list(board.get_legal_actions(color))
  if not action_list:
   return self.evaluate(board, color), None
  
  best_score = my_best
  best_action = None
  
  for action in action_list:
   flipped_pos = self.move(board, action) # 落子
   score, _ = opfor.minimax_alpha_beta(board, self, depth-1, -opp_best, -best_score) # 深度优先,轮到陪练
   self.unmove(board, action, flipped_pos) # 回溯
   
   score = -score
   if score > best_score:
    best_score = score
    best_action = action
    
   if best_score > opp_best:
    break

  return best_score, best_action

othello.py

from board import Board
from player import HumanPlayer, AIPlayer

'''
作者:hhh5460
时间:2017年7月1日
'''

# 游戏
class Game(object):
 def __init__(self):
  self.board = Board()
  self.current_player = None
  
 # 生成两个玩家
 def make_two_players(self):
  ps = input("Please select two player's type:\n\t0.Human\n\t1.AI\nSuch as:0 0\n:")
  p1, p2 = [int(p) for p in ps.split(' ')]
  if p1 == 1 or p2 == 1: # 至少有一个AI玩家
   level_ix = int(input("Please select the level of AI player.\n\t0: beginner\n\t1: intermediate\n\t2: advanced\n:"))
   if p1 == 0:
    player1 = HumanPlayer('X')
    player2 = AIPlayer('O', level_ix)
   elif p2 == 0:
    player1 = AIPlayer('X', level_ix)
    player2 = HumanPlayer('O')
   else:
    player1 = AIPlayer('X', level_ix)
    player2 = AIPlayer('O', level_ix)
  else:
   player1, player2 = HumanPlayer('X'), HumanPlayer('O') # 先手执X,后手执O
  
  return player1, player2
 
 
 # 切换玩家(游戏过程中)
 def switch_player(self, player1, player2):
  if self.current_player is None:
   return player1
  else:
   return [player1, player2][self.current_player == player1]
 
 # 打印赢家
 def print_winner(self, winner): # winner in [0,1,2]
  print(['Winner is player1','Winner is player2','Draw'][winner])
 
 # 运行游戏
 def run(self):
  # 生成两个玩家
  player1, player2 = self.make_two_players()
  
  # 游戏开始
  print('\nGame start!\n')
  self.board.print_b() # 显示棋盘
  while True:
   self.current_player = self.switch_player(player1, player2) # 切换当前玩家
   
   action = self.current_player.think(self.board) # 当前玩家对棋盘进行思考后,得到招法
   
   if action is not None: 
    self.current_player.move(self.board, action) # 当前玩家执行招法,改变棋盘
   
   self.board.print_b() # 显示当前棋盘
   
   if self.board.teminate(): # 根据当前棋盘,判断棋局是否终止
    winner = self.board.get_winner() # 得到赢家 0,1,2
    break
  
  self.print_winner(winner)
  print('Game over!')
  
  self.board.print_history()
 
 
if __name__ == '__main__':
 Game().run()

效果图

python实现翻转棋游戏(othello)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python集合类型用法分析
Apr 08 Python
python爬虫框架talonspider简单介绍
Jun 09 Python
简单学习Python多进程Multiprocessing
Aug 29 Python
MAC中PyCharm设置python3解释器
Dec 15 Python
Python多进程池 multiprocessing Pool用法示例
Sep 07 Python
浅谈Python 多进程默认不能共享全局变量的问题
Jan 11 Python
Python3实现的旋转矩阵图像算法示例
Apr 03 Python
python机器学习包mlxtend的安装和配置详解
Aug 21 Python
python GUI库图形界面开发之PyQt5单行文本框控件QLineEdit详细使用方法与实例
Feb 27 Python
利用python绘制数据曲线图的实现
Apr 09 Python
浅析Python 多行匹配模式
Jul 24 Python
快速创建python 虚拟环境
Nov 28 Python
Django如何将URL映射到视图
Jul 29 #Python
python3获取当前目录的实现方法
Jul 29 #Python
Python在Matplotlib图中显示中文字体的操作方法
Jul 29 #Python
Django框架创建mysql连接与使用示例
Jul 29 #Python
python使用minimax算法实现五子棋
Jul 29 #Python
浅析python 中大括号中括号小括号的区分
Jul 29 #Python
Django分页功能的实现代码详解
Jul 29 #Python
You might like
PHP5 安装方法
2006/10/09 PHP
php集成环境xampp中apache无法启动问题解决方案
2014/11/18 PHP
PHP5.5迭代生成器用法实例详解
2016/03/16 PHP
js获取单选按钮的数据
2006/11/27 Javascript
JavaScript中Object和Function的关系小结
2009/09/26 Javascript
谈谈关于JavaScript 中的 MVC 模式
2013/04/11 Javascript
javascript检测对象中是否存在某个属性判断方法小结
2013/05/19 Javascript
window.onresize 多次触发的解决方法
2013/11/08 Javascript
标题过长使用javascript按字节截取字符串
2014/04/24 Javascript
理解Javascript图片预加载
2016/02/23 Javascript
angularjs表格ng-table使用备忘录
2016/03/09 Javascript
使用javascript插入样式
2016/03/14 Javascript
jQuery通过ajax请求php遍历json数组到table中的代码(推荐)
2016/06/12 Javascript
对javascript继承的理解
2016/10/11 Javascript
详解jQuery uploadify文件上传插件的使用方法
2016/12/16 Javascript
jQuery.Form上传文件操作
2017/02/05 Javascript
vue 运用mock数据的示例代码
2017/11/07 Javascript
8个有意思的JavaScript面试题
2019/07/30 Javascript
JavaScript中while循环的基础使用教程
2020/08/11 Javascript
Openlayers绘制地图标注
2020/09/28 Javascript
原生js实现自定义难度的扫雷游戏
2021/01/22 Javascript
[00:55]深扒TI7聊天轮盘语音出处3
2017/05/11 DOTA
[01:11:11]Alliance vs RNG 2019国际邀请赛淘汰赛 败者组BO1 8.20.mp4
2020/07/19 DOTA
Python实现windows下模拟按键和鼠标点击的方法
2015/03/13 Python
python matplotlib画图库学习绘制常用的图
2019/03/19 Python
python os.path.isfile 的使用误区详解
2019/11/29 Python
python定义类的简单用法
2020/07/24 Python
美国Randolph太阳镜官网:美国制造的飞行员太阳镜和射击眼镜
2018/06/15 全球购物
中药专业大学生医药工作求职信
2013/10/25 职场文书
路政管理专业推荐信
2013/11/11 职场文书
大学生就业求职信
2014/06/12 职场文书
停发工资证明范本
2015/06/12 职场文书
2016年党员读书月活动总结
2016/04/06 职场文书
Python+OpenCV实现图片中的圆形检测
2022/04/07 Python
python 镜像环境搭建总结
2022/09/23 Python
table设置超出部分隐藏,鼠标移上去显示全部内容的方法
2022/12/24 HTML / CSS