利用上一篇的框架,再写了个翻转棋的程序,为了调试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)的更多相关文章

  1. bzoj1647 / P1985 [USACO07OPEN]翻转棋

    P1985 [USACO07OPEN]翻转棋 其实我们只要枚举第一行的状态,后面的所有状态都是可以唯一确定的. 用二进制枚举灰常方便 #include<iostream> #include ...

  2. 洛谷 P1985 翻转棋

    P1985 翻转棋 题目描述 农夫约翰知道,聪明的奶牛可以产更多的牛奶.他为奶牛设计了一种智力游戏,名叫翻转棋. 翻转棋可以分成 M × N 个格子,每个格子有两种颜色,一面是黑的,一面是白的. 一旦 ...

  3. P1985 [USACO07OPEN]翻转棋

    题目链接: 翻转棋 题目分析: 先状压/\(dfs\)枚举第一排状态,然后在每个\(1\)下面翻,即确定了第一排就确定了后面的状态 最后验证一下最后一排是不是全0即可 代码: #include< ...

  4. leetcode python翻转字符串里的单词

    # Leetcode 151 翻转字符串里的单词### 题目描述给定一个字符串,逐个翻转字符串中的每个单词. **示例1:** 输入: "the sky is blue" 输出: ...

  5. 题解 P1985 【[USACO07OPEN]翻转棋】

    讲讲我的做法 刚开始做这道题的时候,看到\(n<=15\),我这个\(6\)年级的蒟蒻的第1反应是状压\(dp\).貌似不好做.然而,等到我在省中集训的时候,老师的一席话,让我豁然开朗.今天我准 ...

  6. Python翻转字符串或者列表的方式

    1. reversed class reversed(object) | reversed(sequence) -> reverse iterator over values of the se ...

  7. python中字符串的翻转(方法总结)

    Python翻转字符串(reverse string), 一共包含5种方法, 其中第一种最简单, 即步长为-1, 输出字符串; 方法如下 5种方法的比较: 1. 简单的步长为-1, 即字符串的翻转(常 ...

  8. 隔壁信概大作业xjb写——同化棋ATAXX

    话说泥萌北大信科啊,助教是有多懒...去年黑白棋今年同化棋,顺带打ai都不用自己写标程... 好吧..我知道泥萌重点在各种sb的辅助操作上..什么悲剧的可以随时暂停载入...有毒吧 [据说泥萌上课没讲 ...

  9. 【Python】Python学习----第一模块笔记

    1.python是什么? python是动态解释型的强类型定义语言. python官方版本的解释器是CPython.该解释器使用C语言开发. 当前主要使用3.x版本的python. 2.第一个pyth ...

随机推荐

  1. Android Monkey的使用

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/71750907 本文出自[赵彦军的博客] 什么是 Monkey Monkey 是一个 ...

  2. python常用模块之logging模块

    #日志级别 : noset debug info warning error critical #控制台默认的级别是warning的. import logging logging.basicConf ...

  3. python生成式和生成器

    一,生成式和生成器 1,列表生成式 mylist = [ i*i for i in range(3) if i>1 ] print(mylist) 运行结果:[4] 可以发现,快速生成列表的表达 ...

  4. Oracle EBS INV创建保留

    CREATE or REPPLACE PROCEDURE CreateReservation AS -- Common Declarations l_api_version NUMBER := 1.0 ...

  5. 【转】Java学习---快速掌握RPC原理及实现

    [原文]https://www.toutiao.com/i6592365493435236872/ ​RPC概述 RPC(Remote Procedure Call)即远程过程调用,也就是说两台服务器 ...

  6. 直播内容不合规怎么办?智能AI为您解决审核难题

    背景 近些年来,视频直播快速发展,大量的直播平台如雨后春笋一般出现,但是这同样给直播内容的监管带来了巨大的挑战,一方面国家对于直播内容监管的要求日益严格,另一方面相对于文字内容的审核,多媒体内容的审核 ...

  7. linux shell编程进阶学习(转)

    第一节:基础 ls -lh  ——可以用户友好的方式看到文件大小 file 文件名 ——查看文件类型 stat 文件名 ——查看文件当前状态 man 命令/函数名 ——查看详细的帮助文档 man中看某 ...

  8. Flex 布局知识点梳理

    传统的布局方案,在针对特殊布局时会很不方便,比如垂直居中,把一个容器等分为N列等等.自从 Flex 出现以后,这些都迎刃而解了,本文对Flex相关内容做一个简单梳理. 什么是 Flex Flex 是 ...

  9. 金三银四求职季,前端面试题小梳理(HTML、CSS、JS)

    好久没写学习记录,最近太多事,又到一年求职季,都说金三银四求职季,自己也做一下最近学习的一些前端面试题梳理,还是个小白,写的不对请指正,不胜感激. HTML篇 html语义化 用语义化的代码标签书写, ...

  10. 3.3 线程---Handler消息传递机制浅析

    Handler的执行流程图: 当我们的子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送信息;而我们发送的信息会先到主线程的MessageQueu ...