Files
game_theory_of_life/modules/gui.py
T
sanctus.hck ff728f53ba correção de apresentação de texto na janela
Definido cor de fundo para o texto (definido para preto,
pygame.Color(0,0,0) )
2026-10-24 18:00:44 +01:00

361 lines
12 KiB
Python

import random
import sys
from types import NoneType
import pygame as pg
from pygame.sprite import Sprite
import modules.estrategia as ModEstrategia
import modules.interaccoes as ModInteraccoes
from modules.agente import Agente
from modules.mapa import Mapa
# 0 para desactivar flags
# pg.FULLSCREEN para fullscreen mode
flags = 0 # pg.FULLSCREEN
sep_janelas: int = 10
# fonte a ser usada
caminho_fonte = pg.font.match_font("roboto")
# caminho_fonte = pg.font.SysFont("roboto", 10)
# classe ElementoUI para gerir elementos de interface (botoes, img, etc)
class ElementoUI(Sprite):
def __init__(self):
Sprite.__init__(self)
self.pos: tuple[int, int]
# classe Frame para gerir diferentes frames dentro da janela principal
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, borda: int = 0, radius=0) -> None:
pg.draw.rect(dest, pg.Color(0, 0, 0, 0), self.rect())
pg.draw.rect(dest, self.cor, self.rect(), borda, border_radius=radius)
def criarFrames(janela_app: pg.Surface) -> tuple[Frame, Frame, Frame, Frame]:
frm_mapa = Frame(
(
janela_app.get_size()[1] - (sep_janelas * 2),
janela_app.get_size()[1] - (sep_janelas * 2),
),
(sep_janelas, sep_janelas),
pg.Color(0, 0, 0),
)
frm_info_mapa = Frame(
(
janela_app.get_size()[0] - frm_mapa.tamanho[0] - (sep_janelas * 3),
(janela_app.get_size()[1] - (sep_janelas * 2)) // 2,
),
(frm_mapa.tamanho[0] + (sep_janelas * 2), sep_janelas),
pg.Color(255, 255, 255),
)
frm_info_pos = Frame(
(
janela_app.get_size()[0] - frm_mapa.tamanho[0] - (sep_janelas * 3),
((frm_info_mapa.tamanho[1] * 2) // 3) - sep_janelas,
),
(
frm_mapa.tamanho[0] + (sep_janelas * 2),
frm_info_mapa.tamanho[1] + (sep_janelas * 2),
),
pg.Color(255, 255, 255),
)
frm_accoes = Frame(
(
janela_app.get_size()[0] - frm_mapa.tamanho[0] - (sep_janelas * 3),
janela_app.get_size()[1]
- frm_info_mapa.tamanho[1]
- frm_info_pos.tamanho[1]
- (sep_janelas * 4),
),
(
frm_mapa.tamanho[0] + 20,
frm_info_mapa.tamanho[1] + frm_info_pos.tamanho[1] + 30,
),
pg.Color(255, 255, 255),
)
return (frm_mapa, frm_info_mapa, frm_info_pos, frm_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 criarMapa(janela_app: pg.Surface, frame: Frame, mapa: Mapa) -> None:
lado: int = frame.tamanho[0] // mapa.dimensao[0]
for pos_y in range(mapa.dimensao[1]):
for pos_x in range(mapa.dimensao[0]):
agente: Agente | None = mapa.posicao((pos_x, pos_y))
cor = devolveCor(agente)
quadrado: pg.Rect = pg.Rect(
10 + (lado * pos_x),
10 + (lado * pos_y),
lado,
lado,
)
pg.draw.rect(janela_app, cor, quadrado, 0)
def popularMapa(mapa: Mapa) -> None:
for pos_y in range(mapa.dimensao[0]):
for pos_x in range(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
for tipo_estrategia in ModEstrategia.lista_estrategias:
stats_estrategias[tipo_estrategia] = 0
for pos_y in range(mapa.dimensao[0]):
for pos_x in range(mapa.dimensao[1]):
tmp_agente = mapa.posicao((pos_y, pos_x))
stats_estrategias[tmp_agente.estrategia] += 1
n_total_agentes += 1
return stats_estrategias
return {}
def actualizarEstatisticas(janela: pg.Surface, frame: Frame, mapa: Mapa) -> None:
frame.desenhar(janela, 5, 10)
font = pg.font.Font(caminho_fonte, 20)
strats: dict[str, int] = prepararEstatistica(mapa)
n_linhas = 0
for strat in strats:
strat_label = font.render(
f"{strat}: {(strats[strat] / mapa.dimensao[0] ** 2) * 100:3.2f}",
True,
pg.Color(255, 255, 255),
pg.Color(0, 0, 0),
)
janela.blit(
strat_label,
(
frame.posicao[0] + (sep_janelas * 2),
frame.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, 5, 10)
# mostrar informação sobre posicao seleccionada
font = pg.font.Font(caminho_fonte, 20)
info_posicao = font.render(
f"{mapa.posicao(pos).estrategia}@{pos}",
True,
pg.Color(255, 255, 255),
pg.Color(0, 0, 0, 0),
)
janela.blit(
info_posicao,
(
frame.posicao[0] + (sep_janelas * 2),
frame.posicao[1] + (sep_janelas * 2),
),
)
def mostrarFrameAccoes(janela: pg.Surface, frm: Frame) -> None:
frm.desenhar(janela, 5, 10)
img_play = pg.image.load(
"assets/play_circle_64dp_E3E3E3_FILL0_wght400_GRAD0_opsz48.png"
)
img_stop = pg.image.load(
"assets/stop_circle_64dp_E3E3E3_FILL0_wght400_GRAD0_opsz48.png"
)
img_step = pg.image.load(
"assets/step_over_64dp_E3E3E3_FILL0_wght400_GRAD0_opsz48.png"
)
img_restart = pg.image.load(
"assets/restart_alt_64dp_E3E3E3_FILL0_wght400_GRAD0_opsz48.png"
)
img_settings = pg.image.load(
"assets/settings_64dp_E3E3E3_FILL0_wght400_GRAD0_opsz48.png"
)
janela.blit(
img_play,
(
frm.posicao[0] + sep_janelas,
frm.posicao[1] + ((frm.tamanho[1] - img_play.get_size()[1]) / 2),
),
)
janela.blit(
img_step,
(
frm.posicao[0] + (sep_janelas + img_play.get_size()[0]),
frm.posicao[1] + ((frm.tamanho[1] - img_step.get_size()[1]) / 2),
),
)
janela.blit(
img_stop,
(
frm.posicao[0] + ((sep_janelas + img_step.get_size()[0]) * 2),
frm.posicao[1] + ((frm.tamanho[1] - img_stop.get_size()[1]) / 2),
),
)
janela.blit(
img_restart,
(
frm.posicao[0] + ((sep_janelas + img_stop.get_size()[0]) * 3),
frm.posicao[1] + ((frm.tamanho[1] - img_restart.get_size()[1]) / 2),
),
)
janela.blit(
img_settings,
(
frm.posicao[0] + ((sep_janelas + img_restart.get_size()[0]) * 5),
frm.posicao[1] + ((frm.tamanho[1] - img_settings.get_size()[1]) / 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
frm_mapa, frm_info_mapa, frm_info_pos, frm_accoes = criarFrames(janela)
# mostrar frames no janela principal
frm_mapa.desenhar(janela, 5, 10)
frm_info_mapa.desenhar(janela, 5, 10)
frm_info_pos.desenhar(janela, 5, 10)
frm_accoes.desenhar(janela, 5, 10)
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: "))
if novo_tamanho > 128:
print("limite de tamanho é de 128")
novo_tamanho = 128
tamanho_mapa = (novo_tamanho, novo_tamanho)
mapa = Mapa(tamanho_mapa)
popularMapa(mapa)
quadrado_seleccionado = None
frm_info_pos.desenhar(janela, 5, 10)
# 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)
quadrado_seleccionado = None
frm_info_pos.desenhar(janela, 5, 10)
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()
quadrado = frm_mapa.tamanho[0] // mapa.dimensao[0]
if pos_rato[0] < 10 or pos_rato[1] < 10:
continue
pos_x = (pos_rato[0] - 10) // quadrado
pos_y = (pos_rato[1] - 10) // quadrado
if pos_x >= mapa.dimensao[0] or pos_y >= mapa.dimensao[1]:
continue
quadrado_novo = pg.Rect(
(pos_x * quadrado) + sep_janelas,
(pos_y * quadrado) + sep_janelas,
quadrado,
quadrado,
)
if quadrado_novo == quadrado_seleccionado:
quadrado_seleccionado = None
frm_info_pos.desenhar(janela, 5, 10)
else:
quadrado_seleccionado = quadrado_novo
# actualizar info na frame_info_pos
pos: tuple[int, int] = (pos_x, pos_y)
actualizarInfoPos(janela, frm_info_pos, mapa, pos)
# desenhar mapa
frm_mapa.desenhar(janela)
criarMapa(janela, frm_mapa, mapa)
# criarTabuleiro(janela, mapa)
# se quadrado estiver seleccionado, mostrá-lo
if quadrado_seleccionado:
pg.draw.rect(
janela,
pg.Color(0, 0, 0, 0),
quadrado_seleccionado,
2,
border_radius=20,
)
# mostrar numero de iteração actual
# mostrar dimensão do mapa
# mostrar estatisticas
actualizarEstatisticas(janela, frm_info_mapa, mapa)
# mostrar frame_accoes
mostrarFrameAccoes(janela, frm_accoes)
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))