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 性能优化技巧总结
Nov 01 Python
python逐行读写txt文件的实例讲解
Apr 03 Python
利用Python如何批量更新服务器文件
Jul 29 Python
Python 实现文件打包、上传与校验的方法
Feb 13 Python
python用win32gui遍历窗口并设置窗口位置的方法
Jul 26 Python
使用 Django Highcharts 实现数据可视化过程解析
Jul 31 Python
flask/django 动态查询表结构相同表名不同数据的Model实现方法
Aug 29 Python
python numpy库linspace相同间隔采样的实现
Feb 25 Python
OpenCV图片漫画效果的实现示例
Aug 18 Python
Python自动化测试中yaml文件读取操作
Aug 20 Python
python基于socket模拟实现ssh远程执行命令
Dec 05 Python
Python采集股票数据并制作可视化柱状图
Apr 04 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
解析array splice的移除数组中指定键的值,返回一个新的数组
2013/07/02 PHP
PHP开源开发框架ZendFramework使用中常见问题说明及解决方案
2014/06/12 PHP
golang与php实现计算两个经纬度之间距离的方法
2016/07/22 PHP
jQuery Real Person验证码插件防止表单自动提交
2015/11/06 Javascript
浅谈使用MVC模式进行JavaScript程序开发
2015/11/10 Javascript
浅析JavaScript声明变量
2015/12/21 Javascript
JS在onclientclick里如何控制onclick的执行
2016/05/30 Javascript
jQuery实现加入收藏夹功能(主流浏览器兼职)
2016/12/24 Javascript
Vue实现双向数据绑定
2017/05/03 Javascript
vue利用better-scroll实现轮播图与页面滚动详解
2017/10/20 Javascript
Vue 图片压缩并上传至服务器功能
2020/01/15 Javascript
解决vue scoped scss 无效的问题
2020/09/04 Javascript
Python中使用PIPE操作Linux管道
2015/02/04 Python
Python中关于Sequence切片的下标问题详解
2017/06/15 Python
JPype实现在python中调用JAVA的实例
2017/07/19 Python
python邮件发送smtplib使用详解
2020/06/16 Python
Python tkinter布局与按钮间距设置方式
2020/03/04 Python
python实现密码强度校验
2020/03/18 Python
Django import export实现数据库导入导出方式
2020/04/03 Python
Python pandas如何向excel添加数据
2020/05/22 Python
理解Django 中Call Stack机制的小Demo
2020/09/01 Python
通过Django Admin+HttpRunner1.5.6实现简易接口测试平台
2020/11/11 Python
python中PyQuery库用法分享
2021/01/15 Python
HTML5 canvas基本绘图之填充样式实现
2016/06/27 HTML / CSS
Html5踩坑记之mandMobile使用小记
2020/04/02 HTML / CSS
StubHub巴西:购买和出售您的门票
2016/07/22 全球购物
英国领先的运动物理治疗供应公司:Vivomed
2018/07/14 全球购物
英国家喻户晓的家居商店:The Range
2019/03/25 全球购物
吉列剃须刀英国官网:Gillette英国
2019/03/28 全球购物
美国在线购买内衣网站:HerRoom
2020/02/22 全球购物
员工试用期转正自我评价
2015/03/10 职场文书
转学证明范本
2015/06/19 职场文书
欠条格式范本
2015/07/03 职场文书
总结Pyinstaller打包的高级用法
2021/06/28 Python
Python调用腾讯API实现人脸身份证比对功能
2022/04/04 Python
MySQL中JOIN连接的基本用法实例
2022/06/05 MySQL