Add pluggable parser system, perception loop, and project infrastructure
Phase 0 (housekeeping): - Add .gitignore to protect credentials and runtime files - Track all project source code, docs, and config Phase 1 (perception loop): - Implement pluggable parser architecture (BaseMudParser ABC + ParserFactory) - Add DeadSoulsParser with regex for HP/MP/SP prompt, room descriptions, exits (multiple formats), NPC/item detection, and combat events - Add OutputBuffer for accumulating text between prompts - Update GameState with SP support and update_from_prompt/room/combat methods - Rewire main loop to use parser + buffer for real-time game state updates - Add reconnection logic to TelnetClient (retry with backoff) - Remove old TextParser (replaced by pluggable system) - Add mud_type config field for selecting parser at runtime - Add comprehensive test suite (50 tests, all passing)
This commit is contained in:
1 parent
0f370ec089
commit
f85c27a742
42 files changed
+3490
-134
No files matched your search
+306
@@ -0,0 +1,306 @@
|
||||
<img src="https://r2cdn.perplexity.ai/pplx-full-logo-primary-dark%402x.png" style="height:64px;margin-right:32px"/>
|
||||
|
||||
# puoi produrre un PR.MD per chiedere ad un agente tipo ralph di produrre un automapper con queste caratteristiche ?
|
||||
|
||||
```markdown
|
||||
# PR: Implementazione Automapper MUD con Archivio Ibrido SQLite/Memvid
|
||||
|
||||
## Descrizione
|
||||
Implementare un modulo automapper per un client MUD che costruisca automaticamente una mappa del mondo di gioco durante l'esplorazione, con persistenza ibrida: SQLite per la memoria attiva real-time e Memvid per l'archivio storico compresso.
|
||||
|
||||
## Obiettivi
|
||||
|
||||
- [ ] Parsing delle descrizioni stanze dal protocollo Telnet MUD
|
||||
- [ ] Costruzione incrementale del grafo di navigazione (stanze = nodi, direzioni = archi)
|
||||
- [ ] Identificazione univoca delle stanze (room fingerprinting)
|
||||
- [ ] Auto-mapping durante l'esplorazione senza intervento umano
|
||||
- [ ] Pathfinding A* tra stanze note
|
||||
- [ ] Storage layer ibrido: SQLite (attivo) + Memvid (storico)
|
||||
- [ ] Sistema di ricerca semantica sulle descrizioni stanze
|
||||
|
||||
## Architettura Proposta
|
||||
|
||||
### 1. Telnet Parser & Room Extractor
|
||||
```python
|
||||
class RoomExtractor:
|
||||
"""Estrae informazioni stanza dal testo MUD grezzo"""
|
||||
|
||||
def parse(self, raw_output: str) -> RoomData:
|
||||
# Estrae: nome stanza, descrizione, exits disponibili,
|
||||
# oggetti presenti, mob/NPC visibili
|
||||
pass
|
||||
```
|
||||
|
||||
**Input**: Testo raw dal server MUD (con ANSI color codes)
|
||||
**Output**: `RoomData` strutturato con:
|
||||
|
||||
- `room_id`: hash univoco (descrizione + exits + nome)
|
||||
- `name`: nome breve stanza
|
||||
- `description`: descrizione lunga
|
||||
- `exits`: dict {direzione: room_id_target} o None se non esplorato
|
||||
- `entities`: lista mob/NPC/oggetti presenti
|
||||
- `metadata`: timestamp, coordinate approssimative (se disponibili)
|
||||
|
||||
|
||||
### 2. Graph Manager (NetworkX)
|
||||
|
||||
```python
|
||||
class MUDGraph:
|
||||
"""Grafo delle stanze con metodi di navigazione"""
|
||||
|
||||
def add_room(self, room: RoomData) -> bool:
|
||||
# Aggiunge nodo, aggiorna archi bidirezionali
|
||||
pass
|
||||
|
||||
def find_path(self, from_id: str, to_id: str) -> List[str]:
|
||||
# A* pathfinding, ritorna lista di direzioni [n, n, e, u, ...]
|
||||
pass
|
||||
|
||||
def get_unexplored_exits(self, room_id: str) -> List[str]:
|
||||
# Ritorna direzioni da esplorare (exit con target=None)
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
### 3. Storage Layer Ibrido
|
||||
|
||||
#### Layer A - SQLite (Memoria Attiva)
|
||||
|
||||
Per operazioni real-time durante l'esplorazione:
|
||||
|
||||
```sql
|
||||
-- rooms: cache attiva delle stanze esplorate
|
||||
CREATE TABLE rooms (
|
||||
room_id TEXT PRIMARY KEY,
|
||||
name TEXT,
|
||||
description TEXT,
|
||||
embedding BLOB, -- vettore per ricerca semantica
|
||||
discovered_at TIMESTAMP,
|
||||
visit_count INTEGER DEFAULT 1,
|
||||
last_visit TIMESTAMP
|
||||
);
|
||||
|
||||
-- exits: grafo di navigazione
|
||||
CREATE TABLE exits (
|
||||
from_room TEXT,
|
||||
direction TEXT,
|
||||
to_room TEXT, -- NULL se non esplorato
|
||||
FOREIGN KEY (from_room) REFERENCES rooms(room_id)
|
||||
);
|
||||
|
||||
-- exploration_queue: stanze con exit non esplorati
|
||||
CREATE TABLE exploration_queue (
|
||||
room_id TEXT,
|
||||
direction TEXT,
|
||||
priority INTEGER,
|
||||
added_at TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
#### Layer B - Memvid (Archivio Storico)
|
||||
|
||||
Per storage compresso e ricerca semantica a lungo termine:
|
||||
|
||||
```python
|
||||
class MemvidArchive:
|
||||
"""Archivio storico in MP4 con QR code frames"""
|
||||
|
||||
def snapshot(self, db_path: str) -> str:
|
||||
# 1. Estrae tutte le stanze da SQLite
|
||||
# 2. Chunking descrizioni + metadati
|
||||
# 3. Embedding con sentence-transformers
|
||||
# 4. Encoding in QR code frames
|
||||
# 5. Genera MP4 H.264 + indice FAISS
|
||||
pass
|
||||
|
||||
def semantic_search(self, query: str, top_k: int = 5) -> List[RoomData]:
|
||||
# Ricerca vettoriale sul MP4 storico
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
### 4. Auto-Explorer Controller
|
||||
|
||||
```python
|
||||
class AutoExplorer:
|
||||
"""Orchestra l'esplorazione automatica"""
|
||||
|
||||
def decide_next_move(self, current_room: RoomData) -> str:
|
||||
# Logica:
|
||||
# 1. Se ci sono exit non esplorati, scegli quella più vicina
|
||||
# 2. Altrimenti, BFS verso la stanza con exit non esplorati
|
||||
# 3. Evita loop (no back-and-forth)
|
||||
pass
|
||||
|
||||
def update_graph(self, move: str, result: str) -> None:
|
||||
# Aggiorna grafo dopo ogni movimento
|
||||
# Se result è "You can't go that way", marca exit come blocked
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
### 5. Sync Manager
|
||||
|
||||
```python
|
||||
class SyncManager:
|
||||
"""Gestisce la sincronizzazione tra SQLite e Memvid"""
|
||||
|
||||
def __init__(self, sync_interval: int = 100):
|
||||
# Ogni N stanze nuove, triggera snapshot Memvid
|
||||
pass
|
||||
|
||||
def should_sync(self) -> bool:
|
||||
# Controlla se ci sono abbastanza nuove stanze
|
||||
pass
|
||||
|
||||
def perform_sync(self):
|
||||
# 1. Crea backup SQLite
|
||||
# 2. Genera snapshot Memvid
|
||||
# 3. Verifica integrità (sample random decode)
|
||||
# 4. Opzionale: pulisce SQLite delle stanze meno recenti
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
## Criteri di Accettazione
|
||||
|
||||
### Test 1: Parsing Base
|
||||
|
||||
```python
|
||||
raw_mud_output = """
|
||||
[Exits: n e w]
|
||||
The Village Square
|
||||
You are standing in the village square. A fountain bubbles here.
|
||||
A guard is standing here.
|
||||
A signpost points north to the Forest, east to the Shop.
|
||||
"""
|
||||
room = extractor.parse(raw_mud_output)
|
||||
assert room.name == "The Village Square"
|
||||
assert room.exits == {"n": None, "e": None, "w": None}
|
||||
assert "guard" in room.entities
|
||||
```
|
||||
|
||||
|
||||
### Test 2: Costruzione Grafo
|
||||
|
||||
```python
|
||||
# Simula esplorazione: Square → North → Forest
|
||||
graph = MUDGraph()
|
||||
# ... aggiungi stanze ...
|
||||
path = graph.find_path("square_123", "forest_456")
|
||||
assert path == ["n"] # diretta, una sola mossa
|
||||
```
|
||||
|
||||
|
||||
### Test 3: Persistenza SQLite
|
||||
|
||||
```python
|
||||
# Dopo 10 stanze esplorate
|
||||
assert db.count_rooms() == 10
|
||||
assert db.get_room("square_123").visit_count >= 1
|
||||
```
|
||||
|
||||
|
||||
### Test 4: Memvid Snapshot
|
||||
|
||||
```python
|
||||
# Dopo sync, verifica che MP4 sia leggibile
|
||||
archive = MemvidArchive("world_map.mp4")
|
||||
results = archive.semantic_search("forest with dark trees")
|
||||
assert len(results) > 0
|
||||
assert "forest" in results.description.lower()
|
||||
```
|
||||
|
||||
|
||||
### Test 5: Auto-Explorer (Integration)
|
||||
|
||||
```python
|
||||
# Simula 20 movimenti in un mondo di test
|
||||
explorer = AutoExplorer(graph)
|
||||
for _ in range(20):
|
||||
move = explorer.decide_next_move(current_room)
|
||||
# Simula risposta server
|
||||
new_room = simulate_mud_response(move)
|
||||
explorer.update_graph(move, new_room)
|
||||
|
||||
# Dovrebbe aver esplorato almeno 5 stanze uniche
|
||||
assert len(graph.rooms) >= 5
|
||||
```
|
||||
|
||||
|
||||
## Considerazioni Tecniche
|
||||
|
||||
### Room ID Generation
|
||||
|
||||
Usare hash consistente (SHA256 truncato) di:
|
||||
|
||||
- Nome stanza normalizzato (lowercase, no color codes)
|
||||
- Descrizione normalizzata (prime 100 char)
|
||||
- Lista di exits disponibili (ordinata)
|
||||
|
||||
|
||||
### Gestione Stanze Dinamiche
|
||||
|
||||
Alcuni MUD hanno stanze che cambiano (portali random, maze). Implementare:
|
||||
|
||||
- `is_dynamic`: flag se la stanza cambia tra visite
|
||||
- `confidence`: score 0-1 sulla stabilità dell'ID
|
||||
|
||||
|
||||
### Performance Targets
|
||||
|
||||
- Parsing stanza: < 50ms
|
||||
- Query pathfinding: < 100ms per 1000 stanze
|
||||
- Sync SQLite→Memvid: background, non bloccare < 500ms UI
|
||||
|
||||
|
||||
### Dipendenze
|
||||
|
||||
```
|
||||
# requirements.txt
|
||||
networkx
|
||||
sqlite3
|
||||
numpy
|
||||
sentence-transformers
|
||||
pillow # QR generation
|
||||
opencv-python # QR decoding
|
||||
faiss-cpu # o faiss-gpu
|
||||
```
|
||||
|
||||
|
||||
## Checklist Implementazione
|
||||
|
||||
- [ ] Implementare `RoomExtractor` con regex MUD
|
||||
- [ ] Implementare `MUDGraph` con NetworkX
|
||||
- [ ] Setup schema SQLite
|
||||
- [ ] Implementare `AutoExplorer` con BFS per exit non esplorati
|
||||
- [ ] Implementare `MemvidArchive` encoding/decoding
|
||||
- [ ] Implementare `SyncManager` con trigger automatico
|
||||
- [ ] Scrivere test unitari per ogni modulo
|
||||
- [ ] Scrivere test di integrazione con server MUD mock
|
||||
- [ ] Documentazione API (docstring)
|
||||
- [ ] Esempio di usage in `examples/basic_explorer.py`
|
||||
|
||||
|
||||
## Note per l'Agente
|
||||
|
||||
1. **Inizia dal SQLite**: implementa prima il grafo in-memory + SQLite, il Memvid è un "nice to have" iniziale
|
||||
2. **Mock del server**: crea un `MockMUDServer` che risponde con testo predefinito per i test
|
||||
3. **Logging**: usa `logging` per tracciare ogni decisione dell'explorer (utile per debug)
|
||||
4. **Configurazione**: usa `pydantic.BaseSettings` per parametri (sync_interval, embedding model, ecc.)
|
||||
|
||||
## Definizione di Completato
|
||||
|
||||
L'automapper è considerato completato quando:
|
||||
|
||||
1. ✅ Può esplorare autonomamente un mondo MUD di test con 50+ stanze
|
||||
2. ✅ Costruisce un grafo navigabile con pathfinding funzionante
|
||||
3. ✅ Persiste i dati in SQLite senza perdita di informazioni
|
||||
4. ✅ Genera correttamente un archivio Memvid ricercabile
|
||||
5. ✅ Tutti i test passano (`pytest tests/ -v`)
|
||||
|
||||
---
|
||||
|
||||
**Priorità**: Alta
|
||||
|
||||
Reference in new issue
Block a user