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

README.md (15649B)


      1 # Catan en C — terminal, gráfico, online y PicoCalc
      2 
      3 Catan (juego base completo + variante oficial para 2) escrito en C99 portable.
      4 El mismo núcleo corre en:
      5 
      6 - **la terminal** (TUI con ncurses, tablero dibujado con medios bloques de color),
      7 - **una ventana gráfica** (SDL3, pixel-art),
      8 - **un servidor** para tu VPS (partidas online en vivo o asincrónicas, que sobreviven reinicios),
      9 - **la PicoCalc** (Raspberry Pi Pico 1), con **cable 1v1** entre dos PicoCalc.
     10 
     11 Todos hablan el mismo protocolo: alguien en la terminal y alguien en la versión
     12 gráfica juegan la misma partida sin notar diferencia.
     13 
     14 ## Compilar
     15 
     16 Dependencias: `gcc`/`clang`, `make`, `ncursesw`, `SDL3` (solo para la GUI y el simulador).
     17 
     18 ```sh
     19 make            # catan, catan-server, catan-picosim
     20 make test       # pruebas del núcleo + 400 partidas bot contra bot + duelos búsqueda vs heurística
     21 make pico       # firmware PicoCalc (ver más abajo)
     22 ```
     23 
     24 ## Jugar
     25 
     26 ```sh
     27 ./catan                 # menú (terminal)
     28 ./catan-gui             # lo mismo, en ventana gráfica   (= ./catan --gui)
     29 ./picocalc-sim          # simulador de la PicoCalc en la PC
     30 
     31 ./catan hotseat -n 4 --bots 3      # vos contra 3 bots, directo
     32 ./catan hotseat -n 2 --2p          # 1v1 en la misma compu con la variante para 2
     33 ./catan resume                     # retomar la última partida
     34 ./catan online mivps.com:7373      # mis partidas en el servidor
     35 ./catan host 7373                  # crear partida en red local
     36 ./catan join 192.168.0.10 7373     # unirse a una partida en red local
     37 ```
     38 
     39 Opciones: `--lang es|en`, `--gui`, `--layout wide|compact`, `--scale N`.
     40 Las partidas locales se guardan solas en `~/.local/share/catan/saves/`.
     41 
     42 ### Controles
     43 
     44 | Tecla | Acción |
     45 |---|---|
     46 | Flechas / hjkl | mover el cursor por los lugares válidos |
     47 | Enter / Espacio | confirmar |
     48 | Esc | volver / cancelar |
     49 | `r` | tirar los dados |
     50 | `1` `2` `3` `4` | ruta, pueblo, ciudad, comprar carta |
     51 | `d` | jugar carta de desarrollo |
     52 | `b` | comerciar con banco / puerto |
     53 | `t` | ofrecer trueque a los demás |
     54 | `e` | terminar el turno |
     55 | `f` `z` | (variante 2p) comercio forzado, ladrón al desierto |
     56 | `Tab` | siguiente objetivo / panel de detalle |
     57 | `?` `L` `q` | ayuda, idioma, salir |
     58 
     59 Con mouse (terminal o GUI) se puede clickear en el tablero, las acciones y los menús.
     60 
     61 En los diálogos de cartas (descartar, trueque, descubrimiento): ←→ elige recurso,
     62 ↑↓ o `+`/`-` cambian la cantidad, `1`-`5` suman, Tab cambia entre "das" y "pedís".
     63 
     64 ## Reglas
     65 
     66 Catan base completo: colocación inicial en serpiente, producción (con la regla de
     67 banco sin cartas), 7 con descarte y ladrón, rutas/pueblos/ciudades, cartas de
     68 desarrollo (14 caballeros, 5 PV, 2 de cada progreso), camino más largo (cortado
     69 por edificios ajenos), ejército más grande, comercio 4:1 / 3:1 / 2:1 y trueques
     70 entre jugadores. Se gana con 10 PV en tu turno.
     71 
     72 **Variante para 2** (inspirada en la de *Mercaderes y Bárbaros*):
     73 
     74 - Hay 2 jugadores **neutrales** con 2 pueblos y 2 rutas iniciales al azar.
     75 - Cada vez que construís una ruta tenés que construirle una ruta gratis a un
     76   neutral; cada pueblo, un pueblo neutral (o una ruta si no hay lugar).
     77 - **Dos tiradas por turno**; la segunda no puede sumar lo mismo que la primera.
     78 - **Fichas de comercio**: arrancás con 2, ganás 1 por cada caballero jugado y por
     79   cada pueblo al lado del desierto. Cuestan 1 (2 si vas ganando) y sirven para:
     80   - **comercio forzado**: le sacás 2 cartas al azar al rival y le devolvés 2 que elijas;
     81   - **ladrón al desierto**.
     82 - **Ladrón amable**: no puede ir a un hexágono de alguien con 2 PV o menos.
     83 
     84 Los valores están en `Opts` (`core/catan.h`) por si tu edición cambia algo.
     85 
     86 ## Online: servidor en tu VPS
     87 
     88 ```sh
     89 make catan-server-static                 # binario estático, anda en cualquier Linux x86-64
     90 scp catan-server ../comun/deploy/install-vps.sh root@mivps:/tmp/
     91 ssh root@mivps sh /tmp/install-vps.sh catan 7373 /tmp/catan-server "Servidor de Catan"
     92 ```
     93 
     94 Eso crea el usuario `catan`, guarda las partidas en `/var/lib/catan` y lo deja
     95 corriendo con systemd en el puerto **7373** (abrilo en el firewall). Logs:
     96 `journalctl -u catan-server -f`.
     97 
     98 Cómo se usa:
     99 
    100 1. En `./catan` → *Jugar online*, poné `mivps.com:7373`, tu **nombre** y una **contraseña**.
    101    No hay cuentas: nombre + contraseña son tu llave (el cliente manda solo un hash).
    102 2. *Crear partida*: elegís jugadores, qué asientos son bots, el nivel de los bots (fáciles,
    103    medios o difíciles: la misma búsqueda con más o menos iteraciones) y **"Bot juega si no
    104    hay"** (nunca, 1, 4, 12, 24 o 48 horas). Quién empieza se sortea. Te da un **código** de 6 letras; pasáselo a tus amigos.
    105 3. Tus amigos: *Jugar online* → su nombre y contraseña → el código → *Unirse con el código*.
    106 4. Se juega igual estén todos o no: los conectados ven todo en vivo y la partida da la
    107    vuelta hasta llegar a alguien que no está. Ahí espera; si pasa el tiempo elegido, el
    108    bot juega ese turno por él y sigue. Cerrá cuando quieras: otro día, desde **cualquier
    109    compu**, nombre + contraseña → *Mis partidas* muestra las tuyas y cuáles dicen **¡TE TOCA!**.
    110 
    111 Dos instancias en la misma compu con distintos nombres son dos jugadores distintos.
    112 Sin contraseña, *Mis partidas* usa las partidas guardadas en esta compu
    113 (`~/.local/share/catan/online`, como antes). El tráfico va en texto plano; si querés
    114 cifrarlo, usá un túnel SSH (`ssh -L 7373:localhost:7373 mivps`) o stunnel.
    115 
    116 Al terminar una partida, **jugar otra** (`a`, o tocar la línea en el cuadro final) arma
    117 una nueva con las mismas opciones: local, con otro reparto; online, el servidor la crea
    118 con la misma gente en los mismos asientos y a los demás les aparece en *Mis partidas*.
    119 
    120 **Si alguien se olvida la contraseña**: el servidor no la guarda (solo la llave que
    121 deriva el cliente), pero el administrador puede ponerle una nueva a ese asiento:
    122 
    123 ```sh
    124 sudo catan-admin partidas                          # códigos y asientos
    125 sudo catan-admin reset ECM8CA Félix nuevaclave     # Félix entra con "nuevaclave" desde ahora
    126 ```
    127 
    128 (`install-vps.sh` instala `catan-admin`; el secreto está en `/var/lib/catan/admin.key`.)
    129 
    130 ## Web: la PWA para el teléfono
    131 
    132 El mismo cliente compilado a WebAssembly (`make web`, ver abajo) se sirve desde una
    133 página HTTPS. En el teléfono se entra a la dirección, aparece el menú de siempre y con
    134 *Agregar a pantalla de inicio* queda como una app. En vertical el tablero va arriba y
    135 el panel abajo; en apaisado es el layout ancho de la PC. Lo que en la PC es una tecla
    136 (Esc, ayuda, idioma, salir) son botones; el chat y los campos de texto abren un cuadro
    137 del navegador (SDL no muestra el teclado táctil).
    138 
    139 - Las partidas locales y la configuración se guardan en el almacenamiento del navegador
    140   (IndexedDB): sobreviven a cerrar y recargar, se pierden si se borran los datos del
    141   sitio. En iPhone, instalada en la pantalla de inicio, queda exenta de la limpieza de
    142   7 días de Safari. Con la app instalada, jugar solo anda sin internet.
    143 - Online es idéntico a la PC: nombre + contraseña dan la misma llave, así que *Mis
    144   partidas* muestra las mismas y se puede meter un turno desde el celular en la partida
    145   que se juega en la compu. El campo "Servidor" no aparece: la web habla siempre con el
    146   servidor de la página.
    147 
    148 **Compilar** (`make web` → `web/dist/`): necesita emscripten (`pacman -S emscripten`,
    149 `source /etc/profile.d/emscripten.sh`) y la fuente de SDL3 (la misma de `make windows`);
    150 la primera vez compila SDL3 para wasm en `~/.local/opt/SDL3-em`. El cliente no cambia:
    151 `-sASYNCIFY` hace que sus esperas (SDL, `poll()`) cedan al navegador, y los sockets van
    152 por un WebSocket. Para probar en la PC:
    153 
    154 ```sh
    155 ./catan-server --port 7373 --data /tmp/catan-data &      # acepta TCP y WebSocket en el mismo puerto
    156 (cd web/dist && python3 -m http.server 8080)
    157 chromium --window-size=390,844 'http://localhost:8080/?ws=ws://localhost:7373'
    158 ```
    159 
    160 **Servir**: el navegador necesita `wss://` (TLS), que pone Caddy (o nginx) delante del
    161 servidor; el `catan-server` entiende WebSocket solo (`comun/server/ws.c`). Bloque de
    162 `/etc/caddy/Caddyfile`:
    163 
    164 ```
    165 catan.lu3dhn.xyz {
    166     encode zstd gzip
    167     handle /ws { reverse_proxy 127.0.0.1:7373 }
    168     handle { root * /var/www/catan; file_server }
    169 }
    170 ```
    171 
    172 `make deploy-web` sube `web/dist/` al VPS (host `vps` de ssh) a `/var/www/catan`.
    173 
    174 Pendientes conocidos de la web: en iPhone el cuadro de texto no abre el teclado solo
    175 (hay que tocar el campo); una partida local vive en el navegador de ese teléfono (si se
    176 borran los datos del sitio se pierde: no hay exportar/importar todavía); una sola
    177 instancia a la vez (la segunda pestaña avisa y no arranca).
    178 
    179 ## PicoCalc
    180 
    181 ### Compilar y cargar desde la tarjeta SD (sin cable USB)
    182 
    183 Hace falta el compilador ARM y el pico-sdk:
    184 
    185 ```sh
    186 sudo pacman -S arm-none-eabi-gcc arm-none-eabi-newlib
    187 git clone --depth 1 --recurse-submodules https://github.com/raspberrypi/pico-sdk ~/pico-sdk
    188 make pico            # arma la carpeta sd/ lista para copiar
    189 ```
    190 
    191 Copiá a la **tarjeta SD** de la PicoCalc lo que corresponda a tu cargador:
    192 
    193 - **Cargador de ClockworkPi "Bootloader v0.5"** (la SD tiene la carpeta `firmware/` con
    194   archivos `.bin`, y al prender aparece la lista): copiá `sd/firmware/Catan.bin` a
    195   `firmware/` y elegilo en la lista. Es el que está probado.
    196 - **uf2loader** (SD v0.6 o más nueva, carpeta `pico1-apps/` con `.uf2`): copiá
    197   `sd/pico1-apps/Catan.uf2` a `pico1-apps/` y elegilo en el menú (encendé con **Arriba**).
    198 
    199 Además van en la raíz de la SD dos archivos que `make pico` deja en `sd/`:
    200 
    201 - `CATAN.SAV` (80 KB): **la partida guardada**. Sin este archivo el juego no guarda.
    202 - `CATAN.LOG` (256 KB de espacios): cada arranque agrega ahí qué pasó (pantalla,
    203   teclado, cable, SD, errores). Si algo falla, ese archivo dice dónde.
    204 
    205 Copialos **solo la primera vez**: si después los volvés a copiar encima, perdés la
    206 partida guardada y el log. El firmware nunca crea archivos ni toca la FAT: solo
    207 reescribe el contenido de estos dos, así que no puede romper la tarjeta. Si hace
    208 falta crearlos a mano:
    209 
    210 ```sh
    211 head -c 81920 /dev/zero > /ruta/a/la/sd/CATAN.SAV
    212 head -c 262144 /dev/zero | tr '\0' ' ' > /ruta/a/la/sd/CATAN.LOG
    213 ```
    214 
    215 **Detalles del arranque desde la SD** (lo que costó hacerlo andar):
    216 
    217 - El cargador salta al programa sin apagar nada: deja interrupciones armadas y **su
    218   segundo núcleo (core 1) corriendo y dibujando en la pantalla**. Catan limpia las
    219   interrupciones en su primera instrucción (`catan_early_reset`, que
    220   `comun/tools/patch_reset.py` instala en el vector de reset del `.bin`) y resetea el core 1
    221   antes de tocar la pantalla.
    222 - La pantalla necesita cada comando con sus parámetros bajo un mismo *chip select*.
    223 - La flash de la Pico no se escribe nunca: la partida va en `CATAN.SAV`, en dos
    224   mitades que se alternan. Si se corta la energía mientras guarda, queda el guardado
    225   anterior.
    226 
    227 Antes de probar en el aparato, probalo en la PC: `./picocalc-sim` corre **el mismo código**.
    228 
    229 En el menú principal está **"Prueba: 10 partidas de bots"**: juega 10 partidas completas
    230 en la Pico verificando las reglas en cada movida y deja el resultado en la pantalla y en
    231 `catan.log`.
    232 
    233 ### El cable 1v1
    234 
    235 La PicoCalc tiene dos headers de 8 pines. Usamos **J703** (el del lado de la Pico),
    236 con el UART1 del RP2040:
    237 
    238 | Pin J703 | Señal | Conectar con la otra PicoCalc |
    239 |---|---|---|
    240 | 4 | GP4 = TX | pin **5** |
    241 | 5 | GP5 = RX | pin **4** |
    242 | 8 | GND | pin **8** |
    243 | 1 | 3V3 | **no conectar** |
    244 
    245 ```
    246 PicoCalc A (J703)        PicoCalc B (J703)
    247    pin 4 (TX) ─────────────── pin 5 (RX)
    248    pin 5 (RX) ─────────────── pin 4 (TX)
    249    pin 8 (GND) ────────────── pin 8 (GND)
    250 ```
    251 
    252 - Las dos son de 3,3 V: no hace falta conversor. Cable corto (< 1 m). 115200 baudios.
    253 - GP2-GP5 y GP21 los comparte la PSRAM de la placa; el firmware deja su CS (GP20)
    254   en alto, así que no molesta (el juego no usa la PSRAM).
    255 - **No** usamos J702 (UART0): va al chip USB-serie del USB-C y chocarían las señales.
    256 
    257 Uso: en una PicoCalc *Cable 1v1: crear (A)*, en la otra *Cable 1v1: unirse (B)*.
    258 La A es la autoridad (dados, mazo, bots extra) y guarda la partida en su SD;
    259 si se desenchufa el cable, la B vuelve a sincronizar sola al reconectar.
    260 Cada línea lleva un checksum; si llega algo corrupto, la B pide todo de nuevo.
    261 
    262 ## Cómo está hecho
    263 
    264 ```
    265 core/     reglas, tablero, movidas, autoridad, bots, i18n, controlador de UI
    266           (C99 sin malloc ni stdio: compila igual para Linux y el RP2040)
    267 gfx/      dibujo de Catan en pixeles (tablero, pantallas compacta/ancha)
    268 linux/    cliente: TUI (ncurses), GUI (SDL3), menús, red, guardado, pruebas
    269 server/   Catan como módulo del servidor de partidas (también embebido en `catan host`)
    270 pico/     firmware PicoCalc: app.c (portable, corre igual en el simulador)
    271 app_config.h  nombre de la app, archivos de la SD
    272 ```
    273 
    274 Lo que no es de Catan vive en `../comun/` (ver `comun/README.md`): renderer de
    275 pixeles y fuente, formularios, kits de TUI y SDL, red, el esqueleto del servidor,
    276 la capa de hardware de la PicoCalc (pantalla, teclado, SD, cable) y su simulador,
    277 y el instalador del VPS. El ajedrez usa lo mismo.
    278 
    279 La idea central: un nodo **autoridad** valida cada movida y resuelve el azar
    280 (dados, robos, mazo); todos los demás aplican las movidas ya resueltas con
    281 `game_apply()`. Una partida es su **log de movidas**, así que guardar, retomar,
    282 reconectar y jugar asincrónico salen de lo mismo. Las cartas de desarrollo
    283 compradas se ocultan a los demás (`move_redact`).
    284 
    285 ### Bots
    286 
    287 Hay dos niveles (en *Nueva partida*: "Bots: Fáciles / Difíciles", se recuerda en la config):
    288 
    289 - **Fáciles** (`core/bot.c`): heurística de una movida. Valora esquinas por pips,
    290   diversidad y puertos, junta para un objetivo (pueblo, ciudad, ruta o carta),
    291   cambia con el banco hacia lo que le falta. No propone cambios a otros.
    292 - **Difíciles** (`core/search.c`): ISMCTS. En cada iteración sortea lo que el bot
    293   no puede ver (cartas de desarrollo ajenas y orden del mazo), baja por un árbol
    294   de movidas candidatas (podadas con la heurística) eligiendo con UCB, juega 8
    295   turnos más con el bot fácil y evalúa (puntos, producción, cartas). Cada nodo
    296   guarda el puntaje de quien movió (max-n). Corta por tiempo: 300 ms por decisión
    297   en la PC, 150 ms en el servidor, 1,2 s en la PicoCalc.
    298 
    299 Contra tres bots fáciles, uno difícil gana ~70-78 % de las partidas de 4
    300 (base 25 %) y ~85 % de las de 2 (`./catan --duel`).
    301 
    302 ### Protocolo
    303 
    304 Líneas de texto terminadas en `\n` (general en `comun/server/srv.h`, lo de Catan en `server/server.h`):
    305 
    306 ```
    307 -> HELLO 1 Ana              <- WELCOME 1
    308 -> CREATE 2 1 10 0 HH       <- GAME K7QX2M 0 <token>
    309                             <- HDR G ...        (tablero y opciones)
    310                             <- SEAT 0 P 1 Ana
    311                             <- EV 2 NEUTRAL 2 1 34   (historial...)
    312                             <- SYNC
    313 -> MOVE 0 SETTLE 12         <- EV 0 SETTLE 12
    314 -> MOVE 0 ROLL 0 0 0 0      <- EV 0 ROLL 3 4 2 5 (la autoridad pone los dados)
    315 ```
    316 
    317 ### Pruebas
    318 
    319 ```sh
    320 ./catan --selftest          # reglas, serialización, replay, redacción
    321 ./catan --sim 2000 4        # 2000 partidas de bots: invariantes (recursos, piezas, mazo)
    322 ./catan --sim 2000 2 2p     # lo mismo con la variante para 2
    323 ./catan --duel 100 4 2000 8 # 100 partidas: un bot difícil (2000 iteraciones, horizonte 8)
    324                             # contra fáciles, rotando el asiento; también 2p, semilla y c de UCB
    325 make debug && make test     # con AddressSanitizer / UBSan
    326 ```