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