import sys import random from types import NoneType import pygame as pg from pygame.sprite import Sprite 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 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(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) 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))