Python五子棋,第一版(弱智电脑)
Python五子棋version:1.0
[*]功能介绍:基本实现五子棋功能,判定棋型,给予得分,因为评分机制,导致防御还可以,进攻就和弱智差不多了
[*]Model.py>>>主要实现的基本功能都在这里
[*]checkerboard.py>>>和界面UI的交互,待下一版实现
[*]cmd_board>>>测试用,cmd的打印界面
[*]setting.py>>>全局变量设置的保存
version:1.1准备实现alpha_beta剪枝搜索,有大佬会的吗?
# -*- coding: utf-8 -*-
# !/usr/bin/python3
"""
@ version: ??
@ author: Alex
@ file: Model
@ datetime: 2021/02/09 - 14:08
@ explain:
"""
from setting import SIZE, ChessType, COMPUTER, PLAYER, VECTOR
from CoreCode.tools import LogHandler
from typing import List, Set, Tuple
from itertools import chain
# TODO =============== 数据类型声明 =============
# 坐标着点 x
X: int = int()
# 坐标着点 y
Y: int = int()
# 坐标
Point: tuple = (X, Y)
class GoBangBase:
"""棋盘数据和得分规则管理
GoBangData实现
对落子的存储,采用的hash字典存储 self = Computer or Player
::data -> 五子棋的数据储存
::one_spot -> 待搜索的点
所有已落子的八个方向,延伸5个点位。不包括已落子和超出边界的子
def _result(self, p) -> List:
获取 p 点四个方向(xy轴及左右斜轴)的落子数据,拼接成字符串返回
def get_xy_score(self, spot:List, flag: str) -> int:
获取 spot 落点的具体评分
def update_one(self, p: List) -> Set:
获得p落点周围八个方位总计40个点,排除已落子和超出边界的点
def evaluation(self) -> Tuple:
对当前局面进行评价,获得敌我双方最大的收益点。
"""
data = dict().fromkeys(range(SIZE * SIZE), "0")
one_spot: set = set()
vector = chain.from_iterable(list(chain.from_iterable(VECTOR)))
@staticmethod
def key(x: X, y: Y): return x * SIZE + y
# TODO 筛选超出边界的点
def f(self, p: Point):
"""
对着点的筛选,A:没有超出边界. B:没有进行落子. C:不在待搜索的列表
:param p:
:return:
"""
if -1 < p < SIZE and -1 < p < SIZE:
if p in self and p not in self.one_spot:
return True
return False
def __getitem__(self, key: Point) -> str:
"""
:param key: List -> 落点的坐标
:return: 落点的数据 '0'=空白 '1' or '2' 已经落子
"""
if -1 < key < SIZE and -1 < key < SIZE:
return self.data
return ""
def __setitem__(self, key: Point, value: str):
"""
:param key: Set(int, int) -> 落点的坐标
:param value: 落点的数据1 or 2 玩家或者电脑AI
:return: None
"""
self.data = value
def __contains__(self, key):
"""
:param key: List -> 落点的坐标
:return: bool ->判断当前点是否可以落子
"""
return self == "0"
def _result(self, p: Point) -> List:
"""
:param p: 五子棋落点, 拼接效果:x->x->p->x->x
:return:四个方向的棋型, List
"""
def count(x, y): return + x, p + y]
def func(iterator):
return "".join( for e in iterator])
return + self + func(v2) for v1, v2 in VECTOR]
def get_xy_score(self, spot: Point, flag: str) -> int:
"""
:param spot:
:param flag:身份标识
:return:四个方向的棋型的评分列表
"""
self = flag
chess_type = self._result(spot)
self = "0"
_flag =
def get(chess):
for score, rules in ChessType.ScoreRules.items():
for rule in rules:
if rule.format(flag, _flag) in chess:
# TODO 玩家评分修正,玩家活三,单四
if flag == PLAYER:
if score == ChessType.LIVE_THREE:
score = ChessType.PLAYER_LIVE_THREE
if score == ChessType.RUSH_FOUR:
score = ChessType.PLAYER_RUSH_FOUR
return score
return 0.5
scores = sorted(, reverse=True)
return ChessType.score(scores)
def update_one(self, p: Point) -> Set:
"""
获取落点周围八个方位总计40个点,排除超出边界的点
:param p: 落点点
:return: set -> ((x, y),(x1, y1)...)
"""
self.one_spot.discard(p)
# TODO 对p点计算位移点。
def count(x, y): return p + x, p + y
# TODO 对VECTOR展开,转化一维列表,通过count函数计算位移后的点
vector =
r = set(filter(self.f, vector))
return r
def _evaluation(self, computer_scores, player_scores):
"""对相同收益进行比较
例如对手出现活三,我们有两个点可以堵截,有一个点既能堵截,又能对我们形成收益。
"""
score, move = player_scores
moves = []
for sco, spot in player_scores[:8]:
if sco == score:
moves.append(spot)
for sco, spot in computer_scores[:8]:
if spot in moves:
return sco, spot
return False
def evaluation(self) -> Tuple:
"""
对当前棋盘局面进行评估
:return: Tuple -> (score, (x, y))
"""
# TODO 遍历待搜索的着点,对双方进行评分.返回双方评分收益大的一个点
# TODO 遍历得道电脑和玩家落子最大收益
computer_scores = [(self.get_xy_score(spot, COMPUTER), spot) for spot in self.one_spot]
player_scores = [(-self.get_xy_score(spot, PLAYER), spot) for spot in self.one_spot]
# TODO 排序获得最大收益点
computer_scores = sorted(computer_scores, key=lambda p: p, reverse=True)
player_scores = sorted(player_scores, key=lambda p: p)
# TODO 如果电脑收益大于玩家,直接返回电脑得分点
if computer_scores > abs(player_scores):
return computer_scores
# TODO 如果电脑收益小于于玩家,进行收益进行比较
res = self._evaluation(computer_scores, player_scores)
if res:
return res
# TODO 返回玩家收益点
return player_scores
from CoreCode import GoBangBase
from setting import COMPUTER
class Board(GoBangBase):
"""
棋盘管理:
computer_order -->电脑落子记录
player_order -->电脑落子记录
Method
"""
computer_order: list = list()
player_order: list = list()
def modify(self, spot: list, identity: str) -> None:
self = identity
self.computer_order.append(spot) if identity == COMPUTER else self.player_order.append(spot)
@staticmethod
def game_over():
return False
def ai_move(self):
try:
result = self.evaluation()
except IndexError:
return 0, (8, 8)
return result
# -*- coding: utf-8 -*-
# !/usr/bin/python3
"""
@ version: ??
@ author: Alex
@ file: cmd_board
@ datetime: 2021/02/09 - 14:08
@ explain:
"""
from CoreCode import Board
from setting import SIZE, COMPUTER, PLAYER
from CoreCode.tools import LogHandler
log = LogHandler("borad", file=True)
class StrBoard(Board):
yingshe = {
"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,
"9":9,"a":10,"b":11,"c":12,"d":13
}
def print_board(self):
str1 = "0123456789abcde"
r = ''
for x in range(SIZE):
s = ""
for y in range(SIZE):
s += " " + self[(x, y)]
r += str1+"" + s + "\n"
str1 ="x/y " + " ".join(str1)
print(r + str1)
def main(self):
next_move = COMPUTER
while True:
self.print_board()
if next_move == PLAYER:
r = input("请输入你要落子的位置(0-d):0d or 91>>>>")
x,y = self.yingshe], self.yingshe]
self[(x, y)] = PLAYER
bb = self.update_one((x, y))
self.one_spot |= bb
next_move = COMPUTER
self.player_order.append((x, y))
else:
r = self.ai_move()
print(f"运行结果:{r}\n")
self] = COMPUTER
bb = self.update_one(r)
self.one_spot |= bb
next_move = PLAYER
self.computer_order.append(r)
if __name__ == '__main__':
s = StrBoard()
try:
s.main()
except KeyError:
log.warning(f"\n{s.player_order}\n{s.computer_order}\n")
# -*- coding: utf-8 -*-
# !/usr/bin/python3
"""
@ version: ??
@ author: Alex
@ file: setting
@ datetime: 2020/10/17 - 21:08
@ explain:
"""
# TODO 棋盘大小
SIZE = 15
# TODO VECTOR 向量,
f = lambda x, y: '[(0 {} v, 0 {} v) for v in range(1, 6)]'.format(x, y)
VECTOR = [
# TODO X轴
,
# TODO Y轴
,
# TODO 左斜轴
,
# TODO 右斜轴
,
]
# TODO 身份标识符,电脑落子代表1 玩家落子代表2
COMPUTER = "1"
PLAYER = "2"
# TODO 分支8,搜索深度 5,博弈树节点
BRANCH = 8
DEPTH = 3
class ChessType:
"""
(1) 如果有一个方向已经成5连, 其分值记为5000;
(2) 未出现 (1) , 有一个方向已经成活4, 其分值记为1000;
(3) 未出现 (1) 、 (2) , 有两个方向出现单4, 其分值记为1000;
(4) 未出现 (1) 、 (2) 、 (3) , 有两个方向出现单4和活3, 其分值记为998;
(5) 未出现 (1) 、 (2) 、 (3) 、 (4) , 有两个方向成活3, 其分值记为954;
(6) 未出现上述情况, 将4个方向中单子分值最大的2个值相加作为其分值。
己方单4 73
对方单4 84
己方活3 67
对方活3 79
单3 16
活2 13
单2 7
活1 3
单1 1
"""
ScoreRules = {
# TODO 得分:棋型List
#如果有新的棋型,在此添加,范围仅限,单活1-4棋型
#TODO眠一型
1: ["0{0}{1}", "{1}{0}0"],
#TODO活一型
3: ["0{0}0"],
#TODO眠二型
7: ["0{0}{0}{1}", "0{0}0{0}{1}", "0{0}00{0}{1}", "{0}000{0}"],
#TODO活二型
13: ["0{0}{0}0", "0{0}0{0}0", "{0}00{0}", "0{0}{0}0"],
#TODO眠三型
16: [
"0{0}{0}{0}{1}", "{1}{0}{0}{0}0", "0{0}0{0}{0}{1}",
"{1}{0}0{0}{0}0", "0{0}{0}0{0}{1}", "{1}{0}{0}0{0}0",
"{1}0{0}{0}{0}0{1}", "{0}00{0}{0}", "{0}0{0}0{0}"
],
#TODO活三型
67: ["0{0}{0}{0}0", "0{0}0{0}{0}0", "0{0}{0}0{0}0"],
#TODO眠四型
73: ["0{0}{0}{0}{0}{1}", "{0}0{0}{0}{0}", "{0}{0}0{0}{0}", "{0}{0}{0}0{0}", "{1}{0}{0}{0}{0}0"],
#TODO活四型
1000: ["0{0}{0}{0}{0}0"],
#TODO连五型
5000: ["{0}{0}{0}{0}{0}"]
}
LIVE_THREE: int = 67
RUSH_FOUR: int = 73
# TODO 对方活3及单四评分
PLAYER_LIVE_THREE: int = 79
PLAYER_RUSH_FOUR: int = 84
# TODO 异常棋型得分,双活三,单四活三,双单四,
DOUBLE_LIVE_THREE: int = 954
RUSH_FOUR_LIVE_THREE: int = 998
DOUBLE_RUSH_FOUR: int = 1000
@staticmethod
def score(scores) -> int:
"""返回最终评分
如果有新的异常棋型得分,在此修改。
scores->List是一个落子的四个方向的棋型得分
"""
value = scores + scores
# TODO 连五棋型 5000分
if value >= 5000:
return 5000
# TODO 双冲四棋型 1000分
if value >= ChessType.RUSH_FOUR * 2:
return ChessType.DOUBLE_RUSH_FOUR
# TODO 活四+活三棋型 998分
elif value >= ChessType.RUSH_FOUR + ChessType.LIVE_THREE:
return ChessType.RUSH_FOUR_LIVE_THREE
# TODO 双活三棋型 954分
elif value >= ChessType.LIVE_THREE * 2:
return ChessType.DOUBLE_LIVE_THREE
# TODO 以上都不符
return value 顶楼!{:10_256:} 学习 厉害{:5_106:}
页:
[1]