import sys import random from types import NoneType import pygame as pg 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 = 0 # pg.FULLSCREEN sep_janelas: int = 10 # fonte a ser usada caminho_fonte = pg.font.match_font("roboto") # 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(0, mapa.dimensao[1]): for pos_x in range(0, 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(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 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 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), ) 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), ) 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) # 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() 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))