Na função 'popularMapa()' introduzimos o parametro 'semente' que aceita um inteiro, que será usado para inicializar o gerador de pRNG. Desta forma introduzimos um elemento deterministico na aleatoriedade da função, gerando mapas com população determinada por esta 'semente'.
364 lines
12 KiB
Python
364 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, semente: int | None = None) -> None:
|
|
if semente is None:
|
|
semente = 1
|
|
random.seed(semente)
|
|
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))
|