fix: genome GA converges via BFS fitness + two-rate prefix-aware mutation
- Replace Manhattan distance with BFS distances (actual maze path length) - Switch from NN to direct sequence genome (what YouTube Shorts actually use) - Mutation-only GA (crossover breaks positional maze paths) - Two-rate mutation: low rate before best-step (preserve prefix), high after (explore tail) - Auto-seed selection finds maze with short BFS path - Default maze 15x15, cell_size=36, max_steps=150 - Typically converges in 5-15 generations
This commit is contained in:
34
main.py
34
main.py
@@ -2,8 +2,8 @@ import argparse
|
||||
import sys
|
||||
import os
|
||||
from config import MazeConfig
|
||||
from maze import generate
|
||||
from agent import Agent, NeuralNet
|
||||
from maze import generate, bfs_distances, find_short_seed
|
||||
from agent import Agent, Genome
|
||||
from genetic import next_generation
|
||||
|
||||
|
||||
@@ -57,10 +57,26 @@ def main():
|
||||
goal_row = cfg.maze_rows - 1
|
||||
goal_col = cfg.maze_cols - 1
|
||||
|
||||
grid = generate(cfg.maze_rows, cfg.maze_cols, seed=args.seed)
|
||||
if args.seed is None:
|
||||
print("Auto-selecting a maze with a short solution path...")
|
||||
chosen_seed, grid, bfs = find_short_seed(cfg.maze_rows, cfg.maze_cols)
|
||||
print(f"Using seed={chosen_seed}, BFS path={int(bfs[0,0])} steps")
|
||||
else:
|
||||
grid = generate(cfg.maze_rows, cfg.maze_cols, seed=args.seed)
|
||||
bfs = bfs_distances(grid, goal_row, goal_col)
|
||||
path_len = int(bfs[0, 0])
|
||||
print(f"Maze BFS path: {path_len} steps")
|
||||
if path_len > cfg.maze_rows * cfg.maze_cols // 3:
|
||||
print(f" [hint: long path ({path_len} steps) -- try omitting --seed for auto-selection]")
|
||||
|
||||
def make_agents(genomes):
|
||||
agents = [Agent(g, 0, 0) for g in genomes]
|
||||
for a in agents:
|
||||
a._bfs = bfs
|
||||
return agents
|
||||
|
||||
# Initialize population
|
||||
nets = [NeuralNet() for _ in range(cfg.population)]
|
||||
nets = [Genome(cfg=cfg) for _ in range(cfg.population)]
|
||||
|
||||
print(f"Starting training: {args.generations} generations, population={cfg.population}")
|
||||
|
||||
@@ -69,7 +85,7 @@ def main():
|
||||
print("Fast mode: rendering disabled during training.")
|
||||
|
||||
for gen in range(args.generations):
|
||||
agents = [Agent(net, 0, 0) for net in nets]
|
||||
agents = make_agents(nets)
|
||||
run_generation_headless(agents, grid, goal_row, goal_col, cfg)
|
||||
|
||||
for agent in agents:
|
||||
@@ -94,7 +110,8 @@ def main():
|
||||
clock = pygame.time.Clock()
|
||||
viz = Visualizer(screen, grid, cfg)
|
||||
|
||||
replay_agents = [Agent(net, 0, 0) for net in nets]
|
||||
replay_agents = make_agents(nets)
|
||||
|
||||
ok = run_generation_visual(replay_agents, grid, goal_row, goal_col, cfg, viz, clock, args.speed, args.generations, args.steps_per_frame)
|
||||
if ok:
|
||||
print("Done. Press ESC or close window to exit.")
|
||||
@@ -120,7 +137,7 @@ def main():
|
||||
viz = Visualizer(screen, grid, cfg)
|
||||
|
||||
for gen in range(args.generations):
|
||||
agents = [Agent(net, 0, 0) for net in nets]
|
||||
agents = make_agents(nets)
|
||||
|
||||
ok = run_generation_visual(agents, grid, goal_row, goal_col, cfg, viz, clock, args.speed, gen, args.steps_per_frame)
|
||||
if not ok:
|
||||
@@ -140,7 +157,8 @@ def main():
|
||||
|
||||
# Final visual replay with best generation
|
||||
print("\nTraining complete. Showing final generation replay...")
|
||||
replay_agents = [Agent(net, 0, 0) for net in nets]
|
||||
replay_agents = make_agents(nets)
|
||||
|
||||
viz.reset_trails()
|
||||
run_generation_visual(replay_agents, grid, goal_row, goal_col, cfg, viz, clock, args.speed, args.generations, args.steps_per_frame)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user