课程上要求使用蒙特卡洛数搜索实现黑白棋的人机对弈,以下是我参考了许多大佬的资料整合后实现的最终版可直接运行代码,略微垃圾,欢迎各位大佬批评指正!
游戏规则
黑白棋的棋盘是一个有8*8方格的棋盘。下棋时将棋下在空格中间,而不是像围棋一样下在交叉点上。开始时在棋盘正中有两白两黑四个棋子交叉放置,黑棋总是先下子 。
下子的方法
把自己颜色的棋子放在棋盘的空格上,而当自己放下的棋子在横、竖、斜八个方向内有一个自己的棋子,则被夹在中间的全部翻转会成为自己的棋子。并且,只有在可以翻转棋子的地方才可以下子。
棋规
1.棋局开始时黑棋位于e4和d5,白棋位于d4和e5。
2.黑方先行,双方交替下棋。
3.一步合法的棋步包括:在一个空格新落下一个棋子,并且翻转对手一个或多个棋子。
4.新落下的棋子与棋盘上已有的同色棋子间,对方被夹住的所有棋子都要翻转过来。可以是横着夹,竖着夹,或是斜着夹。夹住的位置上必须全部是对手的棋子,不能有空格。
5.一步棋可以在数个方向上翻棋,任何被夹住的棋子都必须被翻转过来,棋手无权选择不去翻某个棋子。
6.除非至少翻转了对手的一个棋子,否则就不能落子。如果一方没有合法棋步,也就是说不管他下到哪里,都不能至少翻转对手的一个棋子,那他这一轮只能弃权,而由他的对手继续落子直到他有合法棋步可下。
7.如果一方至少有一步合法棋步可下,他就必须落子,不得弃权。
8.棋局持续下去,直到棋盘填满或者双方都无合法棋步可下。
代码部分
首先是有关游戏设定以及棋盘设置等内置类以及一些函数;
# !/usr/bin/Anaconda3/python
# -*- coding: utf-8 -*-
from func_timeout import func_timeout, FunctionTimedOut
import datetime
from board import Board
from copy import deepcopy
class Game(object):
def __init__(self, black_player, white_player):
self.board = Board() # 棋盘
# 定义棋盘上当前下棋棋手,先默认是 None
self.current_player = None
self.black_player = black_player # 黑棋一方
self.white_player = white_player # 白棋一方
self.black_player.color = "X"
self.white_player.color = "O"
def switch_player(self, black_player, white_player):
"""
游戏过程中切换玩家
:param black_player: 黑棋
:param white_player: 白棋
:return: 当前玩家
"""
# 如果当前玩家是 None 或者 白棋一方 white_player,则返回 黑棋一方 black_player;
if self.current_player is None:
return black_player
else:
# 如果当前玩家是黑棋一方 black_player 则返回 白棋一方 white_player
if self.current_player == self.black_player:
return white_player
else:
return black_player
def print_winner(self, winner):
"""
打印赢家
:param winner: [0,1,2] 分别代表黑棋获胜、白棋获胜、平局3种可能。
:return:
"""
print(['黑棋获胜!', '白棋获胜!', '平局'][winner])
def force_loss(self, is_timeout=False, is_board=False, is_legal=False):
"""
落子3个不合符规则和超时则结束游戏,修改棋盘也是输
:param is_timeout: 时间是否超时,默认不超时
:param is_board: 是否修改棋盘
:param is_legal: 落子是否合法
:return: 赢家(0,1),棋子差 0
"""
if self.current_player == self.black_player:
win_color = '白棋 - O'
loss_color = '黑棋 - X'
winner = 1
else:
win_color = '黑棋 - X'
loss_color = '白棋 - O'
winner = 0
if is_timeout:
print('\n{} 思考超过 60s, {} 胜'.format(loss_color, win_color))
if is_legal:
print('\n{} 落子 3 次不符合规则,故 {} 胜'.format(loss_color, win_color))
if is_board:
print('\n{} 擅自改动棋盘判输,故 {} 胜'.format(loss_color, win_color))
diff = 0
return winner, diff
def run(self):
"""
运行游戏
:return:
"""
# 定义统计双方下棋时间
total_time = {"X": 0, "O": 0}
# 定义双方每一步下棋时间
step_time = {"X": 0, "O": 0}
# 初始化胜负结果和棋子差
winner = None
diff = -1
# 游戏开始
print('\n=====开始游戏!=====\n')
# 棋盘初始化
self.board.display(step_time, total_time)
while True:
# 切换当前玩家,如果当前玩家是 None 或者白棋 white_player,则返回黑棋 black_player;
# 否则返回 white_player。
self.current_player = self.switch_player(self.black_player, self.white_player)
start_time = datetime.datetime.now()
# 当前玩家对棋盘进行思考后,得到落子位置
# 判断当前下棋方
color = "X" if self.current_player == self.black_player else "O"
# 获取当前下棋方合法落子位置
legal_actions = list(self.board.get_legal_actions(color))
# print("%s合法落子坐标列表:"%color,legal_actions)
if len(legal_actions) == 0:
# 判断游戏是否结束
if self.game_over():
# 游戏结束,双方都没有合法位置
winner, diff = self.board.get_winner() # 得到赢家 0,1,2
break
else:
# 另一方有合法位置,切换下棋方
continue
board = deepcopy(self.board._board)
# legal_actions 不等于 0 则表示当前下棋方有合法落子位置
try:
for i in range(0, 3):
# 获取落子位置
action = func_timeout(60, self.current_player.get_move,
kwargs={'board': self.board})
# 如果 action 是 Q 则说明人类想结束比赛
if action == "Q":
# 说明人类想结束游戏,即根据棋子个数定输赢。
break
if action not in legal_actions:
# 判断当前下棋方落子是否符合合法落子,如果不合法,则需要对方重新输入
print("你落子不符合规则,请重新落子!")
continue
else:
# 落子合法则直接 break
break
else:
# 落子3次不合法,结束游戏!
winner, diff = self.force_loss(is_legal=True)
break
except FunctionTimedOut:
# 落子超时,结束游戏
winner, diff = self.force_loss(is_timeout=True)
break
# 结束时间
end_time = datetime.datetime.now()
if board != self.board._board:
# 修改棋盘,结束游戏!
winner, diff = self.force_loss(is_board=True)
break
if action