┌─────────────────────────────────────────────────────────────┐
│ MazeController │
│ - Generates maze │
│ - Manages renderers │
│ - Triggers agent spawning │
└────────────────┬────────────────────────────────────────────┘
│
│ spawns agents via
↓
┌─────────────────────────────────────────────────────────────┐
│ AIAgentManager │
│ - Spawns agents (initial + runtime) │
│ - Tracks active agents │
│ - Manages configuration │
│ - Provides statistics │
└────────────────┬────────────────────────────────────────────┘
│
│ creates
↓
┌─────────────────────────────────────────────────────────────┐
│ AIAgent (10+ instances) │
│ - Navigates maze │
│ - Makes pathfinding decisions │
│ - Moves toward goal │
│ - Reports visited rooms │
└────────────────┬────────────────────────────────────────────┘
│
│ uses
↓
┌─────────────────────────────────────────────────────────────┐
│ AIRoomMemoryManager (Singleton) │
│ - Manages memories per character type │
└────────────────┬────────────────────────────────────────────┘
│
│ provides memory
↓
┌─────────────────────────────────────────────────────────────┐
│ AIRoomMemory (per character type) │
│ - Tracks visited rooms │
│ - Filters unvisited rooms │
│ - Shared by all agents of same type │
└─────────────────────────────────────────────────────────────┘
┌──────────────┐
│ Agent Update │
└──────┬───────┘
│
↓
┌─────────────────────────────────┐
│ Determine Current Room │
│ (check position with GetRoomAtTile)
└──────┬──────────────────────────┘
│
↓
┌─────────────────────────────────┐
│ Check if at Goal Exit Point │
│ (maze.ExitPoints.Contains()) │
└──────┬──────────────────────────┘
│
YES │ NO
┌────┴──────────┐
│ │
↓ ↓
┌──────────────────┐ ┌──────────────────────────────┐
│ Goal Reached │ │ In Goal Room? │
│ (Stop movement) │ │ (GetRoomsByType End) │
└──────────────────┘ └──────┬───────────────────────┘
│
YES │ NO
┌──────┴───────┐
│ │
↓ ↓
┌─────────────┐ ┌───────────────────┐
│ Path to │ │ Choose Next Room │
│ Exit in │ │ (ChooseNextRoom) │
│ Current Rm │ └────────┬──────────┘
└─────────────┘ │
┌──────┴──────────┐
│ │
↓ ↓
┌──────────────────┐
│ Prefer Unvisited│
│ (via RoomMemory) │
└──────────────────┘
│
↓
┌──────────────────┐
│ Path to Room Exit│
│ (A* within room) │
└──────────────────┘
│
↓
┌──────────────────┐
│ FollowPath() │
│ Move at speed │
└──────────────────┘
Agent starts in START room
│
├─ Visit START room
│ └─ Mark in RoomMemory as visited
│
├─ Look for exits from START room
│ └─ Find connected rooms via exits
│
├─ Check RoomMemory: which are unvisited?
│ └─ Unvisited rooms = HIGH PRIORITY
│
├─ Path to exit of START room leading to unvisited room
│
├─ Enter adjacent room
│ └─ Mark in RoomMemory as visited
│
├─ Repeat for new room...
│
└─ Eventually reach END room
└─ Path to exit point
└─ GOAL REACHED!
Character type memory shared between all agents:
- Agent A visited rooms: [1, 2, 3, 5]
- Agent B can see: "Rooms 1,2,3,5 visited" → knows to check rooms 4,6,7
┌──────────────────────┐
│ AIRoomMemoryManager │
│ (Singleton) │
└──────┬───────────────┘
│ manages many
↓
┌──────────────────────────────────┐
│ AIRoomMemory │
│ (one per character type) │
│ │
│ Properties: │
│ - characterType: string │
│ - visitedRoomIds: HashSet<int> │
│ - roomLastVisitTime: Dict<> │
└──────────────────────────────────┘
┌──────────────────────┐
│ AIAgentManager │
│ (one per game) │
└──────┬───────────────┘
│ spawns many
↓
┌──────────────────────────────────┐
│ AIAgent (MonoBehaviour) │
│ (one per agent in scene) │
│ │
│ Properties: │
│ - agentCharacterType: string │
│ - agentId: int │
│ - currentRoom: Vector2Int │
│ - roomMemory: AIRoomMemory │
│ │
│ Methods: │
│ - Update() - main loop │
│ - UpdateCurrentRoom() │
│ - UpdatePathToGoal() │
│ - FollowPath() │
└──────────────────────────────────┘
│ references
↓
┌──────────────────────────────────┐
│ MazeData │
│ (one per maze) │
│ │
│ Provides: │
│ - GetRoomAtTile(x, y) │
│ - IsWalkable(x, y) │
│ - GetAdjacentWalkable(x, y) │
│ - ExitPoints, StartPoints │
└──────────────────────────────────┘
Agent 1 explores maze:
Room 0 (start) → Room 2 → Room 5
│
└─ Mark visited: {0, 2, 5}
Store in: AIRoomMemory["Default"]
Agent 2 spawns later:
Room 0 (start)
│
├─ Check memory: visited = {0, 2, 5}
├─ Available neighbors: {0, 1, 3}
└─ Unvisited: {1, 3} → Choose 1 or 3
→ Room 1 → Room 3
│
└─ Mark visited: {0, 2, 5, 1, 3}
Update: AIRoomMemory["Default"]
Agent 3 spawns even later:
All agents see the same visited rooms list
Coordinates exploration better
Reaches goal faster with collective knowledge
Agents (n=10, default):
- Each agent pathfinds: every 0.5 seconds
- A* search scope: within single room bounds
- Memory per agent: ~1KB (pathfinding data)
- Shared memory per type: ~100 bytes (room ID set)
Total overhead:
- CPU: ~1-2ms per frame (10 agents)
- Memory: ~10KB total
Scaling:
100 agents: ~10ms, 100KB
1000 agents: ~100ms, 1MB (not recommended)
Optimizations:
- Shared room memory (not duplicated per agent)
- A* limited to single room bounds (not full maze)
- Configurable update interval
- Path caching until room changes
This architecture ensures: