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

catan.h (16316B)


      1 /* catan.h - nucleo portable del juego (reglas, tablero, movidas, autoridad, bots).
      2  *
      3  * Todo lo de core/ es C99 puro: sin malloc, sin stdio, sin sistema operativo.
      4  * Compila igual para Linux, para el servidor y para el RP2040 de la PicoCalc.
      5  *
      6  * Modelo: un nodo "autoridad" (Auth) valida cada movida, resuelve el azar
      7  * (dados, robos, mazo) y la emite ya resuelta. Todos los demas aplican las
      8  * movidas resueltas con game_apply(). Una partida = cabecera + lista de movidas.
      9  */
     10 #ifndef CATAN_H
     11 #define CATAN_H
     12 
     13 #include <stdint.h>
     14 #include <stdbool.h>
     15 #include "rng.h"
     16 #include "str.h"
     17 #include "lang.h"
     18 
     19 /* Tamanos maximos. Tableros: el clasico (19 hexagonos, 3-4 jugadores), el grande de la
     20  * expansion 5-6 (30) y los mapas de Navegantes (con mar: hasta 63). Cada Topo dice
     21  * cuantos usa de verdad (nhex...). Los indices de vertices y aristas entran en 8 bits
     22  * sin signo (<= 255). La PicoCalc compila con CATAN_SMALL: solo el clasico y hasta 4
     23  * jugadores (menos RAM). */
     24 #ifdef CATAN_SMALL
     25 enum { NHEX = 19, NVERT = 54, NEDGE = 72, NPORT = 9, NCOAST = 30, MAXP = 4 };
     26 #else
     27 enum { NHEX = 63, NVERT = 158, NEDGE = 220, NPORT = 11, NCOAST = 38, MAXP = 6 };
     28 #endif
     29 enum { NRES = 5 };
     30 /* Familias de tableros, cada una con NMAPS mapas (el 0 es el de siempre). El tablero es
     31  * familia * NMAPS + mapa. SEA1..3: Navegantes "Nuevas costas", "Cuatro islas", "Archipielago". */
     32 enum { BF_CLASSIC, BF_LARGE, BF_SEA1, BF_SEA2, BF_SEA3, BF__COUNT };
     33 enum { NMAPS = 5 };
     34 enum { BOARD_CLASSIC = BF_CLASSIC * NMAPS, BOARD_LARGE = BF_LARGE * NMAPS, BOARD_SEA1 = BF_SEA1 * NMAPS,
     35        BOARD__COUNT = BF__COUNT * NMAPS };
     36 
     37 enum { BRICK, LUMBER, WOOL, GRAIN, ORE };             /* recursos */
     38 /* terreno i < T_DESERT produce el recurso i; el oro, el que elija el jugador */
     39 enum { T_HILLS, T_FOREST, T_PASTURE, T_FIELDS, T_MOUNTAINS, T_DESERT, T_SEA, T_GOLD, T__COUNT };
     40 static inline bool ter_land(int t) { return t != T_SEA; }
     41 static inline bool ter_res(int t)  { return t < T_DESERT; }      /* produce un recurso fijo */
     42 enum { D_KNIGHT, D_ROADS, D_PLENTY, D_MONO, D_VP, NDEV };
     43 enum { PORT_ANY = 5 };                                 /* 0..4 = puerto 2:1 de ese recurso */
     44 
     45 enum { MAX_ROADS = 15, MAX_SETTLES = 5, MAX_CITIES = 4, MAX_SHIPS = 15 };
     46 /* escenarios de Navegantes: "Nuevas costas", "Cuatro islas", "Archipielago" */
     47 enum { SCEN_NONE, SCEN_SEA1, SCEN_SEA2, SCEN_SEA3, SCEN__COUNT };
     48 enum { DECK_MAX = 34 };                               /* mazo de la expansion 5-6 */
     49 
     50 /* ---------------------------------------------------------------- topologia */
     51 
     52 /* Coordenadas enteras: X en unidades de (sqrt3/2)*R, Y en unidades de R/2,
     53  * con R el radio del hexagono (punta arriba). Centro del hex (q,r) = (2q+r, 3r). */
     54 typedef struct {
     55     uint8_t board;                 /* BOARD_* */
     56     uint8_t family;                /* BF_* */
     57     uint8_t sea;                   /* tiene mar: barcos, pirata, oro */
     58     uint8_t nhex, nvert, nedge, nport, ncoast;
     59     uint8_t nregion;               /* tierras separadas por mar (1 sin mar) */
     60     uint8_t home;                  /* regiones donde se arranca (bit por region) */
     61     int8_t  q[NHEX], r[NHEX];
     62     int16_t hx[NHEX], hy[NHEX];
     63     uint8_t hex_vert[NHEX][6];     /* N, NE, SE, S, SW, NW */
     64     uint8_t hex_edge[NHEX][6];     /* N-NE, NE-SE, SE-S, S-SW, SW-NW, NW-N */
     65     int8_t  hex_adj[NHEX][6];
     66     int16_t vx[NVERT], vy[NVERT];
     67     int8_t  vert_hex[NVERT][3];
     68     int16_t vert_edge[NVERT][3];
     69     int16_t vert_adj[NVERT][3];
     70     uint8_t edge_v[NEDGE][2];
     71     int8_t  edge_hex[NEDGE][2];
     72     uint8_t coast[NCOAST];         /* aristas costeras en orden horario */
     73     uint8_t port_edge[NPORT];      /* aristas con puerto */
     74     int8_t  vert_port[NVERT];      /* indice de puerto o -1 */
     75     int8_t  region[NHEX];          /* -1 mar, 0.. tierras separadas (islas) */
     76     uint8_t kind[NHEX];            /* como viene en el dibujo: HK_* */
     77     int8_t  sea_start;             /* hex donde arranca el pirata (-1 sin mar) */
     78 } Topo;
     79 
     80 enum { HK_SEA, HK_HOME, HK_ISLE, HK_DESERT };      /* mar, tierra de arranque, isla, desierto fijo */
     81 
     82 const Topo *topo_of(int board);
     83 
     84 /* ------------------------------------------------------------------ opciones */
     85 
     86 #define OPTS_FIRST_RANDOM 255
     87 
     88 typedef struct {
     89     uint8_t np;              /* asientos totales (incluye neutrales), 2..MAXP */
     90     uint8_t variant2p;       /* variante oficial de 2 (asientos 2 y 3 neutrales) */
     91     uint8_t vp_target;       /* puntos para ganar (10) */
     92     uint8_t friendly_robber; /* ladron no puede afectar a quien tiene <=2 PV */
     93     uint8_t tokens_start;    /* fichas de comercio iniciales (2p) */
     94     uint8_t neutral_setup;   /* pueblos iniciales por neutral (2p) */
     95     uint8_t beginner;        /* tablero de principiante */
     96     uint8_t setup_first;     /* mascara de asientos que ponen sus dos pueblos antes que el resto */
     97     uint8_t freeplay;        /* al ganar se anuncia pero se sigue jugando */
     98     uint8_t board;           /* lo pone game_init: con mas de 4 jugadores, el grande (5-6) */
     99     uint8_t scenario;        /* SCEN_*: Navegantes usa su mapa, barcos, pirata y oro */
    100     uint8_t map;             /* mapa de la familia (0..NMAPS-1; 0 = el de siempre) */
    101     uint32_t bseed;          /* semilla del tablero (0: sale de la de la partida); la elige quien crea */
    102     uint8_t first;           /* asiento que empieza (setup y primer turno); OPTS_FIRST_RANDOM: lo sortea host_init
    103                                 con la semilla de la partida. Las partidas viejas, sin el campo, arrancan por el 0. */
    104 } Opts;
    105 
    106 void opts_default(Opts *o, int humans, bool variant2p);
    107 void opts_scenario(Opts *o, int scenario);   /* pone el escenario y sus puntos (Navegantes: 13) */
    108 int  opts_board(const Opts *o);              /* tablero que corresponde a esas opciones */
    109 int  opts_family(const Opts *o);             /* BF_* */
    110 int  bank_start(const Opts *o);              /* cartas de cada recurso en el banco: 19 / 24 */
    111 int  dev_counts(const Opts *o, uint8_t out[]);   /* mazo por tipo (NDEV); devuelve el total */
    112 
    113 /* -------------------------------------------------------------------- estado */
    114 
    115 typedef struct {
    116     uint8_t res[NRES];
    117     uint8_t dev[NDEV];       /* cartas conocidas en mano */
    118     uint8_t dev_new[NDEV];   /* compradas este turno: no se pueden jugar */
    119     uint8_t dev_hidden;      /* cartas de otro jugador que esta vista no conoce */
    120     uint8_t knights;
    121     uint8_t roads, settles, cities;   /* piezas que quedan */
    122     uint8_t tokens;
    123     uint8_t road_len;
    124     uint8_t discard;         /* cartas que tiene que descartar */
    125     uint8_t neutral;
    126     uint8_t ships;           /* barcos que quedan (Navegantes) */
    127     uint8_t gold;            /* recursos a elegir por el oro (PH_GOLD) */
    128     uint8_t isles;           /* islas donde ya cobro los 2 puntos (bit por region) */
    129     uint8_t bonus;           /* puntos especiales (islas nuevas) */
    130 } Player;
    131 
    132 typedef struct {
    133     uint8_t active, from;
    134     uint8_t tomask;          /* asientos a los que se ofrece (bit por asiento) */
    135     uint8_t give[NRES], get[NRES];   /* desde el punto de vista de quien ofrece */
    136     int8_t  resp[MAXP];      /* 0 = sin respuesta, 1 acepta, -1 rechaza, 2 contraoferta */
    137     uint8_t cgive[MAXP][NRES], cget[MAXP][NRES];   /* contraofertas, desde quien responde */
    138 } Offer;
    139 
    140 enum Phase {
    141     PH_SETUP, PH_ROLL, PH_DISCARD, PH_ROBBER, PH_MAIN, PH_ROADBUILD,
    142     PH_NEUTRAL, PH_GIVEBACK, PH_OVER,
    143     PH_PAIRED,               /* 5-6: la pareja del turno construye (va al final: el numero viaja en STAT) */
    144     PH_GOLD                  /* Navegantes: los que cobran oro eligen el recurso */
    145 };
    146 
    147 enum { NEED_NONE, NEED_ROAD, NEED_SETTLE };
    148 
    149 typedef struct {
    150     Opts    o;
    151     uint8_t terrain[NHEX], num[NHEX], port[NPORT];
    152     int8_t  vown[NVERT];
    153     uint8_t vlev[NVERT];     /* 0 vacio, 1 pueblo, 2 ciudad */
    154     int8_t  eown[NEDGE];
    155     uint8_t ship[NEDGE];     /* 1: en esa arista hay un barco (no una ruta) */
    156     uint8_t ship_new[(NEDGE + 7) / 8];   /* barcos puestos en este turno (no se mueven) */
    157     uint8_t pirate;          /* hex de mar con el pirata (Navegantes) */
    158     uint8_t ship_moved;      /* ya movio un barco en este turno */
    159     uint8_t robber;
    160     uint8_t bank[NRES];
    161     uint8_t deck_n;
    162     Player  p[MAXP];
    163     uint8_t cur, phase, ret_phase;
    164     uint8_t setup_i, setup_sub, setup_n, setup_order[2 * MAXP];
    165     int16_t setup_v;         /* ultimo pueblo del setup: la ruta tiene que tocarlo (hay mas de 127 vertices) */
    166     uint8_t rolled, dev_played, free_roads;
    167     uint8_t dice[4];         /* dice[2..3] = segunda tirada (2p) */
    168     uint8_t neutral_need;
    169     uint8_t give_back;
    170     int8_t  longest, army, winner;
    171     Offer   offer;
    172     uint16_t turn;
    173     uint32_t nmoves;
    174     uint8_t undo_n;          /* acciones deshacibles en este turno (ver Undo) */
    175     uint8_t offers;          /* ofertas hechas en este turno */
    176     uint8_t owner;           /* PH_PAIRED: de quien es el turno (cur es la pareja) */
    177 } Game;
    178 
    179 /* -------------------------------------------------------------------- movidas */
    180 
    181 enum MoveType {
    182     M_NONE, M_SETTLE, M_ROAD, M_CITY, M_ROLL, M_DISCARD, M_ROBBER, M_BUYDEV,
    183     M_KNIGHT, M_ROADS, M_PLENTY, M_MONO, M_BANK, M_OFFER, M_ACCEPT, M_REJECT,
    184     M_CONFIRM, M_CANCEL, M_END, M_NEUTRAL, M_TOKTRADE, M_GIVEBACK, M_TOKROBBER,
    185     M_WIN, M_UNDO, M_COUNTER, M_SHIP, M_MOVESHIP, M_GOLD, M__COUNT
    186 };
    187 
    188 /* Argumentos por tipo:
    189  *  SETTLE/CITY a=vertice   ROAD a=arista
    190  *  ROLL r[0..3]=dados (resuelto)          DISCARD r[]=cartas
    191  *  ROBBER a=hex b=victima(-1) c=recurso robado (resuelto, -1 nada)
    192  *  BUYDEV a=carta (resuelto; -1 si esta redactada)
    193  *  PLENTY a,b=recursos   MONO a=recurso   BANK a=doy b=recibo c=cantidad recibida
    194  *  OFFER r=doy r2=pido a=mascara de destinatarios (-1 todos)   CONFIRM a=con quien
    195  *  COUNTER r=doy r2=pido (quien responde una oferta propone otros terminos)
    196  *  SHIP a=arista   MOVESHIP a=desde b=hasta   GOLD r[]=recursos elegidos por el oro
    197  *  NEUTRAL a=neutral b=0 ruta / 1 pueblo c=lugar
    198  *  TOKTRADE a,b=cartas tomadas (resuelto, -1 ninguna)   GIVEBACK r[]=cartas devueltas
    199  *  WIN a=cartas de PV reveladas */
    200 typedef struct {
    201     uint8_t type;
    202     int8_t  p;
    203     int16_t a, b, c;
    204     uint8_t r[NRES], r2[NRES];
    205 } Move;
    206 
    207 enum Err {
    208     E_OK, E_PHASE, E_TURN, E_ARGS, E_PLACE, E_RES, E_PIECES, E_NODEV,
    209     E_DEVPLAYED, E_BANK, E_ROBBER, E_OFFER, E_TOKENS, E__COUNT
    210 };
    211 
    212 /* ------------------------------------------------------------------ board.c */
    213 
    214 void board_beginner(Game *g);                          /* solo el clasico */
    215 struct Rng;
    216 void board_random(Game *g, int board, struct Rng *rng);
    217 const char *board_name(int board);                     /* nombre del mapa ("" si no existe) */
    218 /* Tablero y estado inicial de una partida con esas opciones, como lo arma la autoridad:
    219  * con o->bseed sale solo de esa semilla (para mostrarlo antes de crear la partida). */
    220 void board_make(Game *g, const Opts *o, struct Rng *game_rng);
    221 static inline const Topo *game_topo(const Game *g) { return topo_of(g->o.board); }
    222 
    223 /* ------------------------------------------------------------------- game.c */
    224 
    225 void game_init(Game *g, const Opts *o);      /* tablero ya cargado aparte */
    226 void game_reset_board(Game *g);              /* robber en el desierto, etc */
    227 int  game_check(const Game *g, const Move *m);
    228 void game_apply(Game *g, const Move *m);
    229 
    230 /* Deshacer: fotos del estado antes de cada accion deshacible del turno
    231  * (construir, cambiar con el banco, construccion neutral). Todos los nodos
    232  * aplican las movidas con game_apply_u y guardan las mismas fotos, asi M_UNDO
    233  * es determinista en todos lados. No se deshace nada que involucre azar o a
    234  * otros jugadores. */
    235 enum { UNDO_MAX = 12 };
    236 typedef struct { Game snap[UNDO_MAX]; uint8_t n; } Undo;
    237 bool game_undoable(const Game *g, const Move *m);
    238 void game_apply_u(Game *g, Undo *u, const Move *m);
    239 
    240 int  res_total(const uint8_t r[NRES]);
    241 int  hand_size(const Game *g, int p);
    242 int  game_vp(const Game *g, int p);          /* con las cartas de PV que la vista conoce */
    243 int  game_vp_public(const Game *g, int p);   /* sin cartas de PV */
    244 bool game_can_settle(const Game *g, int p, int v, bool setup);
    245 bool game_can_road(const Game *g, int p, int e);
    246 bool game_can_ship(const Game *g, int p, int e, bool setup);     /* setup: pegado al pueblo del setup */
    247 bool game_ship_movable(const Game *g, int p, int e);             /* barco suelto en una punta */
    248 bool game_can_moveship(const Game *g, int p, int from, int to);
    249 bool game_has_sea(const Game *g);
    250 bool game_can_city(const Game *g, int p, int v);
    251 bool game_can_robber(const Game *g, int p, int h);
    252 bool game_robber_victim_ok(const Game *g, int p, int h, int victim);
    253 int  game_bank_ratio(const Game *g, int p, int res);
    254 int  game_road_len(const Game *g, int p);
    255 bool game_can_afford(const Game *g, int p, const uint8_t cost[NRES]);
    256 bool game_needs(const Game *g, int seat);    /* seat tiene que (o puede) actuar ahora */
    257 int  game_rival(const Game *g, int p);       /* 2p: el otro jugador humano */
    258 int  game_token_cost(const Game *g, int p);
    259 bool game_neutral_possible(const Game *g, int kind);
    260 bool game_is_neutral(const Game *g, int p);
    261 int  game_next_player(const Game *g, int p);
    262 int  game_pair(const Game *g, int p);        /* 5-6 "en pareja": quien juega con p (3 asientos despues), -1 si no hay */
    263 uint8_t game_offer_candidates(const Game *g, int p);   /* a quienes se puede ofrecer */
    264 
    265 extern const uint8_t COST_ROAD[NRES], COST_SETTLE[NRES], COST_CITY[NRES], COST_DEV[NRES], COST_SHIP[NRES];
    266 
    267 /* ------------------------------------------------------------------- move.c */
    268 
    269 /* Serializacion a texto (una linea, sin '\n'). Devuelven largo o -1. */
    270 int  move_fmt(const Move *m, char *buf, int n);
    271 bool move_parse(const char *s, Move *m);
    272 void move_redact(const Move *m, int viewer, Move *out);
    273 /* Para el registro, como en la mesa: que carta se robo lo saben solo el ladron y la
    274  * victima. viewers = asientos que miran esta pantalla (0: nadie en particular). */
    275 void move_public(const Move *m, unsigned viewers, Move *out);
    276 bool move_is_auto(const Move *m);      /* la genera la autoridad sola */
    277 int  hdr_fmt(const Game *g, char *buf, int n);
    278 bool hdr_parse(const char *s, Game *g);  /* carga opts + tablero e inicializa */
    279 const char *move_name(int type);
    280 
    281 
    282 /* ------------------------------------------------------------------- auth.c */
    283 
    284 typedef void (*EmitFn)(void *ctx, const Move *m);
    285 
    286 typedef struct {
    287     Game     g;              /* estado completo, sin redactar */
    288     Rng      rng;
    289     uint64_t seed;
    290     uint8_t  deck[DECK_MAX];
    291     Undo     undo;
    292     EmitFn   emit;
    293     void    *ctx;
    294 } Auth;
    295 
    296 /* Crea la partida: genera tablero y mazo, y emite las movidas automaticas
    297  * del arranque (pueblos neutrales). emit puede ser NULL. */
    298 void auth_new(Auth *a, const Opts *o, uint64_t seed, EmitFn emit, void *ctx);
    299 /* Valida, resuelve el azar, aplica y emite. m queda resuelta. */
    300 int  auth_submit(Auth *a, Move *m);
    301 
    302 /* -------------------------------------------------------------------- bot.c */
    303 
    304 /* Si seat tiene que actuar, propone una movida (sin resolver). */
    305 bool bot_decide(const Game *g, int seat, Rng *rng, Move *out);
    306 /* Comercio: si p ofrecio y falta que contesten, bot_decide no juega (espera). Quien
    307  * lo maneje puede cortar la espera con bot_close_offer (mejor respuesta o cancelar). */
    308 bool bot_offer_pending(const Game *g, int p);
    309 bool bot_close_offer(const Game *g, int p, Move *out);
    310 
    311 /* ----------------------------------------------------------------- search.c */
    312 
    313 /* Bot fuerte (ISMCTS). Usa solo info publica + su mano: sortea lo oculto. */
    314 typedef struct {
    315     int      iters;          /* iteraciones (<= 0: heuristica pura) */
    316     int      horizon;        /* turnos simulados tras la hoja (0 = hasta el final) */
    317     float    c_ucb;          /* exploracion */
    318     uint32_t (*now_ms)(void);    /* opcional: corte por tiempo */
    319     uint32_t max_ms;
    320 } SearchCfg;
    321 
    322 bool search_decide(const Game *g, int seat, Rng *rng, const SearchCfg *cfg, Move *out);
    323 
    324 /* ------------------------------------------------------------------- i18n.c */
    325 
    326 const char *res_name(int r);
    327 const char *res_short(int r);
    328 const char *terrain_name(int t);
    329 const char *dev_name(int d);
    330 const char *err_text(int e);
    331 const char *phase_text(int ph);
    332 const char *tr(int id);
    333 
    334 #include "strings.h"
    335 
    336 #endif