Python to write gomoku game (with complete source code)

beginning

beginning

Draw a chessboard

First of all, we must  draw a chessboard. It is not difficult to draw a 19 × 19 or 15 × 15 chessboard with  pygame . This has been used many times in the previous article, so I won't repeat it.

Draw chess pieces

What needs to be said is to draw chess pieces, because there is no suitable chess piece picture, so just draw the chess pieces by yourself.  The circle
we  pygame.draw.circledraw is like this:

Many people learn python and after mastering the basic grammar, they don't know where to find cases to get started.

Many people who have done case studies do not know how to learn more advanced knowledge.

So for these three types of people, I will provide you with a good learning platform, free to receive video tutorials, e-books, and the source code of the course!

QQ group: 701698587


The jagged shape is very obvious. pygame.draw There is  a function to draw an anti- aliased straight line  aaline, but there is no  aacirclesuch function to draw an anti-aliased circle.

You need to use it here  pygame.gfxdraw . pygame.gfxdraw It is currently only an experimental version, which means that this  API  may  change or disappear in future versions of  pygame .

To draw anti-aliasing and filled shapes, first use the aa* version of the function, then use the filled version. E.g:

image

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)

 

Let's try this method by drawing a piece on the chessboard.

You can see that the effect has been significantly improved.

Laozi

The move needs to determine the mouse event. When the left mouse button is clicked, the position of the mouse click is obtained, and then according to the position of the chessboard, the position of the chessman on the chessboard is calculated.

    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)

 

Victory decision

When a child falls, how to determine whether or not to win?

What is certain is that when a certain child falls, if there is a 5th connection, then the fallen child must be on this 5th connection. Then this problem can be simplified. We don't need to scan the entire board, we just need to scan it horizontally and vertically to determine whether there are 5 connections.

We define a chessboard class, in which a 19 × 19 two-dimensional array is instantiated. The initial values ​​are all 0, which means empty, and 1 means black and 2 means white. This class provides a placement method externally  drop, which receives the parameters of the placement square and the placement coordinates. If the placement is successful, it returns the winner, otherwise it returns 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

 

Here I have defined an offset. We have to calculate a total of 4 lines horizontally and vertically. If any line appears for 5 consecutive times, it will be considered a win. The calculation method is actually the same, but the direction is different, so define an offset array, different offsets represent different directions, so that you can use the loop to achieve, save a lot of code.

Computer placement

This is the highlight of the whole article, how to teach the computer to play Gobang.
First of all, I am using a relatively traditional method, not deep learning.

Gobang is to achieve 5 connections. So, at the beginning, my idea was to save all the connections in an array, and choose the longest connection when placing a piece. But there is a problem that can't be solved. How to make the computer recognize the "three-three"?

Later, I saw an article on the Internet. The method used is: traverse the empty positions on the chessboard and calculate whether there is a player in each position in 8 directions. If there is one, add 10 points, and finally choose the position with the highest score. .

This is not very rigorous. The computer estimated level written is very good, but the idea is correct. The placement is to find the most worthwhile place, then we simply make an assessment of each place that can be placed and select the best. solution.

Here we need to understand several basic chess patterns of Gobang: consecutive five, live four, punch four, live three, sleep three, live two, sleep two.

Consecutive five

As the name suggests, five pieces of the same color are connected together and win

image

Live four

Four pieces of the same color are connected together, and there is no opposing piece blocking on either side, there are two pieces with five points.

Okushi

Four pieces of the same color are connected together, and one side is blocked by the opponent's piece, or the four pieces are not connected, and there is a gap. At this time, there is only one with five points.

Live three, jump live three

Live two and sleep two

Living two can form two of living three; sleeping two can form two of sleeping three. The picture is not shown here, refer to Living Three Sleeping Three.

Scoring mechanism

After understanding these chess patterns, then according to our previous thinking, it is how to score.

  • First of all, there is definitely no consecutive fives. The five consecutive wins and losses have already been divided, so as long as the game is still in progress, there will be no consecutive fives. So, what is the highest priority? Naturally it is four alive.

  • The second is the "four" of the opponent. If the opponent lives four, you lose the same if you guard or fail. If the opponent rushes four, you must defend.

  • Once again, it's our live three or four, live three and four are actually at the same level, and the opponent must defend.

  • Once again, it is the opponent's live three or four.

And so on. We can summarize a little rule:

  • With the same chess shape, our side is better than the other side.

  • Rush four and live three levels, sleep three and live two levels.

  • If there is a space in the middle, it must be slightly lower than the one without spaces, but it will not be degraded.

The basic logic is like this. I didn't write this piece of code very well. I wrote more than 100 lines of the whole judgment, so I won't post the code. You can just download the source code to see.

Gobang black is a must. In the code, the player is the black first and the computer the white second. Therefore, a good game can completely win the computer, but a small mistake is also likely to be overturned by the computer.

 

All source code:

ManAndMachine.py

"""Gobang: Human-Machine Battle"""

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

"""Gobang: Human-Machine Battle"""

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

image

Guess you like

Origin blog.csdn.net/Python_kele/article/details/115147664
Recommended