juegos

Juegos de terminal de Pancho: Catan (TUI, GUI, web, servidor, PicoCalc), ajedrez, calculadora y minijuegos
git clone https://git.lu3dhn.xyz/juegos.git
Log | Files | Refs

commit 8920f9890b79b7e70180d6827caf4c4f0bce9e78
parent 5815b3a21937a54aaa2aa532c9cb926ba1ed25ad
Author: Francisco Armando Rosmino <franciscorosmino@rosmino.com.ar>
Date:   Sat,  3 Oct 2026 15:39:51 -0300

catan-duel: busqueda nueva contra la vieja

make catan-duel compila ademas la search.c de DUEL_BASE (un tag o commit) con
el simbolo renombrado; --duel ahi enfrenta las dos busquedas en la misma
partida rotando asientos. Es la medida para cualquier cambio de bots.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
M.gitignore | 3+++
MCLAUDE.md | 3++-
Mcatan/Makefile | 14+++++++++++++-
Mcatan/linux/selftest.c | 16+++++++++++++++-
4 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -28,3 +28,6 @@ __pycache__/ ajedrez/engine/stockfish kit-picocalc.zip catan/release/ +catan/catan-duel +catan/core/search_old.c +catan/core/search_old.o diff --git a/CLAUDE.md b/CLAUDE.md @@ -39,6 +39,7 @@ make debug && make test # recompila con ASan/UBSan ./catan --selftest # solo reglas/serialización/replay/redacción (linux/selftest.c) ./catan --sim N [jugadores] [2p] # N partidas bot vs bot verificando invariantes ./catan --duel N jugadores iter horizonte [2p|-] [semilla] [c] # bot ISMCTS vs heurísticos: % de victorias +make catan-duel && ./catan-duel --duel 80 4 100 8 - 7 # búsqueda NUEVA vs la de DUEL_BASE (último tag): así se mide un cambio de bots make catan-server-static # servidor estático para el VPS (instalar con comun/deploy/install-vps.sh) make pico # firmware PicoCalc → sd/pico1-apps/Catan.uf2 y sd/firmware/Catan.bin make web # PWA (emscripten + SDL3 estático) → web/dist/; make deploy-web la sube al VPS (/var/www/catan, Caddy) @@ -65,7 +66,7 @@ Al correr pruebas o el binario, aislar `XDG_CONFIG_HOME`/`XDG_DATA_HOME` en un d - `ctrl.c`: máquina de estados de interacción **compartida** por TUI, GUI y PicoCalc (teclas abstractas / clicks resueltos → `Move`). - i18n: `strings.h` es una X-macro `X(id, es, en)`; todo texto visible va ahí y se lee con `tr()`. - Reglas configurables en `Opts` (variante 2p, ladrón amable, fichas, etc.). - - Bots: `bot.c` heurístico ("fáciles"); `search.c` ISMCTS ("difíciles", `search_decide` + `SearchCfg` en `Host.bot`; iters 0 = heurístico). Determiniza solo lo oculto para ese asiento, candidatas podadas con `bot_int.h`, playouts con `bot_decide`, horizonte 8 + evaluación. Árbol estático (`SEARCH_NODES`, 1024 en la Pico): una búsqueda a la vez. Corta por tiempo: 300 ms Linux (`sess_set_bots`), 150 ms servidor, 1200 ms Pico. Medir cambios con `--duel` (el ruido con 200 partidas es ±3-4 puntos). + - Bots: `bot.c` heurístico ("fáciles"); `search.c` ISMCTS ("difíciles", `search_decide` + `SearchCfg` en `Host.bot`; iters 0 = heurístico). Determiniza solo lo oculto para ese asiento, candidatas podadas con `bot_int.h`, playouts con `bot_decide`, horizonte 8 + evaluación. Árbol estático (`SEARCH_NODES`, 1024 en la Pico): una búsqueda a la vez. Corta por tiempo: 300 ms Linux (`sess_set_bots`), 150 ms servidor, 1200 ms Pico. **Todo cambio de bots se mide nueva contra vieja**: `make catan-duel` (lleva además la `search.c` de `DUEL_BASE`, por defecto el último tag, con el símbolo renombrado a `search_old_decide`) y `./catan-duel --duel 80 4 100 8 - <semilla>` enfrenta la búsqueda nueva contra la vieja en la misma partida rotando asientos (base 25 % con 4 jugadores; correr dos semillas, el ruido con 80 partidas es ±8 puntos). `--duel` en `./catan` sigue siendo contra los heurísticos y solo sirve de sanidad. - **`gfx/`** (`catan_gfx.h`): tablero y pantallas de Catan en píxeles (layouts compact 320x320 y wide) sobre `comun/gfx`. `geom_fit(geo, game, ...)` encuadra cualquier tablero por los límites de sus vértices. Panel ancho: con más de 4 jugadores las filas son más bajas; acciones y costos van en dos columnas. - **GUI ancha**: lienzo lógico de 640x360 (16:9) con escala **entera** (`gk_open_fluid` en `comun/linux/guikit.c`: 2x en 720p, 3x en 1080p; un factor no entero deja la letra despareja). Pensada para 720p/1080p (los amigos de Pancho juegan en Windows con `catan.exe`). Fuentes en `comun/gfx/font.c`, generadas por `comun/tools/mkfont.py` desde `comun/tools/fonts/`: Terminus 6x12 (normal, y engrosada a 7x12 como negrita) y Spleen 5x8 (chica, con los acentos del castellano agregados). - **`linux/`**: cliente. `frontend.h` define la interfaz TUI (`tui.c`, sobre `comun/linux/tuikit`; colores opacos propios — el usuario no quiere depender de la transparencia de su terminal) / GUI (`gui_sdl.c`, sobre `guikit`). `app.c` menús y bucle, `session.c` guardado, `net.c`/`online.c` red (sobre `netline`). diff --git a/catan/Makefile b/catan/Makefile @@ -125,6 +125,18 @@ pico: @echo "Listo: copiá el contenido de sd/ a la raíz de la tarjeta SD de la PicoCalc" @echo "CATAN.LOG y CATAN.SAV van solo la primera vez: si los pisás, perdés el log y la partida guardada" +# Duelo nueva vs vieja: catan-duel lleva ademas la search.c de DUEL_BASE (un tag o commit +# del git) con el simbolo renombrado; --duel ahi enfrenta la busqueda nueva contra la vieja +# en la misma partida, rotando asientos. Asi se ve si un cambio del bot es positivo. +DUEL_BASE ?= v1.1.1 +core/search_old.c: FORCE + git show $(DUEL_BASE):catan/core/search.c > $@ +core/search_old.o: core/search_old.c $(HDRS) + $(CC) $(CPPFLAGS) $(CFLAGS) -Dsearch_decide=search_old_decide -c $< -o $@ +catan-duel: $(CLIENT_SRC) $(HDRS) core/search_old.o + $(CC) $(CPPFLAGS) $(CFLAGS) -DDUEL_OLD -o $@ $(CLIENT_SRC) core/search_old.o $(LDLIBS) +FORCE: + debug: $(MAKE) -B catan CFLAGS="-O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer" @@ -143,4 +155,4 @@ clean: rm -f catan catan-server catan-picosim catan.exe rm -rf pico/build -.PHONY: all debug test clean pico catan-server-static windows web deploy-web release publish +.PHONY: all debug test clean pico catan-server-static windows web deploy-web release publish FORCE diff --git a/catan/linux/selftest.c b/catan/linux/selftest.c @@ -1500,7 +1500,13 @@ int sim_one(uint64_t seed, int players, bool v2p, bool verbose, Auth *a, EmitFn return -1; } -/* Duelo: en la partida i el asiento i % jugadores usa la busqueda, el resto la heuristica. */ +#ifdef DUEL_OLD +/* la search.c de la version publicada (core/search_old.c, la genera el Makefile) */ +bool search_old_decide(const Game *g, int seat, Rng *rng, const SearchCfg *cfg, Move *out); +#endif + +/* Duelo: en la partida i el asiento i % jugadores usa la busqueda, el resto la heuristica + * (o, en catan-duel, la busqueda de la version anterior: nueva contra vieja). */ int app_duel(int n, uint64_t seed0, int players, bool v2p, const SearchCfg *cfg) { static Auth a; @@ -1536,7 +1542,11 @@ int app_duel(int n, uint64_t seed0, int players, bool v2p, const SearchCfg *cfg) clock_gettime(CLOCK_MONOTONIC, &t1); if (d) { ms += (t1.tv_sec - t0.tv_sec) * 1e3 + (t1.tv_nsec - t0.tv_nsec) / 1e6; decisions++; } } else { +#ifdef DUEL_OLD + d = search_old_decide(g, s, &br, cfg, &m); +#else d = bot_decide(g, s, &br, &m); +#endif } if (!d) continue; int e = auth_submit(&a, &m); @@ -1555,7 +1565,11 @@ int app_duel(int n, uint64_t seed0, int players, bool v2p, const SearchCfg *cfg) } if (ok) { done++; if (g->winner == strong) wins++; } } +#ifdef DUEL_OLD + printf("%d partidas (%d jugadores%s, %d iter, horizonte %d): la busqueda NUEVA gano %d de %d contra la VIEJA (%.1f%%, base %.1f%%), %d errores\n", +#else printf("%d partidas (%d jugadores%s, %d iter, horizonte %d): busqueda gano %d de %d (%.1f%%, base %.1f%%), %d errores\n", +#endif n, players, v2p ? ", 2p" : "", cfg->iters, cfg->horizon, wins, done, done ? 100.0 * wins / done : 0.0, 100.0 / humans, bad); printf("%.2f ms por decision (%ld decisiones)\n", decisions ? ms / (double)decisions : 0.0, decisions);