|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
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[x,y] = Computer or Player
::data -> 五子棋的数据储存
::one_spot -> 待搜索的点
所有已落子的八个方向,延伸5个点位。不包括已落子和超出边界的子
def _result(self, p) -> List[str]:
获取 p 点四个方向(xy轴及左右斜轴)的落子数据,拼接成字符串返回
def get_xy_score(self, spot:List[int], flag: str) -> int:
获取 spot 落点的具体评分
def update_one(self, p: List[int]) -> Set[tuple]:
获得p落点周围八个方位总计40个点,排除已落子和超出边界的点
def evaluation(self) -> Tuple[int, Point]:
对当前局面进行评价,获得敌我双方最大的收益点。
"""
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[0] < SIZE and -1 < p[1] < 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[int, int] -> [x, y] 落点的坐标
:return: 落点的数据 '0'=空白 '1' or '2' 已经落子
"""
if -1 < key[0] < SIZE and -1 < key[1] < SIZE:
return self.data[self.key(*key)]
return ""
def __setitem__(self, key: Point, value: str):
"""
:param key: Set(int, int) -> [x, y] 落点的坐标
:param value: 落点的数据 1 or 2 玩家或者电脑AI
:return: None
"""
self.data[self.key(*key)] = value
def __contains__(self, key):
"""
:param key: List[int, int] -> [x, y] 落点的坐标
:return: bool ->判断当前点是否可以落子
"""
return self[key] == "0"
def _result(self, p: Point) -> List[str]:
"""
:param p: 五子棋落点, 拼接效果:x->x->p->x->x
:return:四个方向的棋型, List[str]
"""
def count(x, y): return [p[0] + x, p[1] + y]
def func(iterator):
return "".join([self[count(*e)] for e in iterator])
return [func(v1)[::-1] + self[p] + func(v2) for v1, v2 in VECTOR]
def get_xy_score(self, spot: Point, flag: str) -> int:
"""
:param spot:
:param flag:身份标识
:return:四个方向的棋型的评分列表
"""
self[spot] = flag
chess_type = self._result(spot)
self[spot] = "0"
_flag = [COMPUTER, PLAYER][flag == COMPUTER]
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([get(chess) for chess in chess_type], reverse=True)
return ChessType.score(scores)
def update_one(self, p: Point) -> Set[Tuple]:
"""
获取落点周围八个方位总计40个点,排除超出边界的点
:param p: 落点点
:return: set[tuple] -> ((x, y),(x1, y1)...)
"""
self.one_spot.discard(p)
# TODO 对p点计算位移点。
def count(x, y): return p[0] + x, p[1] + y
# TODO 对VECTOR展开,转化一维列表,通过count函数计算位移后的点
vector = [count(*each) for each in chain.from_iterable(list(chain.from_iterable(VECTOR)))]
r = set(filter(self.f, vector))
return r
def _evaluation(self, computer_scores, player_scores):
"""对相同收益进行比较
例如对手出现活三,我们有两个点可以堵截,有一个点既能堵截,又能对我们形成收益。
"""
score, move = player_scores[0]
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[int, tuple]:
"""
对当前棋盘局面进行评估
:return: Tuple[int, 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[0], reverse=True)
player_scores = sorted(player_scores, key=lambda p: p[0])
# TODO 如果电脑收益大于玩家,直接返回电脑得分点
if computer_scores[0][0] > abs(player_scores[0][0]):
return computer_scores[0]
# TODO 如果电脑收益小于于玩家,进行收益进行比较
res = self._evaluation(computer_scores, player_scores)
if res:
return res
# TODO 返回玩家收益点
return player_scores[0]
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[spot] = 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[x]+" " + 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[r[0]], self.yingshe[r[1]]
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[r[1]] = COMPUTER
bb = self.update_one(r[1])
self.one_spot |= bb
next_move = PLAYER
self.computer_order.append(r[1])
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轴
[eval(f("-", "*")), eval(f("+", "*"))],
# TODO Y轴
[eval(f("*", "+")), eval(f("*", "-"))],
# TODO 左斜轴
[eval(f("-", "+")), eval(f("+", "-"))],
# TODO 右斜轴
[eval(f("-", "-")), eval(f("+", "+"))],
]
# 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 得分[int]:棋型List[str]
# 如果有新的棋型,在此添加,范围仅限,单活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[int]是一个落子的四个方向的棋型得分
"""
value = scores[0] + scores[1]
# 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
|
评分
-
查看全部评分
|