Python写五子棋游戏 (附完整源代码)

开端

开端

画棋盘

首先肯定是要画出棋盘来,用 pygame 画出一个 19 × 19 或 15 × 15 的棋盘并不是什么难事,这在之前的文章中已经多次用到,就不赘述了。

画棋子

需要说一下的是画棋子,因为没找到什么合适的棋子图片,所以只要自己来画棋子。
我们用 pygame.draw.circle 画出来的圆形是这样的:

很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。

很多已经做案例的人,却不知道如何去学习更加高深的知识。

那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!

QQ群:701698587


锯齿状十分明显,pygame.draw 中有画抗锯齿直线的函数 aaline,但是并没有 aacircle 这样的函数来画一个抗锯齿的圆。

这里就需要用到 pygame.gfxdraw 啦。pygame.gfxdraw 目前还仅是实验版本,这意味着这个 API 可能会在以后的 pygame 版本中发生变化或消失。

要绘制抗锯齿和填充形状,请首先使用函数的aa *版本,然后使用填充版本。例如:

扫描二维码关注公众号,回复: 12971380 查看本文章

图片

col = (255, 0, 0)
surf.fill((255, 255, 255))
pygame.gfxdraw.aacircle(surf, x, y, 30, col)
pygame.gfxdraw.filled_circle(surf, x, y, 30, col)

我们用这个方法在棋盘上画一个棋子试试看。

可以看到效果已明显改善。

落子

落子需要判断鼠标事件,当鼠标左键点击,获取鼠标点击的位置,然后根据棋盘的位置,计算出棋子落在棋盘的位置。

    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit()
            elif event.type == MOUSEBUTTONDOWN:
                pressed_array = pygame.mouse.get_pressed()
                if pressed_array[0]:  # 鼠标左键点击
                    mouse_pos = pygame.mouse.get_pos()
                    click_point = _get_clickpoint(mouse_pos)

胜利判定

当一子落下,如何判定是否胜利?

可以肯定的是,当某一子落下的时候,如果出现了 5 连,那么落下的这颗子必定在这条 5 连线上。那么这个问题就可以简化了,我们无需全盘扫描,只需要在落子位置上横竖撇捺扫描一下,判断是否出现 5 连即可。

我们定义一个棋盘类,类中实例化一个 19 × 19 的二维数组,初始值皆为 0,表示空,用 1 表示黑子,2 表示白子。这个类对外提供一个落子方法 drop,接收参数落子方和落子坐标,如果落子后胜利,则返回胜利者,否则返回 None。

Chessman = namedtuple('Chessman', 'Name Value Color')
Point = namedtuple('Point', 'X Y')

BLACK_CHESSMAN = Chessman('黑子', 1, (45, 45, 45))
WHITE_CHESSMAN = Chessman('白子', 2, (219, 219, 219))

offset = [(1, 0), (0, 1), (1, 1), (1, -1)]


class Checkerboard:
    def __init__(self, line_points):
        self._line_points = line_points
        self._checkerboard = [[0] * line_points for _ in range(line_points)]

    def _get_checkerboard(self):
        return self._checkerboard

    checkerboard = property(_get_checkerboard)

    # 判断是否可落子
    def can_drop(self, point):
        return self._checkerboard[point.Y][point.X] == 0

    def drop(self, chessman, point):
        """
        落子
        :param chessman: 黑子/白子
        :param point:落子位置
        :return:若该子落下之后即可获胜,则返回获胜方,否则返回 None
        """
        print(f'{chessman.Name} ({point.X}, {point.Y})')
        self._checkerboard[point.Y][point.X] = chessman.Value

        if self._win(point):
            print(f'{chessman.Name}获胜')
            return chessman

    # 判断是否赢了
    def _win(self, point):
        cur_value = self._checkerboard[point.Y][point.X]
        for os in offset:
            if self._get_count_on_direction(point, cur_value, os[0], os[1]):
                return True

    def _get_count_on_direction(self, point, value, x_offset, y_offset):
        count = 1
        for step in range(1, 5):
            x = point.X + step * x_offset
            y = point.Y + step * y_offset
            if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value:
                count += 1
            else:
                break
        for step in range(1, 5):
            x = point.X - step * x_offset
            y = point.Y - step * y_offset
            if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value:
                count += 1
            else:
                break

        return count >= 5

这里我定义了一个偏移量,我们一共要计算横竖撇捺 4 条线,任意一条线出现 5 连就算获胜。计算方法实际上是一样的,只是方向不同,所以定义一个偏移量数组,不同的偏移量表示不同的方向,这样就可以利用循环来实现了,节省了很多代码。

电脑落子

这就是全篇的重头戏了,要怎么教电脑下五子棋。
首先声明,我用的是相对传统的方式,不是深度学习。

五子棋就是要实现 5 连,所以,一开始,我的想法是:将所有连线保存在一个数组中,落子的时候选择最长的连线落子。但这样有个问题解决不掉,如何让电脑识别“三三”呢?

后来网上看到篇文章,使用的方法是:遍历棋盘上的空位,计算每一个位置其横竖撇捺 8 个方向上是否有己方的子,有一个就加 10 分,最后选得分最高的位置落子。

这样不太严谨,写出来的电脑估计水平很菜,但是这个思路却是对的,落子就是要找到最值得的地方,那么我们干脆对每一个可落子的地方来做一个评估,选出最优解。

这里我们需要了解一下五子棋的几种基本棋形:连五,活四,冲四,活三,眠三,活二,眠二。

连五

顾名思义,五颗同色棋子连在一起,赢了

图片

活四

四颗同色棋子连在一起,并且左右两边都没有对方棋子阻挡,有两个连五点。

冲四

四颗同色棋子连在一起,并且一边有对方棋子阻挡,或者四颗棋子不是连的,当中有个空挡,这时只有一个连五点。

活三、跳活三

活二和眠二

活二,能够形成活三的二;眠二,能够形成眠三的二。这里就不放图了,参考活三眠三。

打分机制

理解了这些棋形,那么按我们之前的思路,就是如何打分了。

  • 首先,连五肯定是不存在的,出现连五胜负已分,所以只要棋局还在进行中,就不会出现连五。那么,什么优先级最高?自然就是活四了。

  • 其次是对方的“四”,对方活四,你防不防都一样输了,对方冲四,你就必须防守。

  • 再次是我方的活三或冲四,活三跟冲四其实是一个级别的,对方必须防守。

  • 再次是对方的活三或冲四。

以此类推下去。我们可以总结一点规律:

  • 相同的棋形,我方优于对方。

  • 冲四跟活三一个级别,眠三跟活二一个级别。

  • 如果中间有空格的话,肯定是要比没空格的略微低级一点,但不至于降级。

基本逻辑就是这样,这一块的代码我写得也不好,整个判断写了100多行,就不贴代码了,大家可以直接下源码看。

五子棋执黑是必赢的,代码中,玩家就是执黑先手,电脑执白后手,所以,下的好是完全可以赢电脑的,不过一个小小失误也很可能被电脑翻盘。

全部源码:

ManAndMachine.py

"""五子棋之人机对战"""

import sysimport randomimport pygamefrom pygame.locals import *import pygame.gfxdrawfrom checkerboard import Checkerboard, BLACK_CHESSMAN, WHITE_CHESSMAN, offset, Point
SIZE = 30  # 棋盘每个点时间的间隔Line_Points = 19  # 棋盘每行/每列点数Outer_Width = 20  # 棋盘外宽度Border_Width = 4  # 边框宽度Inside_Width = 4  # 边框跟实际的棋盘之间的间隔Border_Length = SIZE * (Line_Points - 1) + Inside_Width * 2 + Border_Width  # 边框线的长度Start_X = Start_Y = Outer_Width + int(Border_Width / 2) + Inside_Width  # 网格线起点(左上角)坐标SCREEN_HEIGHT = SIZE * (Line_Points - 1) + Outer_Width * 2 + Border_Width + Inside_Width * 2  # 游戏屏幕的高SCREEN_WIDTH = SCREEN_HEIGHT + 200  # 游戏屏幕的宽
Stone_Radius = SIZE // 2 - 3  # 棋子半径Stone_Radius2 = SIZE // 2 + 3Checkerboard_Color = (0xE3, 0x92, 0x65)  # 棋盘颜色BLACK_COLOR = (0, 0, 0)WHITE_COLOR = (255, 255, 255)RED_COLOR = (200, 30, 30)BLUE_COLOR = (30, 30, 200)
RIGHT_INFO_POS_X = SCREEN_HEIGHT + Stone_Radius2 * 2 + 10

def print_text(screen, font, x, y, text, fcolor=(255, 255, 255)):    imgText = font.render(text, True, fcolor)    screen.blit(imgText, (x, y))

def main():    pygame.init()    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))    pygame.display.set_caption('五子棋')
    font1 = pygame.font.SysFont('SimHei', 32)    font2 = pygame.font.SysFont('SimHei', 72)    fwidth, fheight = font2.size('黑方获胜')
    checkerboard = Checkerboard(Line_Points)    cur_runner = BLACK_CHESSMAN    winner = None    computer = AI(Line_Points, WHITE_CHESSMAN)
    black_win_count = 0    white_win_count = 0
    while True:        for event in pygame.event.get():            if event.type == QUIT:                sys.exit()            elif event.type == KEYDOWN:                if event.key == K_RETURN:                    if winner is not None:                        winner = None                        cur_runner = BLACK_CHESSMAN                        checkerboard = Checkerboard(Line_Points)                        computer = AI(Line_Points, WHITE_CHESSMAN)            elif event.type == MOUSEBUTTONDOWN:                if winner is None:                    pressed_array = pygame.mouse.get_pressed()                    if pressed_array[0]:                        mouse_pos = pygame.mouse.get_pos()                        click_point = _get_clickpoint(mouse_pos)                        if click_point is not None:                            if checkerboard.can_drop(click_point):                                winner = checkerboard.drop(cur_runner, click_point)                                if winner is None:                                    cur_runner = _get_next(cur_runner)                                    computer.get_opponent_drop(click_point)                                    AI_point = computer.AI_drop()                                    winner = checkerboard.drop(cur_runner, AI_point)                                    if winner is not None:                                        white_win_count += 1                                    cur_runner = _get_next(cur_runner)                                else:                                    black_win_count += 1                        else:                            print('超出棋盘区域')
        # 画棋盘        _draw_checkerboard(screen)
        # 画棋盘上已有的棋子        for i, row in enumerate(checkerboard.checkerboard):            for j, cell in enumerate(row):                if cell == BLACK_CHESSMAN.Value:                    _draw_chessman(screen, Point(j, i), BLACK_CHESSMAN.Color)                elif cell == WHITE_CHESSMAN.Value:                    _draw_chessman(screen, Point(j, i), WHITE_CHESSMAN.Color)
        _draw_left_info(screen, font1, cur_runner, black_win_count, white_win_count)
        if winner:            print_text(screen, font2, (SCREEN_WIDTH - fwidth)//2, (SCREEN_HEIGHT - fheight)//2, winner.Name + '获胜', RED_COLOR)
        pygame.display.flip()

def _get_next(cur_runner):    if cur_runner == BLACK_CHESSMAN:        return WHITE_CHESSMAN    else:        return BLACK_CHESSMAN

# 画棋盘def _draw_checkerboard(screen):    # 填充棋盘背景色    screen.fill(Checkerboard_Color)    # 画棋盘网格线外的边框    pygame.draw.rect(screen, BLACK_COLOR, (Outer_Width, Outer_Width, Border_Length, Border_Length), Border_Width)    # 画网格线    for i in range(Line_Points):        pygame.draw.line(screen, BLACK_COLOR,                         (Start_Y, Start_Y + SIZE * i),                         (Start_Y + SIZE * (Line_Points - 1), Start_Y + SIZE * i),                         1)    for j in range(Line_Points):        pygame.draw.line(screen, BLACK_COLOR,                         (Start_X + SIZE * j, Start_X),                         (Start_X + SIZE * j, Start_X + SIZE * (Line_Points - 1)),                         1)    # 画星位和天元    for i in (3, 9, 15):        for j in (3, 9, 15):            if i == j == 9:                radius = 5            else:                radius = 3            # pygame.draw.circle(screen, BLACK, (Start_X + SIZE * i, Start_Y + SIZE * j), radius)            pygame.gfxdraw.aacircle(screen, Start_X + SIZE * i, Start_Y + SIZE * j, radius, BLACK_COLOR)            pygame.gfxdraw.filled_circle(screen, Start_X + SIZE * i, Start_Y + SIZE * j, radius, BLACK_COLOR)

# 画棋子def _draw_chessman(screen, point, stone_color):    # pygame.draw.circle(screen, stone_color, (Start_X + SIZE * point.X, Start_Y + SIZE * point.Y), Stone_Radius)    pygame.gfxdraw.aacircle(screen, Start_X + SIZE * point.X, Start_Y + SIZE * point.Y, Stone_Radius, stone_color)    pygame.gfxdraw.filled_circle(screen, Start_X + SIZE * point.X, Start_Y + SIZE * point.Y, Stone_Radius, stone_color)

# 画左侧信息显示def _draw_left_info(screen, font, cur_runner, black_win_count, white_win_count):    _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, Start_X + Stone_Radius2), BLACK_CHESSMAN.Color)    _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, Start_X + Stone_Radius2 * 4), WHITE_CHESSMAN.Color)
    print_text(screen, font, RIGHT_INFO_POS_X, Start_X + 3, '玩家', BLUE_COLOR)    print_text(screen, font, RIGHT_INFO_POS_X, Start_X + Stone_Radius2 * 3 + 3, '电脑', BLUE_COLOR)
    print_text(screen, font, SCREEN_HEIGHT, SCREEN_HEIGHT - Stone_Radius2 * 8, '战况:', BLUE_COLOR)    _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, SCREEN_HEIGHT - int(Stone_Radius2 * 4.5)), BLACK_CHESSMAN.Color)    _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, SCREEN_HEIGHT - Stone_Radius2 * 2), WHITE_CHESSMAN.Color)    print_text(screen, font, RIGHT_INFO_POS_X, SCREEN_HEIGHT - int(Stone_Radius2 * 5.5) + 3, f'{black_win_count} 胜', BLUE_COLOR)    print_text(screen, font, RIGHT_INFO_POS_X, SCREEN_HEIGHT - Stone_Radius2 * 3 + 3, f'{white_win_count} 胜', BLUE_COLOR)

def _draw_chessman_pos(screen, pos, stone_color):    pygame.gfxdraw.aacircle(screen, pos[0], pos[1], Stone_Radius2, stone_color)    pygame.gfxdraw.filled_circle(screen, pos[0], pos[1], Stone_Radius2, stone_color)

# 根据鼠标点击位置,返回游戏区坐标def _get_clickpoint(click_pos):    pos_x = click_pos[0] - Start_X    pos_y = click_pos[1] - Start_Y    if pos_x < -Inside_Width or pos_y < -Inside_Width:        return None    x = pos_x // SIZE    y = pos_y // SIZE    if pos_x % SIZE > Stone_Radius:        x += 1    if pos_y % SIZE > Stone_Radius:        y += 1    if x >= Line_Points or y >= Line_Points:        return None
    return Point(x, y)

class AI:    def __init__(self, line_points, chessman):        self._line_points = line_points        self._my = chessman        self._opponent = BLACK_CHESSMAN if chessman == WHITE_CHESSMAN else WHITE_CHESSMAN        self._checkerboard = [[0] * line_points for _ in range(line_points)]
    def get_opponent_drop(self, point):        self._checkerboard[point.Y][point.X] = self._opponent.Value
    def AI_drop(self):        point = None        score = 0        for i in range(self._line_points):            for j in range(self._line_points):                if self._checkerboard[j][i] == 0:                    _score = self._get_point_score(Point(i, j))                    if _score > score:                        score = _score                        point = Point(i, j)                    elif _score == score and _score > 0:                        r = random.randint(0, 100)                        if r % 2 == 0:                            point = Point(i, j)        self._checkerboard[point.Y][point.X] = self._my.Value        return point
    def _get_point_score(self, point):        score = 0        for os in offset:            score += self._get_direction_score(point, os[0], os[1])        return score
    def _get_direction_score(self, point, x_offset, y_offset):        count = 0   # 落子处我方连续子数        _count = 0  # 落子处对方连续子数        space = None   # 我方连续子中有无空格        _space = None  # 对方连续子中有无空格        both = 0    # 我方连续子两端有无阻挡        _both = 0   # 对方连续子两端有无阻挡
        # 如果是 1 表示是边上是我方子,2 表示敌方子        flag = self._get_stone_color(point, x_offset, y_offset, True)        if flag != 0:            for step in range(1, 6):                x = point.X + step * x_offset                y = point.Y + step * y_offset                if 0 <= x < self._line_points and 0 <= y < self._line_points:                    if flag == 1:                        if self._checkerboard[y][x] == self._my.Value:                            count += 1                            if space is False:                                space = True                        elif self._checkerboard[y][x] == self._opponent.Value:                            _both += 1                            break                        else:                            if space is None:                                space = False                            else:                                break   # 遇到第二个空格退出                    elif flag == 2:                        if self._checkerboard[y][x] == self._my.Value:                            _both += 1                            break                        elif self._checkerboard[y][x] == self._opponent.Value:                            _count += 1                            if _space is False:                                _space = True                        else:                            if _space is None:                                _space = False                            else:                                break                else:                    # 遇到边也就是阻挡                    if flag == 1:                        both += 1                    elif flag == 2:                        _both += 1
        if space is False:            space = None        if _space is False:            _space = None
        _flag = self._get_stone_color(point, -x_offset, -y_offset, True)        if _flag != 0:            for step in range(1, 6):                x = point.X - step * x_offset                y = point.Y - step * y_offset                if 0 <= x < self._line_points and 0 <= y < self._line_points:                    if _flag == 1:                        if self._checkerboard[y][x] == self._my.Value:                            count += 1                            if space is False:                                space = True                        elif self._checkerboard[y][x] == self._opponent.Value:                            _both += 1                            break                        else:                            if space is None:                                space = False                            else:                                break   # 遇到第二个空格退出                    elif _flag == 2:                        if self._checkerboard[y][x] == self._my.Value:                            _both += 1                            break                        elif self._checkerboard[y][x] == self._opponent.Value:                            _count += 1                            if _space is False:                                _space = True                        else:                            if _space is None:                                _space = False                            else:                                break                else:                    # 遇到边也就是阻挡                    if _flag == 1:                        both += 1                    elif _flag == 2:                        _both += 1
        score = 0        if count == 4:            score = 10000        elif _count == 4:            score = 9000        elif count == 3:            if both == 0:                score = 1000            elif both == 1:                score = 100            else:                score = 0        elif _count == 3:            if _both == 0:                score = 900            elif _both == 1:                score = 90            else:                score = 0        elif count == 2:            if both == 0:                score = 100            elif both == 1:                score = 10            else:                score = 0        elif _count == 2:            if _both == 0:                score = 90            elif _both == 1:                score = 9            else:                score = 0        elif count == 1:            score = 10        elif _count == 1:            score = 9        else:            score = 0
        if space or _space:            score /= 2
        return score
    # 判断指定位置处在指定方向上是我方子、对方子、空    def _get_stone_color(self, point, x_offset, y_offset, next):        x = point.X + x_offset        y = point.Y + y_offset        if 0 <= x < self._line_points and 0 <= y < self._line_points:            if self._checkerboard[y][x] == self._my.Value:                return 1            elif self._checkerboard[y][x] == self._opponent.Value:                return 2            else:                if next:                    return self._get_stone_color(Point(x, y), x_offset, y_offset, False)                else:                    return 0        else:            return 0

if __name__ == '__main__':    main()© 2019 GitHub, Inc.

ManAndMachine.py

"""五子棋之人机对战"""

import sysimport randomimport pygamefrom pygame.locals import *import pygame.gfxdrawfrom checkerboard import Checkerboard, BLACK_CHESSMAN, WHITE_CHESSMAN, offset, Point
SIZE = 30  # 棋盘每个点时间的间隔Line_Points = 19  # 棋盘每行/每列点数Outer_Width = 20  # 棋盘外宽度Border_Width = 4  # 边框宽度Inside_Width = 4  # 边框跟实际的棋盘之间的间隔Border_Length = SIZE * (Line_Points - 1) + Inside_Width * 2 + Border_Width  # 边框线的长度Start_X = Start_Y = Outer_Width + int(Border_Width / 2) + Inside_Width  # 网格线起点(左上角)坐标SCREEN_HEIGHT = SIZE * (Line_Points - 1) + Outer_Width * 2 + Border_Width + Inside_Width * 2  # 游戏屏幕的高SCREEN_WIDTH = SCREEN_HEIGHT + 200  # 游戏屏幕的宽
Stone_Radius = SIZE // 2 - 3  # 棋子半径Stone_Radius2 = SIZE // 2 + 3Checkerboard_Color = (0xE3, 0x92, 0x65)  # 棋盘颜色BLACK_COLOR = (0, 0, 0)WHITE_COLOR = (255, 255, 255)RED_COLOR = (200, 30, 30)BLUE_COLOR = (30, 30, 200)
RIGHT_INFO_POS_X = SCREEN_HEIGHT + Stone_Radius2 * 2 + 10

def print_text(screen, font, x, y, text, fcolor=(255, 255, 255)):    imgText = font.render(text, True, fcolor)    screen.blit(imgText, (x, y))

def main():    pygame.init()    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))    pygame.display.set_caption('五子棋')
    font1 = pygame.font.SysFont('SimHei', 32)    font2 = pygame.font.SysFont('SimHei', 72)    fwidth, fheight = font2.size('黑方获胜')
    checkerboard = Checkerboard(Line_Points)    cur_runner = BLACK_CHESSMAN    winner = None    computer = AI(Line_Points, WHITE_CHESSMAN)
    black_win_count = 0    white_win_count = 0
    while True:        for event in pygame.event.get():            if event.type == QUIT:                sys.exit()            elif event.type == KEYDOWN:                if event.key == K_RETURN:                    if winner is not None:                        winner = None                        cur_runner = BLACK_CHESSMAN                        checkerboard = Checkerboard(Line_Points)                        computer = AI(Line_Points, WHITE_CHESSMAN)            elif event.type == MOUSEBUTTONDOWN:                if winner is None:                    pressed_array = pygame.mouse.get_pressed()                    if pressed_array[0]:                        mouse_pos = pygame.mouse.get_pos()                        click_point = _get_clickpoint(mouse_pos)                        if click_point is not None:                            if checkerboard.can_drop(click_point):                                winner = checkerboard.drop(cur_runner, click_point)                                if winner is None:                                    cur_runner = _get_next(cur_runner)                                    computer.get_opponent_drop(click_point)                                    AI_point = computer.AI_drop()                                    winner = checkerboard.drop(cur_runner, AI_point)                                    if winner is not None:                                        white_win_count += 1                                    cur_runner = _get_next(cur_runner)                                else:                                    black_win_count += 1                        else:                            print('超出棋盘区域')
        # 画棋盘        _draw_checkerboard(screen)
        # 画棋盘上已有的棋子        for i, row in enumerate(checkerboard.checkerboard):            for j, cell in enumerate(row):                if cell == BLACK_CHESSMAN.Value:                    _draw_chessman(screen, Point(j, i), BLACK_CHESSMAN.Color)                elif cell == WHITE_CHESSMAN.Value:                    _draw_chessman(screen, Point(j, i), WHITE_CHESSMAN.Color)
        _draw_left_info(screen, font1, cur_runner, black_win_count, white_win_count)
        if winner:            print_text(screen, font2, (SCREEN_WIDTH - fwidth)//2, (SCREEN_HEIGHT - fheight)//2, winner.Name + '获胜', RED_COLOR)
        pygame.display.flip()

def _get_next(cur_runner):    if cur_runner == BLACK_CHESSMAN:        return WHITE_CHESSMAN    else:        return BLACK_CHESSMAN

# 画棋盘def _draw_checkerboard(screen):    # 填充棋盘背景色    screen.fill(Checkerboard_Color)    # 画棋盘网格线外的边框    pygame.draw.rect(screen, BLACK_COLOR, (Outer_Width, Outer_Width, Border_Length, Border_Length), Border_Width)    # 画网格线    for i in range(Line_Points):        pygame.draw.line(screen, BLACK_COLOR,                         (Start_Y, Start_Y + SIZE * i),                         (Start_Y + SIZE * (Line_Points - 1), Start_Y + SIZE * i),                         1)    for j in range(Line_Points):        pygame.draw.line(screen, BLACK_COLOR,                         (Start_X + SIZE * j, Start_X),                         (Start_X + SIZE * j, Start_X + SIZE * (Line_Points - 1)),                         1)    # 画星位和天元    for i in (3, 9, 15):        for j in (3, 9, 15):            if i == j == 9:                radius = 5            else:                radius = 3            # pygame.draw.circle(screen, BLACK, (Start_X + SIZE * i, Start_Y + SIZE * j), radius)            pygame.gfxdraw.aacircle(screen, Start_X + SIZE * i, Start_Y + SIZE * j, radius, BLACK_COLOR)            pygame.gfxdraw.filled_circle(screen, Start_X + SIZE * i, Start_Y + SIZE * j, radius, BLACK_COLOR)

# 画棋子def _draw_chessman(screen, point, stone_color):    # pygame.draw.circle(screen, stone_color, (Start_X + SIZE * point.X, Start_Y + SIZE * point.Y), Stone_Radius)    pygame.gfxdraw.aacircle(screen, Start_X + SIZE * point.X, Start_Y + SIZE * point.Y, Stone_Radius, stone_color)    pygame.gfxdraw.filled_circle(screen, Start_X + SIZE * point.X, Start_Y + SIZE * point.Y, Stone_Radius, stone_color)

# 画左侧信息显示def _draw_left_info(screen, font, cur_runner, black_win_count, white_win_count):    _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, Start_X + Stone_Radius2), BLACK_CHESSMAN.Color)    _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, Start_X + Stone_Radius2 * 4), WHITE_CHESSMAN.Color)
    print_text(screen, font, RIGHT_INFO_POS_X, Start_X + 3, '玩家', BLUE_COLOR)    print_text(screen, font, RIGHT_INFO_POS_X, Start_X + Stone_Radius2 * 3 + 3, '电脑', BLUE_COLOR)
    print_text(screen, font, SCREEN_HEIGHT, SCREEN_HEIGHT - Stone_Radius2 * 8, '战况:', BLUE_COLOR)    _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, SCREEN_HEIGHT - int(Stone_Radius2 * 4.5)), BLACK_CHESSMAN.Color)    _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, SCREEN_HEIGHT - Stone_Radius2 * 2), WHITE_CHESSMAN.Color)    print_text(screen, font, RIGHT_INFO_POS_X, SCREEN_HEIGHT - int(Stone_Radius2 * 5.5) + 3, f'{black_win_count} 胜', BLUE_COLOR)    print_text(screen, font, RIGHT_INFO_POS_X, SCREEN_HEIGHT - Stone_Radius2 * 3 + 3, f'{white_win_count} 胜', BLUE_COLOR)

def _draw_chessman_pos(screen, pos, stone_color):    pygame.gfxdraw.aacircle(screen, pos[0], pos[1], Stone_Radius2, stone_color)    pygame.gfxdraw.filled_circle(screen, pos[0], pos[1], Stone_Radius2, stone_color)

# 根据鼠标点击位置,返回游戏区坐标def _get_clickpoint(click_pos):    pos_x = click_pos[0] - Start_X    pos_y = click_pos[1] - Start_Y    if pos_x < -Inside_Width or pos_y < -Inside_Width:        return None    x = pos_x // SIZE    y = pos_y // SIZE    if pos_x % SIZE > Stone_Radius:        x += 1    if pos_y % SIZE > Stone_Radius:        y += 1    if x >= Line_Points or y >= Line_Points:        return None
    return Point(x, y)

class AI:    def __init__(self, line_points, chessman):        self._line_points = line_points        self._my = chessman        self._opponent = BLACK_CHESSMAN if chessman == WHITE_CHESSMAN else WHITE_CHESSMAN        self._checkerboard = [[0] * line_points for _ in range(line_points)]
    def get_opponent_drop(self, point):        self._checkerboard[point.Y][point.X] = self._opponent.Value
    def AI_drop(self):        point = None        score = 0        for i in range(self._line_points):            for j in range(self._line_points):                if self._checkerboard[j][i] == 0:                    _score = self._get_point_score(Point(i, j))                    if _score > score:                        score = _score                        point = Point(i, j)                    elif _score == score and _score > 0:                        r = random.randint(0, 100)                        if r % 2 == 0:                            point = Point(i, j)        self._checkerboard[point.Y][point.X] = self._my.Value        return point
    def _get_point_score(self, point):        score = 0        for os in offset:            score += self._get_direction_score(point, os[0], os[1])        return score
    def _get_direction_score(self, point, x_offset, y_offset):        count = 0   # 落子处我方连续子数        _count = 0  # 落子处对方连续子数        space = None   # 我方连续子中有无空格        _space = None  # 对方连续子中有无空格        both = 0    # 我方连续子两端有无阻挡        _both = 0   # 对方连续子两端有无阻挡
        # 如果是 1 表示是边上是我方子,2 表示敌方子        flag = self._get_stone_color(point, x_offset, y_offset, True)        if flag != 0:            for step in range(1, 6):                x = point.X + step * x_offset                y = point.Y + step * y_offset                if 0 <= x < self._line_points and 0 <= y < self._line_points:                    if flag == 1:                        if self._checkerboard[y][x] == self._my.Value:                            count += 1                            if space is False:                                space = True                        elif self._checkerboard[y][x] == self._opponent.Value:                            _both += 1                            break                        else:                            if space is None:                                space = False                            else:                                break   # 遇到第二个空格退出                    elif flag == 2:                        if self._checkerboard[y][x] == self._my.Value:                            _both += 1                            break                        elif self._checkerboard[y][x] == self._opponent.Value:                            _count += 1                            if _space is False:                                _space = True                        else:                            if _space is None:                                _space = False                            else:                                break                else:                    # 遇到边也就是阻挡                    if flag == 1:                        both += 1                    elif flag == 2:                        _both += 1
        if space is False:            space = None        if _space is False:            _space = None
        _flag = self._get_stone_color(point, -x_offset, -y_offset, True)        if _flag != 0:            for step in range(1, 6):                x = point.X - step * x_offset                y = point.Y - step * y_offset                if 0 <= x < self._line_points and 0 <= y < self._line_points:                    if _flag == 1:                        if self._checkerboard[y][x] == self._my.Value:                            count += 1                            if space is False:                                space = True                        elif self._checkerboard[y][x] == self._opponent.Value:                            _both += 1                            break                        else:                            if space is None:                                space = False                            else:                                break   # 遇到第二个空格退出                    elif _flag == 2:                        if self._checkerboard[y][x] == self._my.Value:                            _both += 1                            break                        elif self._checkerboard[y][x] == self._opponent.Value:                            _count += 1                            if _space is False:                                _space = True                        else:                            if _space is None:                                _space = False                            else:                                break                else:                    # 遇到边也就是阻挡                    if _flag == 1:                        both += 1                    elif _flag == 2:                        _both += 1
        score = 0        if count == 4:            score = 10000        elif _count == 4:            score = 9000        elif count == 3:            if both == 0:                score = 1000            elif both == 1:                score = 100            else:                score = 0        elif _count == 3:            if _both == 0:                score = 900            elif _both == 1:                score = 90            else:                score = 0        elif count == 2:            if both == 0:                score = 100            elif both == 1:                score = 10            else:                score = 0        elif _count == 2:            if _both == 0:                score = 90            elif _both == 1:                score = 9            else:                score = 0        elif count == 1:            score = 10        elif _count == 1:            score = 9        else:            score = 0
        if space or _space:            score /= 2
        return score
    # 判断指定位置处在指定方向上是我方子、对方子、空    def _get_stone_color(self, point, x_offset, y_offset, next):        x = point.X + x_offset        y = point.Y + y_offset        if 0 <= x < self._line_points and 0 <= y < self._line_points:            if self._checkerboard[y][x] == self._my.Value:                return 1            elif self._checkerboard[y][x] == self._opponent.Value:                return 2            else:                if next:                    return self._get_stone_color(Point(x, y), x_offset, y_offset, False)                else:                    return 0        else:            return 0

if __name__ == '__main__':    main()

checkerboard.pyfrom collections import namedtuple

Chessman = namedtuple('Chessman', 'Name Value Color')Point = namedtuple('Point', 'X Y')
BLACK_CHESSMAN = Chessman('黑子', 1, (45, 45, 45))WHITE_CHESSMAN = Chessman('白子', 2, (219, 219, 219))
offset = [(1, 0), (0, 1), (1, 1), (1, -1)]

class Checkerboard:    def __init__(self, line_points):        self._line_points = line_points        self._checkerboard = [[0] * line_points for _ in range(line_points)]
    def _get_checkerboard(self):        return self._checkerboard
    checkerboard = property(_get_checkerboard)
    # 判断是否可落子    def can_drop(self, point):        return self._checkerboard[point.Y][point.X] == 0
    def drop(self, chessman, point):        """        落子        :param chessman:        :param point:落子位置        :return:若该子落下之后即可获胜,则返回获胜方,否则返回 None        """        print(f'{chessman.Name} ({point.X}, {point.Y})')        self._checkerboard[point.Y][point.X] = chessman.Value
        if self._win(point):            print(f'{chessman.Name}获胜')            return chessman
    # 判断是否赢了    def _win(self, point):        cur_value = self._checkerboard[point.Y][point.X]        for os in offset:            if self._get_count_on_direction(point, cur_value, os[0], os[1]):                return True
    def _get_count_on_direction(self, point, value, x_offset, y_offset):        count = 1        for step in range(1, 5):            x = point.X + step * x_offset            y = point.Y + step * y_offset            if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value:                count += 1            else:                break        for step in range(1, 5):            x = point.X - step * x_offset            y = point.Y - step * y_offset            if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value:                count += 1            else:                break
        return count >= 5

图片

猜你喜欢

转载自blog.csdn.net/Python_kele/article/details/115147664
今日推荐