Этот проект представляет собой реализацию на Python классической игры "Connect Four", в которой игрок может соревноваться с AI. Для интерфейса и управления игры используется библиотека Pygame. AI принимает решения на основе алгоритма поиска в дереве Монте-Карло, и уровень сложности может быть настроен, позволяя игрокам предлагать себе вызов с более умными противниками AI.
Основные концепции:
Использование Pygame для разработки игр.
Реализация алгоритма поиска в дереве Монте-Карло для принятия решений AI.
👀 Предварительный просмотр
🎯 Задачи
В этом проекте вы научитесь:
Как создавать игру с использованием Pygame
Как реализовать алгоритм поиска в дереве Монте-Карло для принятия решений AI
Как настраивать и улучшать уровень сложности AI
Как создать увлекательную и интерактивную игру "Connect Four" для боев между человеком и AI
🏆 Достижения
После завершения этого проекта вы сможете:
Разрабатывать игры с использованием Python и Pygame
Разбираться в принципах алгоритма поиска в дереве Монте-Карло
Настраивать сложность противника AI, чтобы создать захватывающий игровой опыт
Улучшать пользовательский интерфейс, чтобы сделать игровой опыт более увлекательным
Подготовка к разработке
Игра "Connect Four" происходит на сетке размером 7*6. Игроки ходят по очереди, опуская свои фишки сверху в колонку. Фишка падает в самую нижнюю свободную ячейку в этой колонке. Игрок, который соединяет четыре фишки в прямой линию (горизонтально, вертикально или по диагонали), выигрывает игру.
Создайте файл с именем fourinrow.py в директории ~/project, чтобы сохранить код для этого проекта. Кроме того, нам нужно установить библиотеку Pygame, чтобы реализовать интерфейс игры и поддерживать операции.
cd ~/project
touch fourinrow.py
sudo pip install pygame
Вы можете найти требуемые изображение ресурсы для этого проекта в директории ~/project/images.
Чтобы лучше понять код в этом проекте, рекомендуется изучать его вместе с кодом полного решения.
В использовании переменные включают ширину и высоту игровой доски (можно изменить, чтобы проектировать доски различных размеров), уровень сложности, размер игровых фишек и настройку некоторых координатных переменных.
В файле fourinrow.py введите следующий код:
import random, copy, sys, pygame
from pygame.locals import *
BOARDWIDTH = 7 ## Количество столбцов на игровой доске
BOARDHEIGHT = 6 ## Количество строк на игровой доске
assert BOARDWIDTH >= 4 and BOARDHEIGHT >= 4, 'Доска должна быть по крайней мере 4x4.'
## Python утверждение assert используется для того, чтобы объявить, что данное логическое выражение должно быть истинным.
## Если выражение ложно, то возникает исключение.
DIFFICULTY = 2 ## Уровень сложности, количество ходов, которые компьютер может рассмотреть
## Здесь 2 означает рассмотрение 7 возможных ходов противника и как ответить на эти 7 ходов
SPACESIZE = 50 ## Размер игровых фишек
FPS = 30 ## Частота обновления экрана, 30/с
WINDOWWIDTH = 640 ## Ширина игрового экрана в пикселях
WINDOWHEIGHT = 480 ## Высота игрового экрана в пикселях
XMARGIN = int((WINDOWWIDTH - BOARDWIDTH * SPACESIZE) / 2) ## X-координата левого края сетки
YMARGIN = int((WINDOWHEIGHT - BOARDHEIGHT * SPACESIZE) / 2) ## Y-координата верхнего края сетки
BRIGHTBLUE = (0, 50, 255) ## Синий цвет
WHITE = (255, 255, 255) ## Белый цвет
BGCOLOR = BRIGHTBLUE
TEXTCOLOR = WHITE
RED ='red'
BLACK = 'black'
EMPTY = None
HUMAN = 'human'
COMPUTER = 'computer'
Кроме того, нам также нужно определить некоторые глобальные переменные pygame. Эти глобальные переменные будут вызываться несколько раз в различных модулях позже. Многие из них - это переменные, которые хранят загруженные изображения, поэтому подготовительная работа занимает несколько времени, будьте терпеливы.
## Инициализировать модули pygame
pygame.init()
## Создать объект Clock
FPSCLOCK = pygame.time.Clock()
## Создать игровое окно
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
## Установить заголовок игрового окна
pygame.display.set_caption(u'Connect Four')
## Rect(left, top, width, height) используется для определения позиции и размера
REDPILERECT = pygame.Rect(int(SPACESIZE / 2), WINDOWHEIGHT - int(3 * SPACESIZE / 2), SPACESIZE, SPACESIZE)
## Создать нижние левую и нижнюю правую игровые фишки в окне
BLACKPILERECT = pygame.Rect(WINDOWWIDTH - int(3 * SPACESIZE / 2), WINDOWHEIGHT - int(3 * SPACESIZE / 2), SPACESIZE, SPACESIZE)
## Загрузить изображение красной игровой фишки
REDTOKENIMG = pygame.image.load('images/4rowred.png')
## Масштабировать изображение красной игровой фишки до SPACESIZE
REDTOKENIMG = pygame.transform.smoothscale(REDTOKENIMG, (SPACESIZE, SPACESIZE))
## Загрузить изображение черной игровой фишки
BLACKTOKENIMG = pygame.image.load('images/4rowblack.png')
## Масштабировать изображение черной игровой фишки до SPACESIZE
BLACKTOKENIMG = pygame.transform.smoothscale(BLACKTOKENIMG, (SPACESIZE, SPACESIZE))
## Загрузить изображение игровой доски
BOARDIMG = pygame.image.load('images/4rowboard.png')
## Масштабировать изображение игровой доски до SPACESIZE
BOARDIMG = pygame.transform.smoothscale(BOARDIMG, (SPACESIZE, SPACESIZE))
## Загрузить изображение победителя - человека
HUMANWINNERIMG = pygame.image.load('images/4rowhumanwinner.png')
## Загрузить изображение победителя - AI
COMPUTERWINNERIMG = pygame.image.load('images/4rowcomputerwinner.png')
## Загрузить изображение ничьей
TIEWINNERIMG = pygame.image.load('images/4rowtie.png')
## Вернуть объект Rect
WINNERRECT = HUMANWINNERIMG.get_rect()
## Центрировать изображение победителя на игровом окне
WINNERRECT.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2))
## Загрузить изображение стрелки для инструкции пользователю
ARROWIMG = pygame.image.load('images/4rowarrow.png')
## Вернуть объект Rect
ARROWRECT = ARROWIMG.get_rect()
## Установить левую позицию изображения стрелки
ARROWRECT.left = REDPILERECT.right + 10
## Выровнять изображение стрелки по вертикали с красной игровой фишкой под ней
ARROWRECT.centery = REDPILERECT.centery
Чтобы лучше понять код в этом проекте, рекомендуется изучать его вместе с кодом полного решения.
Сначала очистите двухмерный список, представляющий игровую доску, а затем установите соответствующие позиции на доске цветами в зависимости от ходов игрока и AI.
def drawBoard(board, extraToken=None):
## DISPLAYSURF - это наш интерфейс, определенный в модуле инициализации переменных.
DISPLAYSURF.fill(BGCOLOR) ## Заполните фон игрового окна цветом синим.
spaceRect = pygame.Rect(0, 0, SPACESIZE, SPACESIZE) ## Создайте экземпляр Rect.
for x in range(BOARDWIDTH):
## Определите координаты верхнего левого угла каждой ячейки в каждой строке каждого столбца.
for y in range(BOARDHEIGHT):
spaceRect.topleft = (XMARGIN + (x * SPACESIZE), YMARGIN + (y * SPACESIZE))
## Когда x = 0 и y = 0, это первая ячейка в первой строке первого столбца.
if board[x][y] == RED: ## Если значение ячейки равно красному,
## нарисуйте красную фишку в игровом окне внутри spaceRect.
DISPLAYSURF.blit(REDTOKENIMG, spaceRect)
elif board[x][y] == BLACK: ## В противном случае нарисуйте черную фишку.
DISPLAYSURF.blit(BLACKTOKENIMG, spaceRect)
## extraToken - это переменная, которая содержит информацию о позиции и цвете.
## Она используется для отображения указанной фишки.
if extraToken!= None:
if extraToken['color'] == RED:
DISPLAYSURF.blit(REDTOKENIMG, (extraToken['x'], extraToken['y'], SPACESIZE, SPACESIZE))
elif extraToken['color'] == BLACK:
DISPLAYSURF.blit(BLACKTOKENIMG, (extraToken['x'], extraToken['y'], SPACESIZE, SPACESIZE))
## Нарисуйте панели для фишек.
for x in range(BOARDWIDTH):
for y in range(BOARDHEIGHT):
spaceRect.topleft = (XMARGIN + (x * SPACESIZE), YMARGIN + (y * SPACESIZE))
DISPLAYSURF.blit(BOARDIMG, spaceRect)
## Нарисуйте фишки в нижнем левом и нижнем правом углах игрового окна.
DISPLAYSURF.blit(REDTOKENIMG, REDPILERECT) ## Левая красная фишка.
DISPLAYSURF.blit(BLACKTOKENIMG, BLACKPILERECT) ## Правая черная фишка.
def getNewBoard():
board = []
for x in range(BOARDWIDTH):
board.append([EMPTY] * BOARDHEIGHT)
return board ## Верните список доски с количеством BOARDHEIGHT значений None.
В вышеприведенном коде функция drawBoard() рисует игровую доску и фишки на ней. Функция getNewBoard() возвращает новую структуру данных для доски.
Используйте одномерный метод Монте-Карло для оценки игровой доски в Go. Конкретно, когда дана определенная ситуация на доске, программа случайным образом выбирает точку из всех доступных точек в текущей ситуации и размещает на ней фишку. Этот процесс случайного выбора доступных точек (ход броском) повторяется до тех пор, пока ни одна сторона не останется без доступных точек (игра заканчивается), а затем полученное выигрыш или проигрыш этого конечного состояния передается в качестве основания для оценки текущей ситуации.
В этом проекте AI непрерывно выбирает разные столбцы и оценивает результаты побед обоих сторон. AI в конечном итоге выберет стратегию с более высокой оценкой.
Прежде чем смотреть на картинки и текст ниже, пожалуйста, посмотрите на код в конце, а затем обратитесь к соответствующим объяснениям.
Изучите противостояние между AI и игроком на рисунке ниже:
Некоторые переменные в проекте могут интуитивно отобразить процесс операции AI с фишками:
PotentialMoves: Возвращает список, который представляет возможность выигрыша AI при размещении фишки в любом столбце из списка. Значения - это случайные числа от -1 до 1. Когда значение отрицательное, это означает, что игрок может выиграть за следующие два хода, и чем меньше значение, тем выше вероятность победы игрока. Если значение равно 0, это означает, что игрок не выиграет, и AI также не выиграет. Если значение равно 1, это означает, что AI может выиграть.
bestMoveFitness: Фитнес - это максимальное значение, выбранное из PotentialMoves.
bestMoves: Если в PotentialMoves есть несколько максимальных значений, это означает, что шансы игрока на победу наименьшие, когда AI размещает фишку в столбцах, где находятся эти значения. Поэтому эти столбцы добавляются в список bestMoves.
column: Когда в bestMoves несколько значений, случайным образом выбирается один столбец из bestMoves в качестве хода AI. Если есть только одно значение, column это уникальное значение.
В проекте, выводя эти bestMoveFitness, bestMoves, column и potentialMoves, мы можем вывести параметры каждого шага AI на рисунке выше.
Изучением выбора AI на третьем шаге мы можем лучше понять алгоритм:
На рисунке ниже показаны некоторые ходы AI, а также возможные варианты хода игрока, если AI разместит фишку в первом столбце, и влияние следующего хода AI на шансы игрока на победу. Через этот процесс поиска и итерации AI может определить выигрышные ситуации как для противника, так и для себя за следующие два хода и принимать решения соответственно.
На рисунке ниже приведена диаграмма алгоритма вычисления значения фитнеса для AI. В этом проекте коэффициент сложности равен 2, и мы должны рассмотреть 7^4 = 2041 случаев:
Из приведенной выше диаграммы алгоритма不难看出, если AI разместит свою первую фишку в столбце 0, 1, 2, 4, 5 или 6, игрок всегда сможет разместить оставшиеся две фишки в столбце 3 и выиграть. Для удобства выражения мы используем последовательность для представления различных комбинаций, где первый элемент представляет первый ход AI, второй номер - ответ игрока, а третий номер - ответ AI. "X" представляет любой допустимый ход. Поэтому [0,0,x]=0, и можно вывести, что когда последовательность имеет вид [0,x<>3,x], игрок не может выиграть. Только когда вторая фишка игрока находится в столбце 3, а второй ход AI не находится в столбце 3, AI может выиграть. Поэтому [0,x=3,x<>3] = -1, и таких случаев 6. Финальный результат равен (0+0+...(43 раза)-1*6)/7/7 = -0.12.
По аналогии для других четырех случаев результаты равны -0.12. Если первый ход AI находится в столбце 3, игрок не может выиграть, и AI также не может выиграть, поэтому значение равно 0. AI выбирает ход с наивысшим значением фитнеса, что означает, что он разместит свою фишку в столбце 3.
Та же аналитика может быть применена к последующим ходам AI. В целом, чем выше шансы игрока на победу после хода AI, тем ниже значение фитнеса для AI, и AI выбирает ход с более высоким значением фитнеса, чтобы предотвратить победу игрока. Конечно, если AI может выиграть сам, он будет приоритетно выбирать ход, ведущий к своей победе.
def getPotentialMoves(board, tile, lookAhead):
if lookAhead == 0 or isBoardFull(board):
'''
Если коэффициент сложности равен 0 или доска заполнена,
возвращается список, все значения которого равны 0. Это означает, что
значение фитнеса равно потенциальным ходам для каждого столбца.
В этом случае AI будет случайным образом сбрасывать фишку и терять свою интеллектуальность.
'''
return [0] * BOARDWIDTH
## Определить цвет фишки противника
if tile == RED:
enemyTile = BLACK
else:
enemyTile = RED
potentialMoves = [0] * BOARDWIDTH
## Инициализировать список потенциальных ходов, все значения которого равны 0
for firstMove in range(BOARDWIDTH):
## Перебрать каждый столбец и рассматривать любой ход стороны как firstMove
## Ход другой стороны рассматривается как counterMove
## Здесь наш firstMove относится к ходу AI, а ход противника рассматривается как counterMove
## Сделать глубокую копию доски, чтобы предотвратить взаимное влияние между доской и dupeBoard
dupeBoard = copy.deepcopy(board)
if not isValidMove(dupeBoard, firstMove):
## Если ход размещения черной фишки в столбце, указанном firstMove, недопустим в dupeBoard
continue
## Продолжить с следующим firstMove
makeMove(dupeBoard, tile, firstMove)
## Если это допустимый ход, установить соответствующий цвет ячейки
if isWinner(dupeBoard, tile):
## Если AI выигрывает
potentialMoves[firstMove] = 1
## Выигрышная фишка автоматически получает высокое значение, чтобы показать ее шансы на победу
## Чем больше значение, тем выше шансы на победу, и тем ниже шансы противника на победу
break
## Не вмешиваться в расчет других ходов
else:
if isBoardFull(dupeBoard):
## Если в dupeBoard нет пустых ячеек
potentialMoves[firstMove] = 0
## Невозможно сделать ход
else:
for counterMove in range(BOARDWIDTH):
## Рассмотреть ход противника
dupeBoard2 = copy.deepcopy(dupeBoard)
if not isValidMove(dupeBoard2, counterMove):
continue
makeMove(dupeBoard2, enemyTile, counterMove)
if isWinner(dupeBoard2, enemyTile):
potentialMoves[firstMove] = -1
## Если игрок выигрывает, значение фитнеса для AI в этом столбце является самым низким
break
else:
## Рекурсивно вызвать getPotentialMoves
results = getPotentialMoves(dupeBoard2, tile, lookAhead - 1)
## Использовать представление с плавающей точкой здесь для более точных результатов
## Это гарантирует, что значения в potentialMoves находятся в диапазоне [-1, 1]
potentialMoves[firstMove] += (sum(results)*1.0 / BOARDWIDTH) / BOARDWIDTH
return potentialMoves
Перетаскивайте игровую фишку, определяйте квадрат, в котором находится фишка, проверяйте валидность фишки, вызывайте функцию по падению фишки и завершайте операцию.
def getHumanMove(board, isFirstMove):
draggingToken = False
tokenx, tokeny = None, None
while True:
## Используйте pygame.event.get() для обработки всех событий
for event in pygame.event.get():
if event.type == QUIT: ## Остановить и выйти
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN and not draggingToken and REDPILERECT.collidepoint(event.pos):
## Если тип события - нажатие кнопки мыши, draggingToken - истина, и позиция клика мыши внутри REDPILERECT
draggingToken = True
tokenx, tokeny = event.pos
elif event.type == MOUSEMOTION and draggingToken: ## Если красная фишка перетаскивается
tokenx, tokeny = event.pos ## Обновить позицию перетаскиваемой фишки
elif event.type == MOUSEBUTTONUP and draggingToken:
## Если кнопка мыши отпущена, и фишка перетаскивается
## Если фишка перетащена прямо над игровой доской
if tokeny < YMARGIN and tokenx > XMARGIN and tokenx < WINDOWWIDTH - XMARGIN:
column = int((tokenx - XMARGIN) / SPACESIZE) ## Определить столбец, в который будет падать фишка, на основе x-координаты фишки (0,1...6)
if isValidMove(board, column): ## Если ход фишки допустим
"""
Падать в соответствующий пустой квадрат,
Эта функция только показывает эффект падения
Заполнение квадрата фишкой также можно достичь без этой функции с помощью следующего кода
"""
animateDroppingToken(board, column, RED)
## Установить самый нижний пустой квадрат в пустом столбце в красный цвет
board[column][getLowestEmptySpace(board, column)] = RED
drawBoard(board) ## Нарисовать красную игровую фишку в выброшенном квадрате
pygame.display.update() ## Обновить окно
return
tokenx, tokeny = None, None
draggingToken = False
if tokenx!= None and tokeny!= None: ## Если фишка перетаскивается, отобразить перетаскиваемую фишку
drawBoard(board, {'x':tokenx - int(SPACESIZE / 2), 'y':tokeny - int(SPACESIZE / 2), 'color':RED})
## Применить корректировки к x, y координатам, чтобы при перетаскивании курсор мыши всегда находился в центре фишки
else:
drawBoard(board) ## Когда ход недействителен, после отпускания кнопки мыши, потому что все значения на доске - это None
## При вызове drawBoard выполняемые операции - это отображение двух игровых фишек ниже, что эквивалентно возврату фишки в место, откуда она начинала перетаскиваться
if isFirstMove:
DISPLAYSURF.blit(ARROWIMG, ARROWRECT) ## AI ходит первым, отобразить изображение подсказки по операции
pygame.display.update()
FPSCLOCK.tick()
В приведенном выше коде функция getHumanMove() обрабатывает ход игрока. Функция animateDroppingToken() анимирует падение фишки. Функция getLowestEmptySpace() возвращает самый нижний пустой квадрат в столбце.
Реализуйте функцию для анимации движения компьютера и падения фишек AI в соответствующие позиции.
def animateComputerMoving(board, column):
x = BLACKPILERECT.left ## Левая координата черной фишки внизу
y = BLACKPILERECT.top ## Верхняя координата черной фишки внизу
speed = 1.0
while y > (YMARGIN - SPACESIZE): ## Когда y имеет более большую величину, это означает, что фишка находится ниже окна
y -= int(speed) ## Постепенно уменьшайте y, что означает, что фишка движется вверх
speed += 0.5 ## Увеличивайте скорость уменьшения y
drawBoard(board, {'x':x, 'y':y, 'color':BLACK})
## y постоянно меняется, непрерывно рисуется черная фишка, создавая эффект непрерывного подъема
pygame.display.update()
FPSCLOCK.tick()
## Когда фишка поднимается до верха доски
y = YMARGIN - SPACESIZE ## Сбросьте y, чтобы нижняя часть фишки была выровнена с верхом доски
speed = 1.0
while x > (XMARGIN + column * SPACESIZE): ## Когда x больше x-координаты целевого столбца
x -= int(speed) ## Постепенно уменьшайте x, что означает, что фишка движется влево
speed += 0.5
drawBoard(board, {'x':x, 'y':y, 'color':BLACK})
## В этом случае координата y остается неизменной, что означает, что фишка движется горизонтально в столбец
pygame.display.update()
FPSCLOCK.tick()
## Черная фишка приземляется на вычисленное пустое место
animateDroppingToken(board, column, BLACK)
Выберите наибольшее число из списка возвращаемых potentialMoves в качестве значения фитнеса и случайным образом выберите из тех столбцов с высокими значениями фитнеса в качестве конечной цели движения.
def getComputerMove(board):
potentialMoves = getPotentialMoves(board, BLACK, DIFFICULTY) ## Потенциальные ходы, список с BOARDWIDTH значениями
## Значения в списке связаны с установленным уровнем сложности
bestMoves = [] ## Создайте пустой список bestMoves
bestMoveFitness = -1 ## Поскольку минимальное значение в potentialMoves равно -1, оно служит в качестве нижней границы
print(bestMoveFitness)
for i in range(len(potentialMoves)):
if potentialMoves[i] > bestMoveFitness and isValidMove(board, i):
bestMoveFitness = potentialMoves[i] ## Постепенно обновляйте bestMoves, чтобы каждое значение в bestMoves было наибольшим
## при этом гарантируя, что ход допустим.
for i in range(len(potentialMoves)):
if potentialMoves[i] == bestMoveFitness and isValidMove(board, i):
bestMoves.append(i) ## Список всех столбцов, в которые можно переместить фишку. Этот список может быть пустым, содержать
## только одно значение или несколько значений.
print(bestMoves)
return random.choice(bestMoves) ## Случайным образом выберите один из столбцов, в которые можно переместить фишку, в качестве целевого хода.
Постоянным изменением соответствующих координат фишек достигается эффект анимации падения.
def getLowestEmptySpace(board, column):
## Возвращает самый нижний пустой квадрат в столбце
for y in range(BOARDHEIGHT-1, -1, -1):
if board[column][y] == EMPTY:
return y
return -1
def makeMove(board, player, column):
lowest = getLowestEmptySpace(board, column)
if lowest!= -1:
board[column][lowest] = player
'''
Назначает игроку (красный/черный) самый нижний пустой квадрат в столбце.
Поскольку фишка падает в самый нижний пустой квадрат в столбце,
она считается цветом этого квадрата.
'''
def animateDroppingToken(board, column, color):
x = XMARGIN + column * SPACESIZE
y = YMARGIN - SPACESIZE
dropSpeed = 1.0
lowestEmptySpace = getLowestEmptySpace(board, column)
while True:
y += int(dropSpeed)
dropSpeed += 0.5
if int((y - YMARGIN) / SPACESIZE) >= lowestEmptySpace:
return
drawBoard(board, {'x':x, 'y':y, 'color':color})
pygame.display.update()
FPSCLOCK.tick()
В приведенном выше коде функция makeMove() совершает ход на доске. Функция animateDroppingToken() анимирует падение фишки. Функция getLowestEmptySpace() возвращает самый нижний пустой квадрат в столбце.
Проверьте валидность хода фишки, проверьте, остались ли еще пустые клетки на шахматной доске.
def isValidMove(board, column):
## Проверьте валидность хода фишки
if column < 0 or column >= (BOARDWIDTH) or board[column][0]!= EMPTY:
## Если столбец меньше 0 или больше BOARDWIDTH, или в столбце нет пустой клетки
return False
## Тогда это недопустимый ход, в противном случае он допустим
return True
def isBoardFull(board):
## Если в сетке нет пустых клеток, верните True
for x in range(BOARDWIDTH):
for y in range(BOARDHEIGHT):
if board[x][y] == EMPTY:
return False
return True
В приведенном выше коде функция isValidMove() возвращает True, если ход допустим. Функция isBoardFull() возвращает True, если доска заполнена.
Предоставлены несколько схем для легкого понимания четырех условий победы. Позиции, показанные на схеме, соответствуют экстремальным значениям x и y.
def isWinner(board, tile):
## Проверьте горизонтальное расположение фишек
for x in range(BOARDWIDTH - 3): ## x принимает значения 0, 1, 2, 3
for y in range(BOARDHEIGHT): ## перебираем все строки
## Если x = 0, проверьте, все ли четыре первые фишки в y-й строке одного и того же типа. Это можно использовать для обхода всех горизонтальных расположений фишек, соединенных в ряд из четырех. Если любая пара x, y дает истину, можно определить победу
if board[x][y] == tile and board[x+1][y] == tile and board[x+2][y] == tile and board[x+3][y] == tile:
return True
## Проверьте вертикальное расположение фишек, аналогично горизонтальному
for x in range(BOARDWIDTH):
for y in range(BOARDHEIGHT - 3):
if board[x][y] == tile and board[x][y+1] == tile and board[x][y+2] == tile and board[x][y+3] == tile:
return True
## Проверьте наклон вправо диагональное расположение фишек
for x in range(BOARDWIDTH - 3): ## x принимает значения 0, 1, 2, 3
for y in range(3, BOARDHEIGHT): ## потому что при образовании наклонного вправо диагонали из четырех фишек нижняя фишка должна быть не менее на четыре квадрата выше верхней, то есть y >= 3
if board[x][y] == tile and board[x+1][y-1] == tile and board[x+2][y-2] == tile and board[x+3][y-3] == tile: ## определите, все ли четыре наклонные вправо диагональные фишки одного цвета
return True
## Проверьте наклон влево диагональное расположение фишек, аналогично наклонному вправо диагональному
for x in range(BOARDWIDTH - 3):
for y in range(BOARDHEIGHT - 3):
if board[x][y] == tile and board[x+1][y+1] == tile and board[x+2][y+2] == tile and board[x+3][y+3] == tile:
return True
return False
Наконец, мы создаем главный цикл игры, чтобы держать игру в постоянном движении.
def main():
## Пропущено существующий код
isFirstGame = True ## Инициализируем isFirstGame
while True: ## Держим игру в постоянном движении
runGame(isFirstGame)
isFirstGame = False
def runGame(isFirstGame):
if isFirstGame:
## В начале первой игры
## Позволяем AI сделать первый ход, чтобы игроки могли наблюдать, как играется
turn = COMPUTER
showHelp = True
else:
## Во второй и последующих играх назначайте ходы случайным образом
if random.randint(0, 1) == 0:
turn = COMPUTER
else:
turn = HUMAN
showHelp = False
mainBoard = getNewBoard() ## Создаем начальную пустую структуру доски
while True: ## Главный цикл игры
if turn == HUMAN: ## Если ходит игрок
getHumanMove(mainBoard, showHelp) ## Вызываем метод для хода игрока, детали см. в методе getHumanMove
if showHelp:
## Если есть изображение подсказки, выключаем подсказку после первого хода AI
showHelp = False
if isWinner(mainBoard, RED): ## Если красная фишка (игрок) выигрывает
winnerImg = HUMANWINNERIMG ## Загружаем изображение победы игрока
break ## Выйти из цикла
turn = COMPUTER ## Передаем первый ход AI
else:
## Если ходит AI
column = getComputerMove(mainBoard) ## Вызываем метод для хода AI, детали см. в методе getComputerMove
print(column)
animateComputerMoving(mainBoard, column) ## Перемещаем черную фишку
makeMove(mainBoard, BLACK, column) ## Устанавливаем самый нижний пустой слот в столбце как черный
if isWinner(mainBoard, BLACK):
winnerImg = COMPUTERWINNERIMG
break
turn = HUMAN ## Переключаемся на ход игрока
if isBoardFull(mainBoard):
## Если доска заполнена, это ничья
winnerImg = TIEWINNERIMG
break
На основе алгоритма Монте-Карло этот проект реализовал игру в шахматы между человеком и AI на Python с использованием модуля Pygame. Проект позволил нам познакомиться с основами создания экземпляров и перемещения объектов в Pygame, а также дать предварительное понимание конкретного применения алгоритма Монте-Карло.