使用pygame实现的五子棋,状态机, 估值函数

本文介绍了使用pygame库开发的五子棋游戏,包括人机对弈和机机对弈功能。通过状态机设计,程序实现了游戏对象的有效管理,同时采用估值函数评估棋盘局势。主要涉及状态类、状态机类、世界类和游戏实体类等关键组件。文章还展示了部分核心代码和资源链接。
成果展示

首先展示一下程序的成果,最后的画面会是这个模样:
Alt
。。。右边的两颗一黑一白的棋子比较调皮,它会动。


主要功能

主要实现了人机对弈,机机对弈(手动改几行注释就可以了),利用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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值