Desenvolvimento de um Jogo de Gomoku com Pygame Contra a Inteligência Artificial

Introdução ao Pygame

O Pygame é uma biblioteca de desenvolvimento de jogos de código aberto construída sobre a SDL (Simple DirectMedia Layer). Ela fornece módulos úteis como pygame.display para gerenciar janelas, pygame.sprite para detecção de colisões e sprites, e pygame.mixer para efeitos sonoros, simplificando significativamente o processo de criação de jogos.

Instalação do Pygame

Para instalar o Pygame, certifique-se de ter o Python instalado e execute o seguinte comando no terminal:

pip install pygame

Se estiver utilizando o Python 3, pode ser necessário usar pip3.

Código Básico com Pygame

Um exemplo simples de uma janela do Pygame envolve inicialização, configuração da tela, um loop de eventos principal e tratamento adequado para encerrar o programa:

import pygame
import sys

pygame.init()

tela = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Exemplo Pygame")

rodando = True
while rodando:
    for evento in pygame.event.get():
        if evento.type == pygame.QUIT:
            rodando = False

    tela.fill((255, 255, 255))
    pygame.display.flip()

pygame.quit()
sys.exit()

Implementação do Jogo Gomoku

Abaixo está uma implementação completa do jogo Gomoku (cinco em linha) com jogabilidade contra a IA. O código inclui a lógica do tabuleiro, interface gráfica e um algoritmo de avaliação para as jogadas do computador.

import sys
import random
import pygame
from pygame.locals import *
import pygame.gfxdraw
from collections import namedtuple

Peca = namedtuple('Peca', 'nome valor cor')
Posicao = namedtuple('Posicao', 'x y')

PECA_PRETA = Peca('Preta', 1, (45, 45, 45))
PECA_BRANCA = Peca('Branca', 2, (219, 219, 219))

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


class TabuleiroGomoku:
    def __init__(self, pontos_por_linha):
        self.tamanho = pontos_por_linha
        self.grade = [[0] * pontos_por_linha for _ in range(pontos_por_linha)]

    def posicao_valida(self, pos):
        return self.grade[pos.y][pos.x] == 0

    def colocar_peca(self, peca, pos):
        print(f'{peca.nome} ({pos.x}, {pos.y})')
        self.grade[pos.y][pos.x] = peca.valor
        if self._verificar_vitoria(pos):
            print(f'{peca.nome} venceu!')
            return peca

    def _verificar_vitoria(self, pos):
        valor_atual = self.grade[pos.y][pos.x]
        for direcao in direcoes:
            if self._contar_em_direcao(pos, valor_atual, direcao[0], direcao[1]):
                return True
        return False

    def _contar_em_direcao(self, pos, valor, dx, dy):
        contagem = 1
        for passo in range(1, 5):
            nx, ny = pos.x + passo * dx, pos.y + passo * dy
            if (0 <= nx < self.tamanho and 0 <= ny < self.tamanho and
                    self.grade[ny][nx] == valor):
                contagem += 1
            else:
                break
        for passo in range(1, 5):
            nx, ny = pos.x - passo * dx, pos.y - passo * dy
            if (0 <= nx < self.tamanho and 0 <= ny < self.tamanho and
                    self.grade[ny][nx] == valor):
                contagem += 1
            else:
                break
        return contagem >= 5


# Constantes de Layout
TAMANHO_CASA = 30
PONTOS_LINHA = 19
MARGEM_EXTERNA = 20
LARGURA_BORDA = 4
MARGEM_INTERNA = 4
COMPRIMENTO_BORDA = TAMANHO_CASA * (PONTOS_LINHA - 1) + MARGEM_INTERNA * 2 + LARGURA_BORDA
INICIO_X = INICIO_Y = MARGEM_EXTERNA + LARGURA_BORDA // 2 + MARGEM_INTERNA
ALTURA_TELA = TAMANHO_CASA * (PONTOS_LINHA - 1) + MARGEM_EXTERNA * 2 + LARGURA_BORDA + MARGEM_INTERNA * 2
LARGURA_TELA = ALTURA_TELA + 200

RAIO_PECA = TAMANHO_CASA // 2 - 3
RAIO_PECA_GRANDE = TAMANHO_CASA // 2 + 3
COR_TABULEIRO = (0xE3, 0x92, 0x65)
PRETO = (0, 0, 0)
BRANCO = (255, 255, 255)
VERMELHO = (200, 30, 30)
AZUL = (30, 30, 200)

POSICAO_INFO_X = ALTURA_TELA + RAIO_PECA_GRANDE * 2 + 10


def desenhar_texto(tela, fonte, x, y, texto, cor=(255, 255, 255)):
    texto_renderizado = fonte.render(texto, True, cor)
    tela.blit(texto_renderizado, (x, y))


def obter_ponto_clique(pos_clique):
    px = pos_clique[0] - INICIO_X
    py = pos_clique[1] - INICIO_Y
    if px < -MARGEM_INTERNA or py < -MARGEM_INTERNA:
        return None
    gx = px // TAMANHO_CASA
    gy = py // TAMANHO_CASA
    if px % TAMANHO_CASA > RAIO_PECA:
        gx += 1
    if py % TAMANHO_CASA > RAIO_PECA:
        gy += 1
    if gx >= PONTOS_LINHA or gy >= PONTOS_LINHA:
        return None
    return Posicao(gx, gy)


class IA_Gomoku:
    def __init__(self, tamanho, minha_peca):
        self.tamanho = tamanho
        self.minha_peca = minha_peca
        self.peca_adversaria = PECA_PRETA if minha_peca == PECA_BRANCA else PECA_BRANCA
        self.estado = [[0] * tamanho for _ in range(tamanho)]

    def registrar_jogada_adversaria(self, pos):
        self.estado[pos.y][pos.x] = self.peca_adversaria.valor

    def escolher_melhor_jogada(self):
        melhor_pos = None
        maior_pontuacao = -1
        for y in range(self.tamanho):
            for x in range(self.tamanho):
                if self.estado[y][x] == 0:
                    pontuacao = self._avaliar_posicao(Posicao(x, y))
                    if pontuacao > maior_pontuacao:
                        maior_pontuacao = pontuacao
                        melhor_pos = Posicao(x, y)
                    elif pontuacao == maior_pontuacao and pontuacao > 0:
                        if random.randint(0, 1) == 0:
                            melhor_pos = Posicao(x, y)
        self.estado[melhor_pos.y][melhor_pos.x] = self.minha_peca.valor
        return melhor_pos

    def _avaliar_posicao(self, pos):
        total = 0
        for direcao in direcoes:
            total += self._avaliar_direcao(pos, direcao[0], direcao[1])
        return total

    def _avaliar_direcao(self, pos, dx, dy):
        contagem_amigo, contagem_inimigo = 0, 0
        espaco_amigo, espaco_inimigo = False, False
        bloqueio_amigo, bloqueio_inimigo = 0, 0

        # Avaliação na direção positiva
        for passo in range(1, 6):
            nx, ny = pos.x + passo * dx, pos.y + passo * dy
            if not (0 <= nx < self.tamanho and 0 <= ny < self.tamanho):
                if self._tem_peca_na_adjacencia(pos, dx, dy, 1):
                    bloqueio_amigo += 1
                elif self._tem_peca_na_adjacencia(pos, dx, dy, 2):
                    bloqueio_inimigo += 1
                break
            valor = self.estado[ny][nx]
            if valor == self.minha_peca.valor:
                contagem_amigo += 1
            elif valor == self.peca_adversaria.valor:
                contagem_inimigo += 1
            else:
                if contagem_amigo == 0 and contagem_inimigo == 0:
                    continue
                elif contagem_amigo > 0:
                    espaco_amigo = True
                    break
                else:
                    espaco_inimigo = True
                    break

        # Avaliação na direção negativa (similar)
        # ... (código espelhado omitido por brevidade, mas lógica simétrica)

        # Cálculo da pontuação final baseado nos padrões encontrados
        pontuacao = 0
        if contagem_amigo >= 4:
            pontuacao = 100000
        elif contagem_inimigo >= 4:
            pontuacao = 90000
        elif contagem_amigo == 3:
            pontuacao = 5000 if not espaco_amigo else 4500
        elif contagem_inimigo == 3:
            pontuacao = 4500 if not espaco_inimigo else 4000
        elif contagem_amigo == 2:
            pontuacao = 200
        elif contagem_inimigo == 2:
            pontuacao = 180
        elif contagem_amigo == 1:
            pontuacao = 10
        elif contagem_inimigo == 1:
            pontuacao = 9

        return pontuacao


def desenhar_tabuleiro(tela):
    tela.fill(COR_TABULEIRO)
    pygame.draw.rect(tela, PRETO,
                     (MARGEM_EXTERNA, MARGEM_EXTERNA, COMPRIMENTO_BORDA, COMPRIMENTO_BORDA),
                     LARGURA_BORDA)
    for i in range(PONTOS_LINHA):
        pygame.draw.line(tela, PRETO,
                         (INICIO_Y, INICIO_Y + TAMANHO_CASA * i),
                         (INICIO_Y + TAMANHO_CASA * (PONTOS_LINHA - 1), INICIO_Y + TAMANHO_CASA * i), 1)
        pygame.draw.line(tela, PRETO,
                         (INICIO_X + TAMANHO_CASA * i, INICIO_X),
                         (INICIO_X + TAMANHO_CASA * i, INICIO_X + TAMANHO_CASA * (PONTOS_LINHA - 1)), 1)


def desenhar_peca(tela, pos, cor_peca):
    centro_x = INICIO_X + TAMANHO_CASA * pos.x
    centro_y = INICIO_Y + TAMANHO_CASA * pos.y
    pygame.gfxdraw.aacircle(tela, centro_x, centro_y, RAIO_PECA, cor_peca)
    pygame.gfxdraw.filled_circle(tela, centro_x, centro_y, RAIO_PECA, cor_peca)


def executar_jogo():
    pygame.init()
    tela = pygame.display.set_mode((LARGURA_TELA, ALTURA_TELA))
    pygame.display.set_caption('Gomoku - Jogador vs IA')

    fonte_pequena = pygame.font.SysFont('SimHei', 32)
    fonte_grande = pygame.font.SysFont('SimHei', 72)

    tabuleiro = TabuleiroGomoku(PONTOS_LINHA)
    jogador_atual = PECA_PRETA
    vencedor = None
    ia = IA_Gomoku(PONTOS_LINHA, PECA_BRANCA)

    placar_jogador = 0
    placar_ia = 0

    while True:
        for evento in pygame.event.get():
            if evento.type == QUIT:
                sys.exit()
            elif evento.type == MOUSEBUTTONDOWN and vencedor is None:
                if pygame.mouse.get_pressed()[0]:
                    clique = pygame.mouse.get_pos()
                    ponto = obter_ponto_clique(clique)
                    if ponto and tabuleiro.posicao_valida(ponto):
                        vencedor = tabuleiro.colocar_peca(jogador_atual, ponto)
                        if not vencedor:
                            jogador_atual = PECA_BRANCA
                            ia.registrar_jogada_adversaria(ponto)
                            jogada_ia = ia.escolher_melhor_jogada()
                            vencedor = tabuleiro.colocar_peca(jogador_atual, jogada_ia)
                            if vencedor:
                                placar_ia += 1
                            jogador_atual = PECA_PRETA
                        else:
                            placar_jogador += 1

        desenhar_tabuleiro(tela)
        for y, linha in enumerate(tabuleiro.grade):
            for x, valor in enumerate(linha):
                if valor == PECA_PRETA.valor:
                    desenhar_peca(tela, Posicao(x, y), PECA_PRETA.cor)
                elif valor == PECA_BRANCA.valor:
                    desenhar_peca(tela, Posicao(x, y), PECA_BRANCA.cor)

        # Desenhar informações laterais
        desenhar_texto(tela, fonte_pequena, POSICAO_INFO_X, INICIO_Y, f'Jogador: {placar_jogador}', AZUL)
        desenhar_texto(tela, fonte_pequena, POSICAO_INFO_X, INICIO_Y + 40, f'IA: {placar_ia}', AZUL)

        if vencedor:
            desenhar_texto(tela, fonte_grande,
                           (LARGURA_TELA - 300) // 2,
                           (ALTURA_TELA - 50) // 2,
                           f'{vencedor.nome} venceu!', VERMELHO)

        pygame.display.flip()


if __name__ == '__main__':
    executar_jogo()

Conceitos Chave da Implementação

A estrutura do jogo é dividida em classes prnicipais: TabuleiroGomoku para a lógica do jogo e estado do tabuleiro, e IA_Gomoku para a inteligência artificial que avalia as melhores jogadas usando uma heurística baseada em padrões de ameaças e oportunidades. A interface gráfica é construída diretamente com o Pygame, utilizando funções de desenho para o tabuleiro, peças e informações do jogo.

Tags: Pygame Python Gomoku Desenvolvimento de Jogos Inteligência Artificial

Publicado em 6-19 18:43