成果展示
首先展示一下程序的成果,最后的画面会是这个模样:

。。。右边的两颗一黑一白的棋子比较调皮,它会动。
主要功能
主要实现了人机对弈,机机对弈(手动改几行注释就可以了),利用Pygame绘制游戏界面,运用了状态机, 估值函数。 由于状态机的运用,使得所有对象都可以被一个对象管理,方便了程序的扩展,只需要添加状态和创建实体类,就可以增加新的模块与功能。
程序的架构
这个程序文件包括了主要有以下类:
包含14个类与一个运行函数
———————————————————————————
重要的类
按理来说,只要具备了以下四个类,就可以按照这个框架写出所有游戏。。。
只是我说说而已,有待百度。
状态类
该类是虚类,实体的状态类需要继承它
状态 <- 是实体类的成员,该对象描述某个具体的实体类状态,以及在该状态下会做些什么,做之前要有什么准备工作,做完了怎么退出动作,怎么改变状态。比如一个智能棋手有一个下棋状态,、、、
# 状态
class State(object):
def __init__(self, name):
self.name = name
# 执行动作
def do_actions(self):
pass
# 检测外界条件
def check_conditions(self):
pass
# 进入动作
def entry_actions(self):
pass
# 退出动作
def exit_actions(self):
pass
状态机类
该类对状态进行统一管理
状态机 <- 是实体类的成员,它是实体类的大脑,管理着实体类的所有状态,以及状态间的转换(哪个状态是激活态)
# 状态机
class StateMachine(object):
def __init__(self):
self.states = {}
self.active_state = None
# 添加状态
def add_state(self, state):
self.states[state.name] = state
# 决策(思考)
def think(self):
if self.active_state is None:
return
self.active_state.do_actions()
new_state_name = self.active_state.check_conditions()
if new_state_name is not None and new_state_name is not self.active_state:
self.set_state(new_state_name)
# 设置活动状态
def set_state(self, new_state_name):
if self.active_state is not None:
self.active_state.exit_actions()
self.active_state = self.states[new_state_name]
self.active_state.entry_actions()
世界类
管理所有实体类对象
世界 <- 所有对象由它来处理,实体对象必须被它管理才能发挥作用。。。
# 世界类
class World(object):
def __init__(self):
self.entities = {}
self.entity_id = 0
self.update_time = 0
self.is_over = False # 游戏结束
self.playing = False #游戏正在进行
# 下面两句本不应该出现在这里,应该是出现在某个实体类中的,。。。。
# 谁叫我比较懒。。。。。破坏了代码的美感,应该干掉,,,谁有兴趣就做做吧,,
self.background = pygame.surface.Surface(SCREEN_SIZE).convert()
self.background.blit(pygame.image.load(CHESS_BOX).convert_alpha(), (0, 0))
# 添加实体
def add_entity(self, entity):
self.entities[self.entity_id] = entity
entity.id = self.entity_id
self.entity_id += 1
# 移除实体
def remove_entity(self, entity):
del self.entities[entity.id]
# 获取实体
def get(self, entity_id):
if entity_id in self.entities:
return self.entities[entity_id]
else:
return None
# 处理各个实体
def process(self, time_passed):
time_passed_seconds = time_passed / 1000.0
for entity in list(self.entities.values()):
entity.process(time_passed_seconds)
# 绘图
def render(self, surface, current_time):
if self.update_time < current_time:
surface.blit(self.background, (0, 0))
for entity in list(self.entities.values()):
entity.render(surface)
self.update_time = current_time + 30
# 得到接近的实体
def get_close_entity(self, name, location, range=10.):
location = Vector2(*location)
for entity in list(self.entities.values()):
if entity.name == name:
distance = location.get_distance_to(entity.location)
if distance < range:
return entity
return None
游戏实体类
该类需要被其它具有实际意义的实体类继承
实体类 <- 描述了一个实体,比如:一个智能棋手类,一个棋子, ,,,
# 游戏实体类
class GameEntity(object):
def __init__(self, world, name, image=None):
self.world = world
self.name = name
self.image = image
self.location = Vector2(0, 0)
self.destination = Vector2(0, 0)
self.speed = 0.
self.brain = StateMachine()
self.id = 0
# 绘图
def render(self, surface):
if self.image is None:
return
x, y = self.location
w, h = self.image.get_size()
surface.blit(self.image, (x-w/2, y-h/2))
# 处理实体对象
def process(self, time_passed):
self.brain.think()
if self.speed > 0. and self.location != self.destination:
vec_to_destination = self.destination - self.location
distance_to_destination = vec_to_destination.get_length()
heading = vec_to_destination.get_normalized()
travel_distance = min(distance_to_destination, time_passed * self.speed)
self.location += travel_distance * heading
———————————————————————————
不那么重要的类,但对于这个五子棋来说,就是核心类
这里放一个大概,后面会给出全部代码
棋子类
GameEntity的子类
# 棋子类
class Chessman(GameEntity):
def __init__(self, world, image):
GameEntity.__init__(self, world, "Chessman", image)
棋盘局势类
棋盘局势 <- 整个棋盘的局势走向
# 棋盘局势
class ChessBoxSituations(object):
def __init__(self, size=(19, 19)):
self.size = size
self.situations = {} #棋盘局势(棋子在棋盘的点集)
self.last_drop_position = (0, 0)
self.current_drop_player = 0
self.count = 0
# 局势发展(棋子增加)
def evolve(self, chessman_point, owner):
self.situations[chessman_point] = owner
self.last_drop_position = chessman_point
self.count += 1
# 获取棋盘某点坐标的状态
def get_state(self, point):
px, py = point
x, y = self.size
if x - px < 0 or y - py < 0 or x - px >= 19 or y - py >= 19:
return self.OutOfRange()
if point not in self.situations:
return None
return self.situations[point]
class OutOfRange(object):
pass
智能体棋手类
对战的AI
附:
# 八个方向 N:北, E:东, S:南, W:西
D_N = Vector2(0, -1)
D_EN = Vector2(1, -1)
D_E = Vector2(1, 0)
D_ES = Vector2(1, 1)
D_S = Vector2(0, 1)
D_WS = Vector2(-1, 1)
D_W = Vector2(-1, 0)
D_WN = Vector2(-1, -1)
DIRECTION = (D_N, D_EN, D_E, D_ES, D_S, D_WS, D_W, D_WN)
# 智能体棋手类
class PlayerAgent(GameEntity):
def __init__(self, world, box_situations, chess_image, no=1, opponent=0):
GameEntity.__init__(self, world, "PlayerAgent")
self.no = no #自己
self.opponent = opponent #对手
self.box_situations = box_situations
self.chess_image = chess_image
self.drop_points = [] # 可选的将要下的棋子位点
self.points_weight = 0 # drop_points里的点的权重(里面所有点权重相同)
wait_state = PlayerAgentStateWait(self)
drop_state = PlayerAgentStateDrop(self)
self.brain.add_state(drop_state)
self.brain.add_state(wait_state)
# 更新下棋点集
def update_drop_points(self, location, weight):
if self.points_weight < weight:
self.drop_points.clear()
self.drop_points.append(location)
self.points_weight = weight
elif self.points_weight == weight:
self.drop_points.append(location)
# 决策(获取下棋位置)
def decision_making(self):
self.points_weight = 0
self.drop_points.clear()
filter_point = []
# 棋盘上所有的有棋子的点
for point in self.box_situations.situations:
# 获取该点的相邻八个点的空位点
for DIR in DIRECTION:
cur_point = point+DIR
x, y = cur_point
if self.box_situations.get_state((x, y)) is None:
if cur_point in filter_point:
continue
weight = self.evaluate(cur_point, self.no) + self.evaluate(cur_point, self.opponent)
self.update_drop_points((x, y), weight)
filter_point.append(cur_point)
print("权重:", self.points_weight)
print(self.drop_points)
# 棋盘为空,棋盘为满
if len(self.drop_points) == 0:
return 10, 10
return self.drop_points[randint(0, len(self.drop_points) - 1)]
# 估值
def evaluate(self, point, no):
value = 0
#......
#此处省略n行代码
#......
return value
# 下棋
def drop(self, point):
index_x, index_y = point
destination_x = index_x * 30.25
destination_y = index_y * 30.2 - 15
# 棋盘上该点没有棋子
if (index_x, index_y) not in self.box_situations.situations:
chess = Chessman(self.world, self.chess_image)
chess.location = (destination_x, destination_y)
self.world.add_entity(chess)
self.box_situations.evolve(point, self.no)
self.box_situations.current_drop_player = self.opponent
emmm、、、、、
规则设计的一个简单的智能体,主要在对局中对局部点价值的判断,然后综合起来后选择权重最高的点来下。
如何判断一个点的权重:evaluate()
在棋盘上,一个点有八个方向,横竖左斜右斜。下棋时候,就会先估算在这八个方向上的总价值是多少。
栗子(没有看错,也没有写错,就是栗子):点一在这里可以形成五子,那么价值就是300000,点二只能形成活四,那么这个点的价值就是3000。所以电脑在判断完所有的点后,会选出一个价值最大的点下。
五子棋的一些术语:活四,活三,活二,死四,死三,死二
附:

智能体等待状态类
继承State类
# 智能体等待状态
class PlayerAgentStateWait(State):
def __init__(self, player_agent):
State.__init__(self, "PlayerAgentWait")
self.player_agent = player_agent
def check_conditions(self):
if self.player_agent.world.is_over:
return None
if self.player_agent.world.playing and \
self.player_agent.box_situations.current_drop_player == self.player_agent.no:
return "PlayerAgentDrop"
return None
智能体下棋状态类
继承State类
# 智能体下棋状态
class PlayerAgentStateDrop(State):
def __init__(self, player_agent):
State.__init__(self, "PlayerAgentDrop")
self.player_agent = player_agent
self.drop_point = None
def do_actions(self):
if self.drop_point is not None:
self.player_agent.drop(self.drop_point)
def check_conditions(self):
if self.player_agent.box_situations.current_drop_player == self.player_agent.opponent:
return "PlayerAgentWait"
return None
def entry_actions(self):
self.DecisionMakingThread(self).start()
class DecisionMakingThread(threading.Thread):
def __init__(self, owner):
threading.Thread.__init__(self)
self.owner = owner
def run(self):
self.owner.drop_point = self.owner.player_agent.decision_making()
def exit_actions(self):
self.drop_point = None
玩家类
它超脱于世界了~ ) _ )~
还有,感觉有点重复了。。。。
# 玩家
class Player(object):
def __init__(self, world, box_situations, chess_image, no=0, opponent=1):
self.world = world
self.box_situations = box_situations
self.chess_image = chess_image
self.no = no
self.opponent = opponent
# 下棋
def drop(self, location):
# 同上智能体类
裁判员类
胜负在手,天下我有
注:被托管的可怜娃
# 裁判员
class Adjudicator(GameEntity):
def __init__(self, world, box_situations):
GameEntity.__init__(self, world, "Adjudicator")
self.box_situations = box_situations
self.win_image = pygame.image.load(WIN).convert()
self.lose_image = pygame.image.load(LOSE).convert()
self.winner = None # 赢家
self.current_time = 0
self.update_time = -1
def process(self, time_passed):
# 游戏结束
if self.world.is_over:
# 游戏结束,等待3秒显示结果
if self.update_time > self.current_time:
self.current_time += time_passed
return
# 移除棋盘的棋子, 清空局势
for entity in list(self.world.entities.values()):
if entity.name is "Chessman":
self.world.remove_entity(entity)
self.box_situations.situations.clear()
self.box_situations.count = 0
if self.winner == 0:
self.image = self.win_image
elif self.winner == 1:
self.image = self.lose_image
if self.winner is not None:
w, h = SCREEN_SIZE
self.location = (w / 3, h / 2)
self.winner = None
else:
# 是否有胜负
winner = self.win_or_lose()
if winner is not None or self.box_situations.count == 361:
self.world.is_over = True
self.winner = winner
# 得出胜负,在3秒后更新显示胜负视图
self.update_time += 3
else:
# 没有胜负不显示胜负视图
self.image = None
def win_or_lose(self):
#....
次不那么重要的类
状态机类
感觉又重复了、、、嘛,没办法,就算重复3次也是可以原谅的不是?
*不可原谅 *
# 游戏右侧边栏
class Sidebar(GameEntity):
def __init__(self, world):
GameEntity.__init__(self, world, "Sidebar")
x, y = SCREEN_SIZE
w, h = CHESS_BOX_SIZE
self.components = {} #组件
self.component_id = 0
self.background = pygame.surface.Surface((x - w, y)).convert()
self.background.fill((251, 251, 251))
self.background.blit(pygame.image.load(CHESS_BG).convert_alpha(), (20, h * 1 / 8))
# 添加组件
def add_component(self, game_entity: "GameEntity"):
# 移除组件
def remove_entity(self, game_entity: "GameEntity"):
del self.components[game_entity.id]
# 获取组件
def get(self, component_id):
# 处理各个实体
def process(self, time_passed):
# 绘图
def render(self, surface):
按钮类
只是一个按钮 - 兼顾播放背景音乐 - 没办法,这代码写的有点烂。而且还没有注释,。。。。我都这么诚实了,就算看不懂也是可以被原谅的吧。。。以上,以下。
# 按钮类
class Button(GameEntity):
def __init__(self, world, image, position):
GameEntity.__init__(self, world, "Button", image)
self.position = position
self.update_time = 0
self.current_time = 0
self.click = False
self.bgm = pygame.mixer.music.load(BGM)
pygame.mixer.music.play(999)
pygame.mixer.music.pause()
self.bgm_is_play = False
x, y = position
w, h = CHESS_BOX_SIZE
self.location = (x + w, y)
def process(self, time_passed):
# 检查是否被点击
def check_click(self):
# 鼠标悬停
def is_over(self, point):
会移动的棋子类
一个小玩意, 没什么用
# 会移动的棋子
class MoveChessman(GameEntity):
def __init__(self, world, image, location, move_dir):
GameEntity.__init__(self, world, "MoveChessman", image)
self.location = Vector2(location[0], location[1])
self.dir = move_dir
self.speed = 100.
def process(self, time_passed):
self.destination = self.location + 100*self.dir
w, h = CHESS_BOX_SIZE
x, y = self.location
if x > w+285:
self.location[0] = w+285
self.dir = D_S
elif x < w+15:
self.location[0] = w+15
self.dir = D_N
elif y > h-15:
self.location[1] = h-15
self.dir = D_W
elif y < 15:
self.location[1] = 15
self.dir = D_E
super().process(time_passed)
完整代码
这个代码写的不够漂亮,没有用太高级的语法,有些地方甚至都没有注释天哪……
下面给出完整代码(注意需要gameobjects库才可以运行)
# 屏幕大小
SCREEN_SIZE = (905, 570)
# 棋盘大小
CHESS_BOX_SIZE = (605, 570)
# 需要加载的图片
MOUSE_CURSOR = "mousecursor.png"
BUTTON = "button.png"
CHESS_BG = "bg.png"
CHESS_BOX = "ChessBox.jpg"
CHESSMAN = "chessman.png"
WIN = "win.png"
LOSE = "lose.png"
BGM = "BMG.MP3"
from random import randint
import threading
import pygame
from pygame.locals import *
from gameobjects.vector2 import Vector2
# 八个方向 N:北, E:东, S:南, W:西
D_N = Vector2(0, -1)
D_EN = Vector2(1, -1)
D_E = Vector2(1, 0)
D_ES = Vector2(1, 1)
D_S = Vector2(0, 1)
D_WS = Vector2(-1, 1)
D_W = Vector2(-1, 0)
D_WN = Vector2(-1, -1)
DIRECTION = (D_N, D_EN, D_E, D_ES, D_S, D_WS, D_W, D_WN)
# 状态
class State(object):
def __init__(self, name):
self.name = name
# 执行动作
def do_actions(self):
pass
# 检测外界条件
def check_conditions(self):
pass
# 进入动作
def entry_actions(self):
pass
# 退出动作
def exit_actions(self):
pass
# 状态机
class StateMachine(object):
def __init__(self):
self.states = {}
self.active_state = None
# 添加状态
def add_state(self, state):
self.states[state.name] = state
# 决策
def think(self):
if self.active_state is None:
return
self.active_state.do_actions()
new_state_name = self.active_state.check_conditions()
if new_state_name is not None and new_state_name is not self.active_state:
self.set_state(new_state_name)
# 设置活动状态
def set_state(self, new_state_name):
if self.active_state is not None:
self.active_state.exit_actions()
self.active_state = self.states[new_state_name]
self.active_state.entry_actions()
# 世界类
class World(object):
def __init__(self):
self.entities = {}
self.entity_id = 0
self.update_time = 0
self.is_over = False
self.playing = False
self.background = pygame.surface.Surface(SCREEN_SIZE).convert()
self.background.blit(pygame.image.load(CHESS_BOX).convert_alpha(), (0, 0))
# 添加实体
def add_entity(self, entity):
self.entities[self.entity_id] = entity
entity.id = self.entity_id
self.entity_id += 1
# 移除实体
def remove_entity(self, entity):
del self.entities[entity.id]
# 获取实体
def get(self, entity_id):
if entity_id in self.entities:
return self.entities[entity_id]
else:
return None
# 处理各个实体
def process(self, time_passed):
time_passed_seconds = time_passed / 1000.0
for entity in list(self.entities.values()):
entity.process(time_passed_seconds)
# 绘图
def render(self, surface, current_time):
if self.update_time < current_time:
surface.blit(self.background, (0, 0))
for entity in list(self.entities.values()):
entity.render(surface)
self.update_time = current_time + 30
# 得到接近的实体
def get_close_entity(self, name, location, range=10.):
location = Vector2(*location)
for entity in list(self.entities.values()):
if entity.name == name:
distance = location.get_distance_to(entity.location)
if distance < range:
return entity
return None
# 游戏实体类
class GameEntity(object):
def __init__(self, world, name, image=None):
self.world = world
self.name = name
self.image = image
self.location = Vector2(0, 0)
self.destination = Vector2(0, 0)
self.speed = 0.
self.brain = StateMachine()
self.id = 0
# 绘图
def render(self, surface):
if self.image is None:
return
x, y = self.location
w, h = self.image.get_size()
surface.blit(self.image, (x-w/2, y-h/2))
# 处理实体对象
def process(self, time_passed):
self.brain.think()
if self.speed > 0. and self.location != self.destination:
vec_to_destination = self.destination - self.location
distance_to_destination = vec_to_destination.get_length()
heading = vec_to_destination.get_normalized()
travel_distance = min(distance_to_destination, time_passed * self.speed)
self.location += travel_distance * heading
# 棋子类
class Chessman(GameEntity):
def __init__(self, world, image):
GameEntity.__init__(self, world, "Chessman", image)
# 棋盘局势
class ChessBoxSituations(object):
def __init__(self, size=(19, 19)):
self.size = size
self.situations = {}
self.last_drop_position = (0, 0)
self.current_drop_player = 0
self.count = 0
# 局势发展(棋子增加)
def evolve(self, chessman_point, owner):
self.situations[chessman_point] = owner
self.last_drop_position = chessman_point
self.count += 1
# 获取棋盘某点坐标的状态
def get_state(self, point):
px, py = point
x, y = self.size
if x - px < 0 or y - py < 0 or x - px >= 19 or y - py >= 19:
return self.OutOfRange()
if point not in self.situations:
return None
return self.situations[point]
class OutOfRange(object):
pass
# 智能体棋手类
class PlayerAgent(GameEntity):
def __init__(self, world, box_situations, chess_image, no=1, opponent=0):
GameEntity.__init__(self, world, "PlayerAgent")
self.no = no
self.opponent = opponent
self.box_situations = box_situations
self.chess_image = chess_image
self.drop_points = []
self.points_weight = 0
wait_state = PlayerAgentStateWait(self)
drop_state = PlayerAgentStateDrop(self)
self.brain.add_state(drop_state)
self.brain.add_state(wait_state)
# 更新下棋点集
def update_drop_points(self, location, weight):
if self.points_weight < weight:
self.drop_points.clear()
self.drop_points.append(location)
self.points_weight = weight
elif self.points_weight == weight:
self.drop_points.append(location)
# 决策(获取下棋位置)
def decision_making(self):
self.points_weight = 0
self.drop_points.clear()
filter_point = []
# 棋盘上所有的有棋子的点
for point in self.box_situations.situations:
# 获取该点的相邻八个点的空位点
for DIR in DIRECTION:
cur_point = point+DIR
x, y = cur_point
if self.box_situations.get_state((x, y)) is None:
if cur_point in filter_point:
continue
weight = self.evaluate(cur_point, self.no) + self.evaluate(cur_point, self.opponent)
self.update_drop_points((x, y), weight)
filter_point.append(cur_point)
print("权重:", self.points_weight)
print(self.drop_points)
# 棋盘为空,棋盘为满
if len(self.drop_points) == 0:
return 10, 10
return self.drop_points[randint(0, len(self.drop_points) - 1)]
# 估值
def evaluate(self, point, no):
value = 0
# 八个方向
for DIR in DIRECTION:
# 在当前point的某个方向的直线相邻点(dec:负方向, add:正方向)
dec_1 = tuple(point + DIR*-1)
dec_2 = tuple(point + DIR*-2)
dec_3 = tuple(point + DIR*-3)
dec_4 = tuple(point + DIR*-4)
dec_5 = tuple(point + DIR*-5)
add_1 = tuple(point + DIR*1)
add_2 = tuple(point + DIR*2)
add_3 = tuple(point + DIR*3)
add_4 = tuple(point + DIR*4)
add_5 = tuple(point + DIR*5)
# 相邻点的状态
s_dec_1 = self.box_situations.get_state(dec_1)
s_dec_2 = self.box_situations.get_state(dec_2)
s_dec_3 = self.box_situations.get_state(dec_3)
s_dec_4 = self.box_situations.get_state(dec_4)
s_dec_5 = self.box_situations.get_state(dec_5)
s_add_1 = self.box_situations.get_state(add_1)
s_add_2 = self.box_situations.get_state(add_2)
s_add_3 = self.box_situations.get_state(add_3)
s_add_4 = self.box_situations.get_state(add_4)
s_add_5 = self.box_situations.get_state(add_5)
# 4子 1111_, _1111, 11_11, 111_1, 1_111 === 1:我方, 0:敌方, _:当前空位, -:其他空位
if s_dec_1 == s_dec_2 == s_dec_3 == s_dec_4 == no or\
s_add_1 == s_add_2 == s_add_3 == s_add_4 == no or\
s_dec_1 == s_dec_2 == s_add_1 == s_add_2 == no or\
s_dec_1 == s_dec_2 == s_dec_3 == s_add_1 == no or\
s_dec_1 == s_add_1 == s_add_2 == s_add_3 == no:
value += 300000
if no != self.no:
value -= 50000
# 活3A -111_-, -_111-, -1_11-,-11_1-,
elif (s_dec_1 == s_dec_2 == s_dec_3 == no and s_dec_4 is s_add_1 is None) or\
(s_add_1 == s_add_2 == s_add_3 == no and s_dec_1 is s_add_4 is None) or\
(s_dec_1 == s_add_1 == s_add_2 == no and s_dec_2 is s_add_3 is None) or\
(s_dec_1 == s_dec_2 == s_add_1 == no and s_dec_3 is s_add_2 is None):
value += 3000
if no != self.no:
value -= 500
# 活3B -111-_, _-111-
elif (s_dec_2 == s_dec_3 == s_dec_4 == no and s_dec_1 is s_dec_5 is None) or \
(s_add_2 == s_add_3 == s_add_4 == no and s_add_1 is s_add_5 is None):
value += 2000
if no != self.no:
value -= 500
# 死3A _1-11-, -1-11_, _11-1-, -11-1_
elif (s_add_1 == s_add_3 == s_add_4 == no and s_add_2 is s_add_5 is None) or\
(s_dec_1 == s_dec_2 == s_dec_4 == no and s_dec_3 is s_dec_5 is None) or\
(s_add_1 == s_add_2 == s_add_4 == no and s_add_3 is s_add_5 is None) or\
(s_dec_1 == s_dec_3 == s_dec_4 == no and s_dec_2 is s_dec_5 is None):
value += 1600
if no != self.no:
value -= 500
# 死3B _-111, -_111, 111_-, 111-_
elif (s_add_2 == s_add_3 == s_add_4 == no and s_add_1 is None) or \
(s_add_1 == s_add_2 == s_add_3 == no and s_dec_1 is None) or \
(s_dec_1 == s_dec_2 == s_dec_3 == no and s_add_1 is None) or \
(s_dec_2 == s_dec_3 == s_dec_4 == no and s_dec_1 is None):
value += 800
if no != self.no:
value -= 200
# 死3C 1_-11, 1-_11, 1_1-1, 1-1_1
elif (s_dec_1 == s_add_2 == s_add_3 == no and s_add_1 is None) or \
(s_dec_2 == s_add_1 == s_add_2 == no and s_dec_1 is None) or \
(s_dec_1 == s_add_1 == s_add_3 == no and s_add_2 is None) or \
(s_dec_1 == s_dec_3 == s_add_1 == no and s_dec_2 is None):
value += 600
if no != self.no:
value -= 100
# 活二 --_11--, --11_--
elif (s_add_1 == s_add_2 == no and s_dec_1 is s_dec_2 is s_add_3 is s_add_4 is None) or \
(s_dec_1 == s_dec_2 == no and s_dec_3 is s_dec_4 is s_add_1 is s_add_2 is None):
value += 650
if no != self.no:
value -= 100
else:
value += 150
if no != self.no:
value -= 100
return value
# 下棋
def drop(self, point):
index_x, index_y = point
destination_x = index_x * 30.25
destination_y = index_y * 30.2 - 15
if (index_x, index_y) not in self.box_situations.situations:
chess = Chessman(self.world, self.chess_image)
chess.location = (destination_x, destination_y)
self.world.add_entity(chess)
self.box_situations.evolve(point, self.no)
self.box_situations.current_drop_player = self.opponent
# 智能体等待状态
class PlayerAgentStateWait(State):
def __init__(self, player_agent):
State.__init__(self, "PlayerAgentWait")
self.player_agent = player_agent
def check_conditions(self):
if self.player_agent.world.is_over:
return None
if self.player_agent.world.playing and \
self.player_agent.box_situations.current_drop_player == self.player_agent.no:
return "PlayerAgentDrop"
return None
# 智能体下棋状态
class PlayerAgentStateDrop(State):
def __init__(self, player_agent):
State.__init__(self, "PlayerAgentDrop")
self.player_agent = player_agent
self.drop_point = None
def do_actions(self):
if self.drop_point is not None:
self.player_agent.drop(self.drop_point)
def check_conditions(self):
if self.player_agent.box_situations.current_drop_player == self.player_agent.opponent:
return "PlayerAgentWait"
return None
def entry_actions(self):
self.DecisionMakingThread(self).start()
class DecisionMakingThread(threading.Thread):
def __init__(self, owner):
threading.Thread.__init__(self)
self.owner = owner
def run(self):
self.owner.drop_point = self.owner.player_agent.decision_making()
def exit_actions(self):
self.drop_point = None
# 玩家
class Player(object):
def __init__(self, world, box_situations, chess_image, no=0, opponent=1):
self.world = world
self.box_situations = box_situations
self.chess_image = chess_image
self.no = no
self.opponent = opponent
# 下棋
def drop(self, location):
if self.world.is_over or not self.world.playing:
return
index_x, index_y = round(location[0] / 30), round((location[1] + 15) / 30)
destination_x = index_x * 30.25
destination_y = index_y * 30.2 - 15
if (index_x, index_y) not in self.box_situations.situations:
chess = Chessman(self.world, self.chess_image)
chess.location = (destination_x, destination_y)
self.world.add_entity(chess)
self.box_situations.evolve((index_x, index_y), self.no)
self.box_situations.current_drop_player = self.opponent
# 裁判员
class Adjudicator(GameEntity):
def __init__(self, world, box_situations):
GameEntity.__init__(self, world, "Adjudicator")
self.box_situations = box_situations
self.win_image = pygame.image.load(WIN).convert()
self.lose_image = pygame.image.load(LOSE).convert()
self.winner = None
self.current_time = 0
self.update_time = -1
def process(self, time_passed):
# 游戏结束
if self.world.is_over:
# 游戏结束,等待3秒显示结果
if self.update_time > self.current_time:
self.current_time += time_passed
return
# 移除棋盘的棋子, 清空局势
for entity in list(self.world.entities.values()):
if entity.name is "Chessman":
self.world.remove_entity(entity)
self.box_situations.situations.clear()
self.box_situations.count = 0
if self.winner == 0:
self.image = self.win_image
elif self.winner == 1:
self.image = self.lose_image
if self.winner is not None:
w, h = SCREEN_SIZE
self.location = (w / 3, h / 2)
self.winner = None
else:
# 是否有胜负
winner = self.win_or_lose()
if winner is not None or self.box_situations.count == 361:
self.world.is_over = True
self.winner = winner
# 得出胜负,在3秒后更新显示胜负视图
self.update_time += 3
else:
# 没有胜负不显示胜负视图
self.image = None
def win_or_lose(self):
point = self.box_situations.last_drop_position
s_cur = self.box_situations.get_state(point)
# 八个方向
for DIR in DIRECTION:
# 在当前point的某个方向的直线相邻点(dec:负方向, add:正方向)
dec_1 = tuple(point + DIR * -1)
dec_2 = tuple(point + DIR * -2)
add_1 = tuple(point + DIR * 1)
add_2 = tuple(point + DIR * 2)
add_3 = tuple(point + DIR * 3)
add_4 = tuple(point + DIR * 4)
# 相邻点的状态
s_dec_1 = self.box_situations.get_state(dec_1)
s_dec_2 = self.box_situations.get_state(dec_2)
s_add_1 = self.box_situations.get_state(add_1)
s_add_2 = self.box_situations.get_state(add_2)
s_add_3 = self.box_situations.get_state(add_3)
s_add_4 = self.box_situations.get_state(add_4)
# _****, *_***, **_**
if s_cur == s_add_1 == s_add_2 == s_add_3 == s_add_4 or \
s_cur == s_add_1 == s_add_2 == s_add_3 == s_dec_1 or\
s_cur == s_add_1 == s_add_2 == s_dec_1 == s_dec_2:
return s_cur
return None
# 游戏右侧边栏
class Sidebar(GameEntity):
def __init__(self, world):
GameEntity.__init__(self, world, "Sidebar")
x, y = SCREEN_SIZE
w, h = CHESS_BOX_SIZE
self.components = {}
self.component_id = 0
self.background = pygame.surface.Surface((x - w, y)).convert()
self.background.fill((251, 251, 251))
self.background.blit(pygame.image.load(CHESS_BG).convert_alpha(), (20, h * 1 / 8))
# 添加组件
def add_component(self, game_entity: "GameEntity"):
self.components[self.component_id] = game_entity
game_entity.id = self.component_id
self.component_id += 1
# 移除组件
def remove_entity(self, game_entity: "GameEntity"):
del self.components[game_entity.id]
# 获取组件
def get(self, component_id):
if component_id in self.components:
return self.components[component_id]
else:
return None
# 处理各个实体
def process(self, time_passed):
super().process(time_passed)
for component in list(self.components.values()):
component.process(time_passed)
# 绘图
def render(self, surface):
w, h = CHESS_BOX_SIZE
surface.blit(self.background, (w, 0, 300, h))
for component in list(self.components.values()):
component.render(surface)
# 按钮类
class Button(GameEntity):
def __init__(self, world, image, position):
GameEntity.__init__(self, world, "Button", image)
self.position = position
self.update_time = 0
self.current_time = 0
self.click = False
self.bgm = pygame.mixer.music.load(BGM)
pygame.mixer.music.play(999)
pygame.mixer.music.pause()
self.bgm_is_play = False
x, y = position
w, h = CHESS_BOX_SIZE
self.location = (x + w, y)
def process(self, time_passed):
if self.check_click():
if not self.world.world.playing:
self.world.world.playing = True
else:
self.world.world.playing = False
if self.world.world.is_over:
self.world.world.is_over = False
self.world.world.playing = True
elif self.bgm_is_play:
pygame.mixer.music.pause()
self.bgm_is_play = False
print("bgm is pause...")
else:
pygame.mixer.music.unpause()
self.bgm_is_play = True
print("bgm is unpause...")
# 检查是否被点击
def check_click(self):
if self.is_over(pygame.mouse.get_pos()):
click = pygame.mouse.get_pressed()[0]
if self.click is not click:
self.click = click
return self.click
return False
# 鼠标悬停
def is_over(self, point):
point_x, point_y = point
x, y = self.location
w, h = self.image.get_size()
x -= w / 2
y -= h / 2
in_x = x <= point_x < x + w
in_y = y <= point_y < y + h
return in_x and in_y
# 会移动的棋子
class MoveChessman(GameEntity):
def __init__(self, world, image, location, move_dir):
GameEntity.__init__(self, world, "MoveChessman", image)
self.location = Vector2(location[0], location[1])
self.dir = move_dir
self.speed = 100.
def process(self, time_passed):
self.destination = self.location + 100*self.dir
w, h = CHESS_BOX_SIZE
x, y = self.location
if x > w+285:
self.location[0] = w+285
self.dir = D_S
elif x < w+15:
self.location[0] = w+15
self.dir = D_N
elif y > h-15:
self.location[1] = h-15
self.dir = D_W
elif y < 15:
self.location[1] = 15
self.dir = D_E
super().process(time_passed)
def run():
pygame.init()
pygame.display.set_caption('五子棋')
screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)
pygame.mouse.set_visible(False)
mouse_image = pygame.image.load(MOUSE_CURSOR).convert_alpha()
chessman_image = pygame.image.load(CHESSMAN).convert_alpha()
button_image = pygame.image.load(BUTTON).convert_alpha()
w, h = chessman_image.get_size()
black_chessman_image = pygame.transform.smoothscale(chessman_image.subsurface((15, 180, w/2 - 45, 160)), (30, 30))
white_chessman_image = pygame.transform.smoothscale(chessman_image.subsurface((w/2 + 35, 180, w/2 - 45, 160)), (30, 30))
#创建世界实体
world = World()
#创建游戏侧栏
sidebar = Sidebar(world)
w, h = sidebar.background.get_size()
#创建按钮
button = Button(sidebar, button_image, (w/2, 13*h/16))
#将按钮交给sidebar管理
sidebar.add_component(button)
#将sidebar交给world管理
world.add_entity(sidebar)
#下同
w, h = CHESS_BOX_SIZE
black_move_chessman = MoveChessman(world, black_chessman_image, (w+15, 15), D_E)
white_move_chessman = MoveChessman(world, white_chessman_image, (w+285, h - 15), D_W)
world.add_entity(black_move_chessman)
world.add_entity(white_move_chessman)
chess_box_situations = ChessBoxSituations()
player_agent_a = PlayerAgent(world, chess_box_situations, black_chessman_image)
player_agent_b = PlayerAgent(world, chess_box_situations, white_chessman_image, 0, 1) # 机机对战打开这行,同时注释人机对战的两行
player_agent_a.brain.set_state("PlayerAgentWait")
player_agent_b.brain.set_state("PlayerAgentDrop") # 机机对战打开这行,同时注释人机对战的两行
world.add_entity(player_agent_a)
world.add_entity(player_agent_b) # 机机对战打开这行,同时注释人机对战的两行
adjudicator = Adjudicator(world, chess_box_situations)
world.add_entity(adjudicator)
player_1 = Player(world, chess_box_situations, white_chessman_image) # 人机对战打开这行,同时注释机机对战的三行
clock = pygame.time.Clock()
screen.fill((255, 255, 255))
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
if event.type == MOUSEBUTTONDOWN:
location = event.pos
if 16 < location[0] < 580:
if chess_box_situations.current_drop_player == 0:
player_1.drop(location) # 人机对战打开这行,同时注释机机对战的三行
pass
time_passed = clock.tick(30)
current_time = pygame.time.get_ticks()
world.process(time_passed)
world.render(screen, current_time)
mouse_pos = pygame.mouse.get_pos()
screen.blit(mouse_image, mouse_pos)
pygame.display.update()
if __name__ == "__main__":
run()
附 :
本次使用的资源:
度盘连接:five_chess.zip
本文介绍了使用pygame库开发的五子棋游戏,包括人机对弈和机机对弈功能。通过状态机设计,程序实现了游戏对象的有效管理,同时采用估值函数评估棋盘局势。主要涉及状态类、状态机类、世界类和游戏实体类等关键组件。文章还展示了部分核心代码和资源链接。
5067

被折叠的 条评论
为什么被折叠?



