Files
game_theory_of_life/modules/gui.py
Luis Rodrigues 7dca69aba4 implementar modelo de Frames, para simplificar interface gráfica
cada area do ecra ira ter uma Frame, que tem informação sobre tamanho e
posicao dessa area relativamente à janela principal.
2026-03-30 17:47:37 +01:00

321 lines
11 KiB
Python

import sys
import random
from types import NoneType
import pygame as pg
from modules import mapa
from modules.agente import Agente
from modules.mapa import Mapa
import modules.estrategia as ModEstrategia
import modules.interaccoes as ModInteraccoes
# 0 para desactivar flags
# pg.FULLSCREEN para fullscreen mode
flags = pg.FULLSCREEN
sep_janelas: int = 10
class Frame:
def __init__(
self,
tamanho_janela: tuple[int, int],
coord_posicao: tuple[int, int],
cor_fundo: pg.Color = pg.Color(0, 0, 0, 0),
):
self.tamanho = tamanho_janela
self.posicao = coord_posicao
self.surface = pg.Surface(self.tamanho)
self.cor = cor_fundo
def rect(self) -> pg.Rect:
return pg.Rect(self.posicao, self.tamanho)
def desenhar(self, dest: pg.Surface) -> None:
pg.draw.rect(dest, pg.Color(0, 0, 0, 0), self.rect())
pg.draw.rect(dest, self.cor, self.rect(), 5, border_radius=20)
def criarFrames(janela_app: pg.Surface) -> tuple[Frame, Frame, Frame, Frame]:
frame_mapa = Frame(
(
janela_app.get_size()[1] - (sep_janelas) * 2,
janela_app.get_size()[1] - (sep_janelas * 2),
),
(sep_janelas, sep_janelas),
pg.Color(255, 255, 0, 0),
)
frame_info_mapa = Frame(
(
janela_app.get_size()[0] - frame_mapa.tamanho[0] - (sep_janelas * 3),
(janela_app.get_size()[1] - (sep_janelas * 2)) // 2,
),
(frame_mapa.tamanho[0] + (sep_janelas * 2), sep_janelas),
pg.Color(0, 0, 255, 0),
)
frame_info_pos = Frame(
(
janela_app.get_size()[0] - frame_mapa.tamanho[0] - (sep_janelas * 3),
((frame_info_mapa.tamanho[1] * 2) // 3) - sep_janelas,
),
(
frame_mapa.tamanho[0] + (sep_janelas * 2),
frame_info_mapa.tamanho[1] + (sep_janelas * 2),
),
pg.Color(0, 255, 0, 0),
)
frame_accoes = Frame(
(
janela_app.get_size()[0] - frame_mapa.tamanho[0] - (sep_janelas * 3),
janela_app.get_size()[1]
- frame_info_mapa.tamanho[1]
- frame_info_pos.tamanho[1]
- (sep_janelas * 4),
),
(
frame_mapa.tamanho[0] + 20,
frame_info_mapa.tamanho[1] + frame_info_pos.tamanho[1] + 30,
),
pg.Color(255, 0, 0, 0),
)
return (frame_mapa, frame_info_mapa, frame_info_pos, frame_accoes)
def devolveCor(agente: Agente) -> tuple[int, int, int, int]:
if type(agente) is None:
return (0, 0, 0, 0)
else:
match agente.estrategia:
case "Neutro":
return (255, 255, 255, 0)
case "Tit4Tat":
return (255, 255, 0, 0)
case "Vingativo":
return (255, 0, 0, 0)
case "Lunatico":
return (0, 0, 255, 0)
case _:
return (0, 0, 0, 0)
def criarTabuleiro(
surface: pg.Surface,
mapa: Mapa,
) -> None:
dimensao_tabuleiro: tuple[int, int] = mapa.dimensao
dimensoes_surface_principal: tuple[int, int] = surface.get_size()
max_altura: int = int((dimensoes_surface_principal[1] - 20) / dimensao_tabuleiro[0])
# max_largura: int = int(
# (dimensoes_surface_principal[0] - 20) / dimensao_tabuleiro[1]
# )
tamanho_quadrado: int = max_altura # min([max_altura, max_largura])
cor_quadrado: tuple[int, int, int, int]
for pos_y in range(0, dimensao_tabuleiro[0]):
for pos_x in range(0, dimensao_tabuleiro[1]):
agente: Agente | None = mapa.posicao((pos_x, pos_y))
cor_quadrado = devolveCor(agente)
pg.draw.rect(
surface,
cor_quadrado,
[
10 + (tamanho_quadrado * pos_x),
10 + (tamanho_quadrado * pos_y),
tamanho_quadrado,
tamanho_quadrado,
],
0,
)
def popularMapa(mapa: Mapa) -> None:
for pos_y in range(0, mapa.dimensao[0]):
for pos_x in range(0, mapa.dimensao[1]):
# escolher uma estrategia aleatoria
estrategia = random.choice(ModEstrategia.listaNomesEstrategias())
# criar Agente com estrategia aleatoria e colocar na posicao y e x
mapa.mundo[pos_y][pos_x] = Agente(estrategia, (pos_x, pos_y))
def prepararEstatistica(mapa: Mapa | None) -> dict[str, int]:
if mapa is None:
print("SEM MAPA!!")
else:
stats_estrategias: dict[str, int] = {}
n_total_agentes: int = 0
# print("estatisticas mapa:")
for tipo_estrategia in ModEstrategia.lista_estrategias:
stats_estrategias[tipo_estrategia] = 0
for pos_y in range(0, mapa.dimensao[0]):
for pos_x in range(0, mapa.dimensao[1]):
tmp_agente = mapa.posicao((pos_y, pos_x))
stats_estrategias[tmp_agente.estrategia] += 1
n_total_agentes += 1
# for tipo_estrategia in stats_estrategias:
# print(
# f"{tipo_estrategia}: {
# (stats_estrategias[tipo_estrategia] / n_total_agentes) * 100:3.2f}"
# )
return stats_estrategias
return {}
def mostrarMapa(mapa: Mapa | None) -> None:
# verificacao
if isinstance(mapa, NoneType):
raise TypeError("objecto passado do tipo None")
# mostrar mapa com pontuação em cada posição
for pos_y in range(0, mapa.dimensao[0]):
for pos_x in range(0, mapa.dimensao[1]):
tmp_agente: Agente | None = mapa.posicao((pos_x, pos_y))
if not isinstance(tmp_agente, NoneType):
print(f"{tmp_agente.estrategia[0]:2}", end="")
print("")
def actualizarEstatisticas(
janela: pg.Surface, frame_info_mapa: Frame, mapa: Mapa
) -> None:
frame_info_mapa.desenhar(janela)
font = pg.font.Font(None, 20)
dict_estrat = prepararEstatistica(mapa)
n_linhas = 0
for strat in dict_estrat:
strat_label = font.render(
f"{strat}: {(dict_estrat[strat] / mapa.dimensao[0] ** 2) * 100:3.2f}",
False,
pg.Color(255, 255, 255, 0),
)
janela.blit(
strat_label,
(
frame_info_mapa.posicao[0] + (sep_janelas * 2),
frame_info_mapa.posicao[1] + (sep_janelas * 2) + (30 * n_linhas),
),
)
n_linhas += 1
def actualizarInfoPos(
janela: pg.Surface, frame: Frame, mapa: Mapa, pos: tuple[int, int]
) -> None:
# limpar frame_info_mapa
frame.desenhar(janela)
# mostrar informação sobre posicao seleccionada
font = pg.font.Font(None, 20)
info_posicao = font.render(
f"{mapa.posicao(pos).estrategia}@{pos}",
False,
pg.Color(255, 255, 255, 0),
)
janela.blit(
info_posicao,
(
frame.posicao[0] + (sep_janelas * 2),
frame.posicao[1] + (sep_janelas * 2),
),
)
def main(mapa: Mapa | None, tamanho_mapa: tuple[int, int]):
if isinstance(mapa, NoneType):
raise TypeError("não foi passado mapa (tipo None)")
pg.init()
janela = pg.display.set_mode((1280, 800), flags)
pg.display.set_caption("Game Theory of Life")
clock = pg.time.Clock()
running: bool = True
quadrado_seleccionado: pg.Rect | None = None
# criar frames
frame_mapa, frame_info_mapa, frame_info_pos, frame_accoes = criarFrames(janela)
while running:
if quadrado_seleccionado:
pg.draw.rect(
janela,
pg.Color(0, 0, 0, 0),
quadrado_seleccionado,
2,
border_radius=10,
)
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
if event.type == pg.KEYDOWN:
# sair do mapa com 'q' (quit)
if event.key == pg.K_q:
running = False
# mudar mapa com 'c' (change)
if event.key == pg.K_c:
novo_tamanho = int(input("tamanho do mapa: "))
tamanho_mapa = (novo_tamanho, novo_tamanho)
janela.fill(0)
mapa = Mapa(tamanho_mapa)
popularMapa(mapa)
# correr proxima iteração com 'n' (next)
if event.key == pg.K_n:
ModInteraccoes.correrInteraccoesEntreAgentes(mapa)
# gerar novo mapa e popular (fazer reset) com 'r'
if event.key == pg.K_r:
mapa = Mapa(tamanho_mapa)
popularMapa(mapa)
if event.type == pg.MOUSEBUTTONDOWN:
# botao 1 do rato clicado, seleccionar ou tirar selecao
if pg.mouse.get_pressed(num_buttons=3)[0]:
pos_rato: tuple[int, int] = pg.mouse.get_pos()
tamanho_quadrado = int(
(janela.get_size()[1] - 20) / mapa.dimensao[0]
)
if pos_rato[0] < 10 or pos_rato[1] < 10:
continue
pos_x = int((pos_rato[0] - 10) / tamanho_quadrado)
pos_y = int((pos_rato[1] - 10) / tamanho_quadrado)
if pos_x >= mapa.dimensao[0] or pos_y >= mapa.dimensao[1]:
continue
quadrado_novo = pg.Rect(
(pos_x * tamanho_quadrado) + 10,
(pos_y * tamanho_quadrado) + 10,
tamanho_quadrado,
tamanho_quadrado,
)
if quadrado_novo == quadrado_seleccionado:
quadrado_seleccionado = None
frame_info_pos.desenhar(janela)
else:
quadrado_seleccionado = quadrado_novo
agente = mapa.posicao((pos_x, pos_y))
print(f"{agente.posicao} com {agente.estrategia}")
# desenhar mapa
frame_mapa.desenhar(janela)
criarTabuleiro(janela, mapa)
# mostrar numero de iteração actual
# mostrar dimensão do mapa
# mostrar estatisticas
actualizarEstatisticas(janela, frame_info_mapa, mapa)
# mostrar borda da posicao seleccionado com o rato
frame_info_pos.desenhar(janela)
if quadrado_seleccionado:
pg.draw.rect(
janela,
pg.Color(0, 0, 0, 0),
quadrado_seleccionado,
2,
border_radius=20,
)
# actualizar info na frame_info_pos
actualizarInfoPos(janela, frame_info_pos, mapa, (pos_x, pos_y))
# mostrar frame accoes
frame_accoes.desenhar(janela)
pg.display.flip()
clock.tick(60)
pg.quit()
sys.exit()
if __name__ == "__main__":
mapa_mundo: Mapa = Mapa((128, 128))
app = main(mapa_mundo, tamanho_mapa=(128, 128))