/
GraphTreeHeap
/
NeuroFighter
Обзор
Документация
Войти
/
GraphTreeHeap
/
NeuroFighter
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/game.py
746 строк
35 KB
FedMam
another sb pporec
13 окт 2025, 05:28
13 окт 2025, 05:28
1303001
Код
Авторство
О чём код?
import pygame import graphics graphics.init_graphics() import robotics import neural_ppo_singlehead import neural_ppolstm_singlehead from util import hold_fight_universal import random import numpy as np import torch import stable_baselines3 import sb3_contrib from graphics import screen, clock, font, WINDOW_WIDTH, WINDOW_HEIGHT, SCALE, ENABLE_MUSIC from mechanics import * from typing import * BLACK_SCREEN_FRAMES = FPS // 2 INTRO_FRAMES = FPS * 3 def black_screen(frames: int): screen.fill((0x00, 0x00, 0x00)) pygame.display.flip() for _ in range(frames): clock.tick(FPS) def hold_match(player_character: Character, opponent_character: Character, player_network: Any | None, opponent_network: Any | None): player_score = 0 opponent_score = 0 # rounds 1 and 2 for round in (1, 2): result = hold_fight_universal(True, player_character, opponent_character, player_network, opponent_network, player_opponent_reversed=round == 2, round=round) if result == 1: player_score += 1 elif result == -1: opponent_score += 1 if round == 1: black_screen(BLACK_SCREEN_FRAMES) if player_score == 2: return 1 elif opponent_score == 2: return -1 # round 3 black_screen(BLACK_SCREEN_FRAMES) result = hold_fight_universal(True, player_character, opponent_character, player_network, opponent_network, player_opponent_reversed=False, round=3) if result == 1: player_score += 1 elif result == -1: opponent_score += 1 if player_score > opponent_score: return 1 else: return -1 def intro_screen(player_character: CharacterDescription, opponent_character: CharacterDescription): letters_revealed = -1 if ENABLE_MUSIC: pygame.mixer.music.load('assets/music/15a-intro.wav') pygame.mixer.music.play(loops=1) for frame in range(INTRO_FRAMES): clock.tick(FPS) if frame % 2 == 0: letters_revealed += 1 screen.fill((0xff, 0xff, 0xff)) pygame.draw.rect(screen, player_character.color, (0, 0, WINDOW_WIDTH * SCALE, (WINDOW_HEIGHT // 2 - 4) * SCALE)) pygame.draw.rect(screen, opponent_character.color, (0, (WINDOW_HEIGHT // 2 + 4) * SCALE, WINDOW_WIDTH * SCALE, (WINDOW_HEIGHT // 2 - 4) * SCALE)) # characters pygame.draw.rect(screen, tuple(map(lambda rgb: rgb // 2, player_character.color)), ((WINDOW_HEIGHT // 6) * SCALE, (WINDOW_HEIGHT // 6) * SCALE, (WINDOW_HEIGHT // 6) * SCALE, (WINDOW_HEIGHT // 6) * SCALE)) pygame.draw.rect(screen, player_character.color, ((WINDOW_HEIGHT // 6 + 2) * SCALE, (WINDOW_HEIGHT // 6 + 2) * SCALE, (WINDOW_HEIGHT // 6 - 4) * SCALE, (WINDOW_HEIGHT // 6 - 4) * SCALE)) pygame.draw.rect(screen, tuple(map(lambda rgb: rgb // 2, opponent_character.color)), ((WINDOW_WIDTH - WINDOW_HEIGHT // 3) * SCALE, (WINDOW_HEIGHT - WINDOW_HEIGHT // 3) * SCALE, (WINDOW_HEIGHT // 6) * SCALE, (WINDOW_HEIGHT // 6) * SCALE)) pygame.draw.rect(screen, opponent_character.color, ((WINDOW_WIDTH - WINDOW_HEIGHT // 3 + 2) * SCALE, (WINDOW_HEIGHT - WINDOW_HEIGHT // 3 + 2) * SCALE, (WINDOW_HEIGHT // 6 - 4) * SCALE, (WINDOW_HEIGHT // 6 - 4) * SCALE)) # eyes pygame.draw.rect(screen, (0xff, 0xff, 0xff), ((WINDOW_HEIGHT // 4 - 4 - 4) * SCALE, (WINDOW_HEIGHT // 6 + 6) * SCALE, 4 * SCALE, 8 * SCALE)) pygame.draw.rect(screen, (0xff, 0xff, 0xff), ((WINDOW_HEIGHT // 4 + 4) * SCALE, (WINDOW_HEIGHT // 6 + 6) * SCALE, 4 * SCALE, 8 * SCALE)) pygame.draw.rect(screen, (0xff, 0xff, 0xff), ((WINDOW_WIDTH - WINDOW_HEIGHT // 4 - 4 - 4) * SCALE, (WINDOW_HEIGHT - WINDOW_HEIGHT // 3 + 6) * SCALE, 4 * SCALE, 8 * SCALE)) pygame.draw.rect(screen, (0xff, 0xff, 0xff), ((WINDOW_WIDTH - WINDOW_HEIGHT // 4 + 4) * SCALE, (WINDOW_HEIGHT - WINDOW_HEIGHT // 3 + 6) * SCALE, 4 * SCALE, 8 * SCALE)) # letters pl_name_shadow = font.render(player_character.name[:letters_revealed].upper(), False, tuple(map(lambda rgb: rgb // 2, player_character.color))) pl_name_bright = font.render(player_character.name[:letters_revealed].upper(), False, (0xff, 0xff, 0xff)) opp_name_shadow = font.render(opponent_character.name[max(0, len(opponent_character.name) - letters_revealed):].upper(), False, tuple(map(lambda rgb: rgb // 2, opponent_character.color))) opp_name_bright = font.render(opponent_character.name[max(0, len(opponent_character.name) - letters_revealed):].upper(), False, (0xff, 0xff, 0xff)) screen.blit(pl_name_shadow, ((WINDOW_HEIGHT // 6 * 2 + 8 + 1) * SCALE, (WINDOW_HEIGHT // 4 - 4 + 1) * SCALE)) screen.blit(pl_name_bright, ((WINDOW_HEIGHT // 6 * 2 + 8) * SCALE, (WINDOW_HEIGHT // 4 - 4) * SCALE)) screen.blit(opp_name_shadow, ((WINDOW_WIDTH - WINDOW_HEIGHT // 6 * 2 - 8 - 8 * min(len(opponent_character.name), letters_revealed) + 1) * SCALE, (WINDOW_HEIGHT // 4 * 3 - 4 + 1) * SCALE)) screen.blit(opp_name_bright, ((WINDOW_WIDTH - WINDOW_HEIGHT // 6 * 2 - 8 - 8 * min(len(opponent_character.name), letters_revealed)) * SCALE, (WINDOW_HEIGHT // 4 * 3 - 4) * SCALE)) pygame.display.flip() for event in pygame.event.get(): if event.type == pygame.QUIT: graphics.deinit_graphics() exit() CHARACTER_WIN_QUOTES = [ 'Now you have experienced the power of my Dragon Fire!', # Zhan Shi 'Ha, thank you for a refreshing battle!', # Vortex 'I am hiding. You will not see me until you receive the decisive strike.', # Murasaki 'Mission completed successfully, boss. Returning to base.', # Cobra 'Gettin\' a little fizzy and dizzy, aren\'t ya?', # Raio 'Only those to possess the true Russian spirit can withstand the Siberian cold!', # Metelitsa 'I am convinced that honor and valor always triumph over insolence and brute force.', # Ironclad 'Can you survive for at least a month in the forest? No? Then how d\'ya expect to defeat me?', # Raptor 'No matter what one says, we Aztecs are still the true kings of our land.', # Tecolotl 'Oh my, I hope I didn\'t hurt you too much, did I?', # Brise 'Poor landlubber. Ye look like a helpless fish on a hook, hahaarrr!', # Dreadnought 'Remember not to deal with Crusher next time, loser!', # Crusher 'I have always been doing things the precise and scientifically proven way. That\'s the key to my success.', # Dr.Oxide 'Pathetic humans, fall down onto your knees before The Agent.', # The Agent ] '''unused CHARACTER_LOSE_QUOTES = [ 'I never thought that there is a power in this world that is stronger than mine!', # Zhan Shi 'Oh no. Guess I\'ve made a number of wrong moves.', # Vortex 'Grrrr! How in the world could I have lost! I want a rematch!', # Murasaki 'Gekko-3, do you read me? I\'ve been beaten! Repeat: I\'ve been beaten! Do you read me? Over!..', # Cobra 'I\'m really surprised. Usually, people avoid even touching me.', # Raio 'Congratulations on having a heart so warm that even my frost cannot do anything to it.', # Metelitsa 'My respect goes to you. I have had the honor of fighting and being defeated by one stronger than me.', # Ironclad 'That was impressive, mate! I\'m sure you\'d make a nice forest survivor!', # Raptor 'Guess I should train more to become a warrior as skilled as my ancestors.', # Tecolotl 'I can cause tornadoes that blow away cars and trees, but not you! Amazing!', # Brise 'Avast! Enough of that! Didn\'t expect such agility from ye, matey!', # Dreadnought 'Defeated? Wha-at? Nooooo! I can\'t believe', # Crusher 'No! What did I do wrong? Don\'t I deserve the title of Doctor?', # Dr.Oxide 'My mission on Earth is lost. But don\'t rejoice prematurely, humans. My masters will return. Heh-heh-heh...', # The Agent ]''' def win_lose_screen(player_character: CharacterDescription, opponent_character: CharacterDescription, win: bool, campaign: bool=False) -> bool: screen.fill((0x00, 0x00, 0x00)) message = 'YOU WIN!!!' if win else 'GAME OVER' msg_surf = font.render(message, False, player_character.color) screen.blit(msg_surf, ((WINDOW_WIDTH // 2 - 4 * len(message)) * SCALE, (WINDOW_HEIGHT // 2 - 8) * SCALE)) btn_message = 'PRESS ENTER TO CONTINUE' screen.blit(font.render(btn_message, False, (0xff, 0xff, 0xff)), ((WINDOW_WIDTH // 2 - 4 * len(btn_message)) * SCALE, (WINDOW_HEIGHT // 2 + 4) * SCALE)) if campaign and not win: btn_message2 = 'PRESS X/Z TO QUIT' screen.blit(font.render(btn_message2, False, (0xff, 0xff, 0xff)), ((WINDOW_WIDTH // 2 - 4 * len(btn_message2)) * SCALE, (WINDOW_HEIGHT // 2 + 4 + 12) * SCALE)) if win: quote_name = player_character.name.upper() quote = CHARACTER_WIN_QUOTES[player_character.character_id] quote_clr = player_character.color else: quote_name = opponent_character.name.upper() quote = CHARACTER_WIN_QUOTES[opponent_character.character_id] quote_clr = opponent_character.color quote = f"\"{quote.upper()}\"" cols = (WINDOW_WIDTH - 8) // 8 quote_rows = math.ceil(len(quote) / cols) for row_i in range(quote_rows): screen.blit(font.render(quote[row_i * cols : (row_i + 1) * cols], False, (0xff, 0xff, 0xff)), (4 * SCALE, (WINDOW_HEIGHT - 4 - 12 - 12 * quote_rows + 12 * row_i) * SCALE)) screen.blit(font.render(quote_name, False, quote_clr), ((WINDOW_WIDTH - 4 - 8 * len(quote_name)) * SCALE, (WINDOW_HEIGHT - 4 - 12) * SCALE)) pygame.display.flip() # music if ENABLE_MUSIC: pygame.mixer.music.load('assets/music/' + ('15b-win.wav' if win else '15c-lose.wav')) pygame.mixer.music.play(loops=1) while True: clock.tick(FPS) exited = False continued = False for event in pygame.event.get(): if event.type == pygame.QUIT: graphics.deinit_graphics() exit() elif event.type == pygame.KEYDOWN: if event.key in [pygame.K_x, pygame.K_z, pygame.K_RETURN]: continued = event.key == pygame.K_RETURN exited = True break if exited: if ENABLE_MUSIC: pygame.mixer.music.stop() return continued CHARACTERS_INFOS_HEADER = ('LOCATION:', 'POWERS:', 'ATTACK:', 'SPEED:', 'JUMP:') CHARACTERS_INFOS = [ ('CHINA', 'FIRE', 3, 2, 3), # Zhan Shi ('PACIFIC OCEAN', 'WATER', 4, 1, 4), # Vortex ('JAPAN', 'NINJITSU', 4, 5, 3), # Murasaki ('NEW YORK CITY', 'TECH', 5, 2, 5), # Cobra ('BRAZIL', 'ELECTRO', 5, 3, 1), # Raio ('SIBERIA', 'ICE', 3, 3, 2), # Metelitsa ('SCHWARZWALD', 'METAL', 5, 1, 1), # Ironclad ('CONGO BASIN', 'NATURE', 1, 5, 5), # Raptor ('MEXICO', 'SAND', 5, 3, 4), # Tecolotl ('MEDITERRANEAN', 'WIND', 3, 4, 4), # Brise ] CHARACTERS_INFOS = [(loc, pwr, "■" * atk, "■" * spd, "■" * jmp) for loc, pwr, atk, spd, jmp in CHARACTERS_INFOS] def character_select_campaign() -> CharacterDescription: current_char_id = 0 prev_char_id = -1 while True: clock.tick(FPS) if current_char_id != prev_char_id: screen.fill((0x00, 0x00, 0x00)) # character char_clr = CHARACTERS[current_char_id].color white_clr = (0xff, 0xff, 0xff) char_surf = font.render(CHARACTERS[current_char_id].name.upper(), False, char_clr) screen.blit(char_surf, ((WINDOW_WIDTH // 2 - 4 * len(CHARACTERS[current_char_id].name)) * SCALE, (4 + 12) * SCALE)) # messages msg1 = 'PLEASE SELECT CHARACTER' screen.blit(font.render(msg1, False, white_clr), ((WINDOW_WIDTH // 2 - 4 * len(msg1)) * SCALE, 4 * SCALE)) msg2 = 'PRESS ENTER TO CONFIRM' screen.blit(font.render(msg2, False, white_clr), ((WINDOW_WIDTH // 2 - 4 * len(msg2)) * SCALE, (WINDOW_HEIGHT - 12) * SCALE)) # arrows screen.blit(font.render('<', False, white_clr), (4 * SCALE, (4 + 12) * SCALE)) screen.blit(font.render('>', False, white_clr), ((WINDOW_WIDTH - 4 - 8) * SCALE, (4 + 12) * SCALE)) # info for info_i in range(5): screen.blit(font.render(CHARACTERS_INFOS_HEADER[info_i], False, white_clr), (4 * SCALE, (WINDOW_HEIGHT - 12 * (6 - info_i)) * SCALE)) screen.blit(font.render(CHARACTERS_INFOS[current_char_id][info_i], False, char_clr), ((4 + 8 * 10) * SCALE, (WINDOW_HEIGHT - 12 * (6 - info_i)) * SCALE)) pygame.display.flip() prev_char_id = current_char_id for event in pygame.event.get(): if event.type == pygame.QUIT: graphics.deinit_graphics() exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: current_char_id = (current_char_id - 1) % len(CHARACTERS) elif event.key == pygame.K_RIGHT: current_char_id = (current_char_id + 1) % len(CHARACTERS) elif event.key in [pygame.K_x, pygame.K_z, pygame.K_RETURN]: return CHARACTERS[current_char_id] def character_select_2player() -> tuple[CharacterDescription, CharacterDescription]: current_pl_char_id = 0 prev_pl_char_id = -1 current_opp_char_id = 0 prev_opp_char_id = -1 all_characters = CHARACTERS[:] + BOSS_CHARACTERS[:] while True: clock.tick(FPS) if current_pl_char_id != prev_pl_char_id or current_opp_char_id != prev_opp_char_id: screen.fill((0x00, 0x00, 0x00)) # vertical bar pygame.draw.rect(screen, (0xff, 0xff, 0xff), ((WINDOW_WIDTH // 2 - 1) * SCALE, 0, 2 * SCALE, (WINDOW_HEIGHT - 16) * SCALE)) # messages white_clr = (0xff, 0xff, 0xff) pl_chr_msgs = ('SELECT PLAYER 1', 'CHARACTER', '(</>)') for i, msg in enumerate(pl_chr_msgs): screen.blit(font.render(msg, False, white_clr), (4 * SCALE, (4 + 12 * i) * SCALE)) opp_chr_msgs = ('SELECT PLAYER 2', 'CHARACTER', '(Z/X)') for i, msg in enumerate(opp_chr_msgs): screen.blit(font.render(msg, False, white_clr), ((WINDOW_WIDTH - 4 - 8 * len(msg)) * SCALE, (4 + 12 * i) * SCALE)) final_msg = 'PRESS ENTER TO CONFIRM' screen.blit(font.render(final_msg, False, white_clr), ((WINDOW_WIDTH // 2 - 4 * len(final_msg)) * SCALE, (WINDOW_HEIGHT - 12) * SCALE)) # characters pl_char_clr = all_characters[current_pl_char_id].color pl_char_name = all_characters[current_pl_char_id].name.upper() opp_char_clr = all_characters[current_opp_char_id].color opp_char_name = all_characters[current_opp_char_id].name.upper() screen.blit(font.render(pl_char_name, False, pl_char_clr), ((WINDOW_WIDTH // 4 - 4 * len(pl_char_name)) * SCALE, 52 * SCALE)) screen.blit(font.render(opp_char_name, False, opp_char_clr), ((WINDOW_WIDTH * 3 // 4 - 4 * len(opp_char_name)) * SCALE, 52 * SCALE)) pygame.display.flip() prev_pl_char_id = current_pl_char_id prev_opp_char_id = current_opp_char_id for event in pygame.event.get(): if event.type == pygame.QUIT: graphics.deinit_graphics() exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: current_pl_char_id = (current_pl_char_id - 1) % len(all_characters) elif event.key == pygame.K_RIGHT: current_pl_char_id = (current_pl_char_id + 1) % len(all_characters) elif event.key == pygame.K_z: current_opp_char_id = (current_opp_char_id - 1) % len(all_characters) elif event.key == pygame.K_x: current_opp_char_id = (current_opp_char_id + 1) % len(all_characters) elif event.key == pygame.K_RETURN: return all_characters[current_pl_char_id], all_characters[current_opp_char_id] def campaign(player_character: CharacterDescription, opponent: Any, seed: int | None=None): rand = random.Random(seed) opponent_characters = [char for char in CHARACTERS if char.character_id != player_character.character_id] rand.shuffle(opponent_characters) opponent_characters += BOSS_CHARACTERS[:] for opponent_character in opponent_characters: black_screen(BLACK_SCREEN_FRAMES) intro_screen(player_character, opponent_character) black_screen(BLACK_SCREEN_FRAMES) while True: match_result = hold_match(player_character, opponent_character, None, opponent) if match_result == -1: if not win_lose_screen(player_character, opponent_character, False, campaign=True): return black_screen(BLACK_SCREEN_FRAMES) intro_screen(player_character, opponent_character) black_screen(BLACK_SCREEN_FRAMES) else: break win_lose_screen(player_character, opponent_character, True, campaign=True) GAME_OPPONENTS: list[tuple[str, str, Any]] = [] def init_game_opponents(): global GAME_OPPONENTS GAME_OPPONENTS = [] rand = random.Random() def rand_seed(): return rand.randint(0, 0xffffffff) # PPO for name, desc, ppo_file in [ ('PPO: DEFAULT', 'PPO agent with an engineered reward system which rewards clever actions and penalizes stupid actions', 'trained/ppo/agent.pth'), ('PPO: NO SELF-PLAY', 'PPO agent, same as Default but trained only on bots and not via self-play; go ahead and compare it with Default!', 'trained/ppo/agent_before_self_play.pth'), ('PPO: OBJECTIVE', 'PPO agent with an objective reward system which rewards and penalizes nothing than dealing/taking damage and winning', 'trained/ppo-objective/agent.pth'), ('PPO: AGGRESSIVE', 'PPO agent with an aggressive reward system which rewards dealing damage more than how it penalizes taking damage', 'trained/ppo-aggressive/agent.pth'), ('PPO: DEFENSIVE', 'PPO agent with a defensive reward system which penalizes taking damage more than how it rewards dealing damage', 'trained/ppo-defensive/agent.pth'), ('PPO: MASTER', 'PPO agent trained for twice as many iterations as Default', 'trained/ppo-master/agent.pth'), ('PPO: MASTER NO SELF-PLAY', 'PPO agent, same as Master but trained only on bots', 'trained/ppo-master/agent_before_self_play.pth'), ('PPO: RETIRED CHAMPION', 'A PPO agent instance which was trained long ago, before fixing a serious bug and before a massive change in game balance, but it still plays good', 'trained/ppo-retired-champion/agent.pth'), ('PPO: LONG EPISODES', 'A PPO agent trained with 5x less iterations than Default but 10x more trajectories per iteration', 'trained/ppo-long-episodes/agent.pth'), ]: agent = neural_ppo_singlehead.PPOAgentWrapper(logging=False) agent.load_model(ppo_file) GAME_OPPONENTS.append((name, desc, agent.agent)) for event in pygame.event.get(): if event.type == pygame.QUIT: graphics.deinit_graphics() exit() # Failed LSTM lstm_agent = neural_ppolstm_singlehead.PPOAgentWrapperLSTM(logging=False) lstm_agent.load_model('trained/lstm/agent.pth') lstmr_agent = neural_ppolstm_singlehead.PPOAgentWrapperLSTM(logging=False, agent_class=neural_ppolstm_singlehead.PPOAgentLSTMResidual) lstmr_agent.load_model('trained/lstmr/agent.pth') GAME_OPPONENTS.append( ('PPO+LSTM: DEFAULT', 'My attempt to make a PPO+LSTM agent. It experiences LSTM cell norm explosion but still plays decently in some way', lstm_agent.agent) ) GAME_OPPONENTS.append( ('PPO+LSTM: RESIDUAL', 'Same as above, but with added residual connections which bypass the LSTM cell. Plays very good, despite still experiencing cell norm explosion', lstmr_agent.agent) ) for event in pygame.event.get(): if event.type == pygame.QUIT: graphics.deinit_graphics() exit() # stable baselines sb_ppo = stable_baselines3.PPO.load('trained/sb-ppo/agent') GAME_OPPONENTS.append( ('SB-PPO', 'stable_baselines3\'s implementaion of PPO, trained on all my other agents but not against itself', sb_ppo) ) sb_pporec = sb3_contrib.RecurrentPPO.load('trained/sb-pporec/agent') GAME_OPPONENTS.append( ('SB-RECPPO', 'sb3_contrib\'s implementaion of Recurrent PPO, trained on all my other agents but not against itself', sb_pporec) ) sb_pporec2 = sb3_contrib.RecurrentPPO.load('trained/sb-pporec2/agent') GAME_OPPONENTS.append( ('SB-RECPPO-2', 'sb3_contrib\'s implementaion of Recurrent PPO, trained as the previous one but for 3 times more iterations', sb_pporec2) ) sb_deepq = stable_baselines3.DQN.load('trained/sb-deepq/agent') GAME_OPPONENTS.append( ('SB-DQN', 'stable_baselines3\'s implementation of the Deep-Q Network algorithm, trained on all my other agents but not against itself', sb_deepq) ) # bots for name, desc, bot in [ ('BOT: DUMBOT', 'Bot that does almost nothing at all', robotics.Dumbot(rand_seed())), ('BOT: RANDOMINATOR', 'Bot whose moves are randomized', robotics.Randominator(rand_seed())), ('BOT: AVOIDER', 'Bot that tries to keep as far away from you as possible', robotics.Randominator(rand_seed())), ('BOT: RAMMER', 'Bot that moves back and forth, constantly shoots and occasionally jumps', robotics.Rammer(rand_seed())), ('BOT: RAMMER MK.II', 'Same as Rammer, but always moves towards your position', robotics.RammerMkII(rand_seed())), ('BOT: MAD JUMPER', 'Bot that constantly jumps and moves in a semi-chaotic pattern', robotics.MadJumper(rand_seed())), ('BOT: DODGETRON', 'Bot that is an expert at jumping over projectiles', robotics.Dodgetron(rand_seed())), ('BOT: DODGETRON MK.II', 'Same as Dodgetron, but tries to stay close to you', robotics.DodgetronMkII(rand_seed())), ('BOT: STINGER', 'A very tough bot that always tries to reach your position and shoots only when at the same position as you', robotics.Stinger(rand_seed())), ('BOT: RANDOM STRATEGY', 'Bot with a randomized strategy, can be constantly jumping and firing or standing still and carefully overthinking each shot', robotics.UniversalBot(strategy_seed=rand_seed(), action_seed=rand_seed())) ]: GAME_OPPONENTS.append((name, desc, bot)) def reset_bot_seeds(): global GAME_OPPONENTS rand = random.Random() for i in range(len(GAME_OPPONENTS)): opp = GAME_OPPONENTS[i][2] if isinstance(opp, robotics.Bot): if isinstance(opp, robotics.UniversalBot): GAME_OPPONENTS[i] = (*GAME_OPPONENTS[i][:2], robotics.UniversalBot(strategy_seed=rand.randint(0, 0xffffffff), action_seed=rand.randint(0, 0xffffffff))) else: GAME_OPPONENTS[i] = (*GAME_OPPONENTS[i][:2], opp.__class__(rand.randint(0, 0xffffffff))) LOGO_CLRS = [(0xff, 0xc0, 0x00), (0xc0, 0xc0, 0xc0), (0x80, 0xc0, 0xff)] def game_opponent_select_screen() -> Any: global GAME_OPPONENTS total_rows = (WINDOW_HEIGHT - 4) // 12 header_rows = 2 # header & footer description_rows = 5 list_rows = total_rows - header_rows - description_rows cols = (WINDOW_WIDTH - 8) // 8 current_camera_position = 0 current_cursor_position = 0 prev_cursor_position = -1 sel_clr = random.choice(LOGO_CLRS) while True: clock.tick(FPS) if current_cursor_position != prev_cursor_position: current_camera_position = min(current_camera_position, current_cursor_position) current_camera_position = max(current_camera_position, current_cursor_position - list_rows + 1) prev_cursor_position = current_cursor_position black_clr = (0x00, 0x00, 0x00) screen.fill(black_clr) # header & footer white_clr = (0xff, 0xff, 0xff) screen.blit(font.render('SELECT NEURAL NETWORK OR BOT:', False, white_clr), (4 * SCALE, 4 * SCALE)) screen.blit(font.render('PRESS ENTER TO CONFIRM', False, white_clr), (4 * SCALE, (4 + 12 * (total_rows - 1)) * SCALE)) # list if current_camera_position > 0: screen.blit(font.render('↑', False, sel_clr), (4 * SCALE, (4 + 12) * SCALE)) if current_camera_position < len(GAME_OPPONENTS) - list_rows: screen.blit(font.render('↓', False, sel_clr), (4 * SCALE, (4 + 12 * list_rows) * SCALE)) for i in range(list_rows): if current_cursor_position == current_camera_position + i: pygame.draw.rect(screen, sel_clr, ((4 + 8 - 1) * SCALE, (4 + 12 + 12 * i - 1) * SCALE, (WINDOW_WIDTH - 4 - 8 - 4) * SCALE, 10 * SCALE)) screen.blit(font.render(GAME_OPPONENTS[current_camera_position + i][0], False, black_clr if (current_cursor_position == current_camera_position + i) else white_clr), ((4 + 8) * SCALE, (4 + 12 + 12 * i) * SCALE)) # description box pygame.draw.rect(screen, sel_clr, (1 * SCALE, (4 + 12 + 12 * (list_rows) - 1) * SCALE, (cols * 8 + 6) * SCALE, (description_rows * 12 + 1) * SCALE)) pygame.draw.rect(screen, black_clr, (2 * SCALE, (4 + 12 + 12 * (list_rows)) * SCALE, (cols * 8 + 4) * SCALE, (description_rows * 12 - 1) * SCALE)) _, desc,_ = GAME_OPPONENTS[current_cursor_position] desc = desc.upper() for row_i in range(math.ceil(len(desc) / cols)): screen.blit(font.render(desc[row_i * cols : (row_i + 1) * cols], False, sel_clr), (4 * SCALE, (4 + 12 + 12 * (list_rows) + 12 * row_i) * SCALE)) pygame.display.flip() for event in pygame.event.get(): if event.type == pygame.QUIT: graphics.deinit_graphics() exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_UP: current_cursor_position = (current_cursor_position - 1) % len(GAME_OPPONENTS) elif event.key == pygame.K_DOWN: current_cursor_position = (current_cursor_position + 1) % len(GAME_OPPONENTS) elif event.key in [pygame.K_x, pygame.K_z, pygame.K_RETURN]: return GAME_OPPONENTS[current_cursor_position][2] LOGO = ''' .......XX....XX..XXXXXXXX..XX....XX..XXXXXX......XXXX......... .......XXX...XX..XXXXXXXX..XX....XX..XXXXXXX....XXXXXX........ .......XXX...XX..XX........XX....XX..XX...XXX...XX..XX........ .......XXXX..XX..XX........XX....XX..XX....XX..XX....XX....... .......XXXX..XX..XX........XX....XX..XX....XX..XX....XX....... .......XX.XX.XX..XXXXXX....XX....XX..XX...XXX..XX....XX....... .......XX.XX.XX..XXXXXX....XX....XX..XXXXXXX...XX....XX....... .......XX..XXXX..XX........XX....XX..XXXXXX....XX....XX....... .......XX..XXXX..XX........XX....XX..XX..XXX...XX....XX....... .......XX...XXX..XX........XXX..XXX..XX...XXX...XX..XX........ .......XX...XXX..XXXXXXXX...XXXXXX...XX....XX...XXXXXX........ .......XX....XX..XXXXXXXX....XXXX....XX....XX....XXXX......... .............................................................. .............................................................. XXXXXXXX..XX....XXXXX...XX....XX..XXXXXXXX..XXXXXXXX..XXXXXX.. XXXXXXXX..XX...XXXXXXX..XX....XX..XXXXXXXX..XXXXXXXX..XXXXXXX. XX........XX...XX...XX..XX....XX.....XX.....XX........XX...XXX XX........XX..XX........XX....XX.....XX.....XX........XX....XX XX........XX..XX........XX....XX.....XX.....XX........XX....XX XXXXXX....XX..XX..XXXX..XXXXXXXX.....XX.....XXXXXX....XX...XXX XXXXXX....XX..XX..XXXX..XXXXXXXX.....XX.....XXXXXX....XXXXXXX. XX........XX..XX....XX..XX....XX.....XX.....XX........XXXXXX.. XX........XX..XX....XX..XX....XX.....XX.....XX........XX..XXX. XX........XX...XX...XX..XX....XX.....XX.....XX........XX...XXX XX........XX...XXXXXXX..XX....XX.....XX.....XXXXXXXX..XX....XX XX........XX....XXXXX...XX....XX.....XX.....XXXXXXXX..XX....XX'''[1:].split('\n') def title_screen() -> int: modes = ['CAMPAIGN', 'FRIENDLY MATCH', 'PLAYER VS. PLAYER', 'NET VS. NET'] mode_maxlen = max([len(mode) for mode in modes]) current_mode = 0 prev_mode = -1 logo_cell_size = 4 logo_cell_halfsize = logo_cell_size // 2 logo_width_up = WINDOW_WIDTH * 7 // 8 logo_width_down = WINDOW_WIDTH * 2 // 3 logo_start = 8 logo_height = WINDOW_HEIGHT // 2 - 16 logo_rows = len(LOGO) logo_cols = len(LOGO[0]) press_start = True press_start_duration = FPS * 3 // 4 press_start_timer = press_start_duration sel_clr = random.choice(LOGO_CLRS) while True: clock.tick(FPS) press_start_timer -= 1 if current_mode != prev_mode or press_start_timer == 0: if press_start_timer == 0: press_start_timer = press_start_duration press_start = not press_start prev_mode = current_mode screen.fill((0x00, 0x00, 0x00)) # logo for logo_row in range(logo_rows): logo_width = logo_width_up + (logo_width_down - logo_width_up) * (logo_row / (logo_rows - 1)) size_between_squares = logo_width / (logo_cols - 1) for logo_col in range(logo_cols): if LOGO[logo_row][logo_col] == 'X': point = (round((WINDOW_WIDTH - logo_width) / 2 + size_between_squares * logo_col), logo_start + logo_height * logo_row / (logo_rows - 1)) pygame.draw.rect(screen, sel_clr, ((point[0] - logo_cell_halfsize) * SCALE, (point[1] - logo_cell_halfsize) * SCALE, logo_cell_size * SCALE, logo_cell_size * SCALE)) # modes for i in range(len(modes)): screen.blit(font.render(modes[i], False, (0xff, 0xff, 0xff)), ((WINDOW_WIDTH // 2 - 4 * mode_maxlen) * SCALE, (WINDOW_HEIGHT // 2 + 4 + 12 * i) * SCALE)) if i == current_mode: screen.blit(font.render('>', False, sel_clr), ((WINDOW_WIDTH // 2 - 4 * mode_maxlen - 12) * SCALE, (WINDOW_HEIGHT // 2 + 4 + 12 * i) * SCALE)) # press start if press_start: screen.blit(font.render('PRESS ENTER', False, (0xff, 0xff, 0xff)), ((WINDOW_WIDTH // 2 - 4 * 11) * SCALE, (WINDOW_HEIGHT - 4 - 12 * 2) * SCALE)) # credits credits = '© 2025 FYODOR MAMAYEV' screen.blit(font.render(credits, False, sel_clr), ((WINDOW_WIDTH // 2 - 4 * len(credits)) * SCALE, (WINDOW_HEIGHT - 4 - 12) * SCALE)) pygame.display.flip() for event in pygame.event.get(): if event.type == pygame.QUIT: graphics.deinit_graphics() exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_UP: current_mode = (current_mode - 1) % len(modes) elif event.key == pygame.K_DOWN: current_mode = (current_mode + 1) % len(modes) elif event.key in [pygame.K_x, pygame.K_z, pygame.K_RETURN]: return current_mode if __name__ == '__main__': graphics.init_graphics() # agent = neural_ppo_singlehead.PPOAgentWrapper(hidden_dim=256, # logging=False) # agent.load_model('GOOD-trained-24261-ppo-master/agent.pth') screen.fill((0x00, 0x00, 0x00)) loading_msg = 'LOADING...' screen.blit(font.render(loading_msg, False, (0xff, 0xff, 0xff)), ((WINDOW_WIDTH // 2 - 4 * len(loading_msg)) * SCALE, (WINDOW_HEIGHT // 2 - 4) * SCALE)) pygame.display.flip() init_game_opponents() while True: reset_bot_seeds() if ENABLE_MUSIC: pygame.mixer.music.load('assets/music/14-stage-select.wav') pygame.mixer.music.play(loops=-1) mode = title_screen() black_screen(BLACK_SCREEN_FRAMES) if mode == 0: # campaign opponent = game_opponent_select_screen() black_screen(BLACK_SCREEN_FRAMES) player_character = character_select_campaign() if ENABLE_MUSIC: pygame.mixer.music.stop() black_screen(BLACK_SCREEN_FRAMES) campaign(player_character, opponent) black_screen(BLACK_SCREEN_FRAMES) else: # friendly match/player vs. player/net vs.net player_network = None opponent_network = None if mode == 1: opponent_network = game_opponent_select_screen() black_screen(BLACK_SCREEN_FRAMES) elif mode == 3: player_network = game_opponent_select_screen() black_screen(BLACK_SCREEN_FRAMES) opponent_network = game_opponent_select_screen() black_screen(BLACK_SCREEN_FRAMES) player_character, opponent_character = character_select_2player() if ENABLE_MUSIC: pygame.mixer.music.stop() black_screen(BLACK_SCREEN_FRAMES) intro_screen(player_character, opponent_character) black_screen(BLACK_SCREEN_FRAMES) result = hold_match(player_character, opponent_character, player_network, opponent_network) black_screen(BLACK_SCREEN_FRAMES) if mode == 1: win_lose_screen(player_character, opponent_character, result == 1) black_screen(BLACK_SCREEN_FRAMES) else: win_lose_screen(player_character if result == 1 else opponent_character, opponent_character if result == 1 else player_character, True, False) black_screen(BLACK_SCREEN_FRAMES) graphics.deinit_graphics()