Skip to content

Commit

Permalink
Merge pull request #39 from CogitoNTNU/flask-implementation
Browse files Browse the repository at this point in the history
Flask implementation
  • Loading branch information
ChristianFredrikJohnsen authored Oct 31, 2024
2 parents 2ad0274 + 9ca6c90 commit 883bb80
Show file tree
Hide file tree
Showing 12 changed files with 1,136 additions and 29 deletions.
466 changes: 466 additions & 0 deletions app.py

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Example function for sending data to the Flask backend
async function runTrain() {
const response = await fetch('http://localhost:5000/train', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ n_generations: 100, neat_name: 'experiment1' })
});
const result = await response.json();
console.log(result.output); // Process the output as needed
}
10 changes: 4 additions & 6 deletions gui_with_pygame.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ def get_genomes(self):
class Settings():
def __init__(self):
self.fps = 60
self.default_x_size = 2560
self.default_y_size = 1600
self.default_x_size = 1080
self.default_y_size = 720
self.fullscreen = False
self.fs_start_time = 0
self.fs_is_pressed = False
Expand Down Expand Up @@ -125,7 +125,7 @@ def __init__(self, x, y, width, height, items, font, text_color, bg_color, selec
self.scroll_offset = 0 # This tracks where we are in the list
self.padding = padding # Distance between items
# Create a base list of items without positioning them

self.list_items = [
SelectableListItem(
x, y, width, (height - (visible_count - 1) * padding) // visible_count, # Adjust height to account for padding
Expand Down Expand Up @@ -400,7 +400,7 @@ def __init__(self):
Button(460, 170, 200, 50, "Start Training", st.normal_font, st.text_color, st.button_color, st.hover_color, st.pressed_color, self.start_training_process),
Button(460, 300, 200, 50, "Back to Menu", st.normal_font, st.text_color, st.button_color, st.hover_color, st.pressed_color, self.main_menu_scene)]
try:
self.fitness_graph = ImageSprite("data/latest/fitness/fitness_plot.png", (700, 100))
self.fitness_graph = ImageSprite("data/latest/latest/fitness/fitness_plot.png", (700, 100))
except:
print("ERROR: Could not find image path")
self.fitness_graph = ImageSprite("data/genome_frames/genome_0.png", (700, 100))
Expand Down Expand Up @@ -445,7 +445,6 @@ def __init__(self):
# Use the loaded genome objects (printing as an example)
for genome in loaded_genomes:
self.genomes.append(genome)
print(genome)

## Genome viewer
self.genome_viewer = GenomeViewer(self.genomes, st.normal_font, st.text_color, st.input_field_bg, st.input_field_active_bg)
Expand Down Expand Up @@ -658,7 +657,6 @@ def tick(self):
self.update_screen()
self.clock.tick(st.fps)


st = Settings()

def main():
Expand Down
Binary file added main.py.lprof
Binary file not shown.
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ tqdm==4.66.5
triton==3.0.0
typing_extensions==4.12.2
zipp==3.20.2
flask-socketio
eventlet
watchdog
23 changes: 13 additions & 10 deletions src/environments/debug_env.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import numpy as np
import warnings
import time
import gym_super_mario_bros
from gym_super_mario_bros.actions import SIMPLE_MOVEMENT
from typing import Tuple
from src.genetics.genome import Genome
from src.genetics.traverse import Traverse
from src.environments.fitness_function import Fitness
from src.environments.mario_env import MarioJoypadSpace
from src.visualization.visualize_genome import visualize_genome
from src.utils.utils import save_state_as_png
from src.utils.utils import insert_input
import gym_super_mario_bros
from gym_super_mario_bros.actions import SIMPLE_MOVEMENT
from typing import Tuple
import numpy as np
import warnings
import time


def env_debug_init() -> Tuple[MarioJoypadSpace, np.ndarray]:
"Initialize the super-mario environment in human_mode"
Expand All @@ -24,7 +25,8 @@ def env_debug_init() -> Tuple[MarioJoypadSpace, np.ndarray]:
state = env.reset() # Good practice to reset the env before using it.
return env, state

def run_game_debug(env: MarioJoypadSpace, initial_state: np.ndarray, genome: Genome, neat_name: str, visualize: bool = True) -> float:

def run_game_debug(env: MarioJoypadSpace, initial_state: np.ndarray, genome: Genome, neat_name: str, visualize: bool = True, frame_queue=None) -> float:

forward = Traverse(genome)
fitness = Fitness()
Expand All @@ -37,11 +39,11 @@ def run_game_debug(env: MarioJoypadSpace, initial_state: np.ndarray, genome: Gen
action = forward.traverse()
time.sleep(0.01)
sr = env.step(action) # State, Reward, Done, Info
env.render()

# timeout = 600 + sr.info["x_pos"]
if visualize and i % 10000 == 0:
if visualize and i % 1 == 0:
save_state_as_png(i, sr.state, neat_name)
visualize_genome(genome, neat_name, i)
visualize_genome(genome, neat_name, 0)

fitness.calculate_fitness(sr.info, action)

Expand All @@ -56,5 +58,6 @@ def run_game_debug(env: MarioJoypadSpace, initial_state: np.ndarray, genome: Gen
if sr.info["life"] == 1 or stagnation_counter > 150:
env.close()
return fitness.get_fitness()
env.render()
i += 1
insert_input(genome, sr.state)
4 changes: 3 additions & 1 deletion src/environments/mario_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import numpy as np
import cv2
from skimage.transform import resize
from src.utils.config import Config

class StepResult(NamedTuple):
"""A namedtuple-like class representing the result of an environment step.
Expand Down Expand Up @@ -43,6 +44,7 @@ class MarioJoypadSpace(JoypadSpace):

def __init__(self, env, actions):
super().__init__(env, actions)
self.config = Config()
# Potentially add extra variables here.

def reset(self) -> np.ndarray:
Expand All @@ -69,7 +71,7 @@ def interpret_state(self, state: np.ndarray) -> np.ndarray:
state = np.dot(state[..., :3], [0.2989, 0.5870, 0.1140]) # Convert to grayscale
state = state.astype(np.uint8) # Ensure valid grayscale value (can't be float)

state = resize(state, (10, 20), anti_aliasing=False, preserve_range=True).astype(np.uint8) # Reduce pixel count.
state = resize(state, (self.config.input_shape[0], self.config.input_shape[1]), anti_aliasing=True, preserve_range=True).astype(np.uint8) # Reduce pixel count.

state = np.array(state)
grayscale_pixel_values = state / MAX_COLOR
Expand Down
8 changes: 4 additions & 4 deletions src/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ class Config:
c2: float = 1.5
c3: float = 0.4
genomic_distance_threshold: float = 2.69
population_size: int = 56 * 10 # 56 cores on IDUN
generations: int = int(1e6) # A bunch of iterations
population_size: int = 1 # 56 cores on IDUN
generations: int = 2 # A bunch of iterations

connection_weight_mutation_chance: float = 0.8

Expand All @@ -24,8 +24,8 @@ class Config:
# Connections should be added way more often than nodes

num_output_nodes: int = 7
num_input_nodes: int = 200
input_shape: Tuple[int, int] = (10, 20)
num_input_nodes: int = 800
input_shape: Tuple[int, int] = (20, 40)

# Activation function
# Paper: 1/(1+exp(-0.49*x))
Expand Down
3 changes: 2 additions & 1 deletion src/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ def insert_input(genome:Genome, state: np.ndarray) -> None:
start_idx_input_node = config.num_output_nodes
num_input_nodes = config.num_input_nodes
num_columns = config.input_shape[-1]

for i, node in enumerate(genome.nodes[start_idx_input_node:start_idx_input_node+num_input_nodes]): # get all input nodes
node.value = state[i//num_columns][i % num_columns]
# print(f"node value: {node.value} node id: {node.id}")

def save_fitness(best: list, avg: list, min: list, name: str):
os.makedirs(f'data/{name}/fitness', exist_ok=True)
Expand Down
6 changes: 3 additions & 3 deletions src/visualization/visualize_genome.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import List, Dict, Tuple
from src.visualization.colors_visualization import get_weight_color, get_node_colz
from src.visualization.visualization_position_utils import get_position_dict
from src.visualization.viz_config import GRAPH_XMIN, GRAPH_XMAX, GRAPH_YMIN, GRAPH_YMAX
from src.visualization.viz_config import GRAPH_XMIN, GRAPH_XMAX, GRAPH_YMIN, GRAPH_YMAX, NODE_SIZE, FONT_SIZE

def get_node_in_layers(genome: Genome) -> List[List[int]]:
"""
Expand Down Expand Up @@ -74,8 +74,8 @@ def visualize_genome(genome: Genome, neat_name: str, frame_number: int):
layers = get_node_in_layers(genome)
pos_dict = get_position_dict(layers)

nx.draw(G, pos_dict, with_labels=True, edgelist=edges, edge_color=edge_colz, node_size=500,
font_size=8, font_color='y', font_weight='bold',
nx.draw(G, pos_dict, with_labels=True, edgelist=edges, edge_color=edge_colz, node_size=NODE_SIZE,
font_size=FONT_SIZE, font_color='y', font_weight='bold',
node_color=node_colz, ax=ax)

fig.set_facecolor('#d4e6f1') # Background color of network popup.
Expand Down
11 changes: 7 additions & 4 deletions src/visualization/viz_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
GRAPH_YMIN = 0
GRAPH_YMAX = 21

HORIZONTAL_GAP_INPUT = 0.5
VERTICAL_GAP = 1.5
HORIZONTAL_GAP_INPUT = 0.3
VERTICAL_GAP = 0.7

NUM_INPUT_ROWS = 10
NUM_INPUT_COLS = 20
NUM_INPUT_ROWS = 20
NUM_INPUT_COLS = 40

XSTART_INPUT = 1.5
XSTART_OUTPUT = 15
YSTART = 3.0

NODE_SIZE = 100
FONT_SIZE = 4
Loading

0 comments on commit 883bb80

Please sign in to comment.