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

selftest.c (69196B)


      1 /* selftest.c - pruebas del nucleo (--selftest), simulacion bot contra bot (--sim) y duelo busqueda contra heuristica (--duel). */
      2 #include <stddef.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <time.h>
      6 #include <string.h>
      7 #include "../core/catan.h"
      8 #include "../core/ctrl.h"
      9 #include "app.h"
     10 #include "sha1.h"
     11 #include "ws.h"
     12 
     13 static int fails, checks;
     14 static int sim_undo_pct;   /* % de veces que un bot "se arrepiente" (prueba de deshacer) */
     15 
     16 #define CHECK(cond) do { checks++; if (!(cond)) { fails++; printf("  FALLA %s:%d: %s\n", __FILE__, __LINE__, #cond); } } while (0)
     17 
     18 /* ---------------------------------------------------------------- helpers */
     19 
     20 static void fresh(Game *g, int np, bool v2p)
     21 {
     22     Opts o;
     23     opts_default(&o, np, v2p);
     24     o.beginner = 1;
     25     board_beginner(g);
     26     game_init(g, &o);
     27 }
     28 
     29 static Move mv(int type, int p, int a, int b, int c)
     30 {
     31     Move m;
     32     memset(&m, 0, sizeof m);
     33     m.type = (uint8_t)type; m.p = (int8_t)p; m.a = (int16_t)a; m.b = (int16_t)b; m.c = (int16_t)c;
     34     return m;
     35 }
     36 
     37 static int do_move(Game *g, Move m)
     38 {
     39     int e = game_check(g, &m);
     40     if (e == E_OK) game_apply(g, &m);
     41     return e;
     42 }
     43 
     44 /* Salta el setup poniendo pueblos/rutas directamente (sin validar). */
     45 static void place(Game *g, int p, int v, int lev)
     46 {
     47     g->vown[v] = (int8_t)p; g->vlev[v] = (uint8_t)lev;
     48     if (lev == 1) g->p[p].settles--; else g->p[p].cities--;
     49 }
     50 
     51 static void road(Game *g, int p, int e) { g->eown[e] = (int8_t)p; g->p[p].roads--; }
     52 
     53 static void to_main(Game *g, int p)
     54 {
     55     g->phase = PH_MAIN; g->cur = (uint8_t)p; g->rolled = 1;
     56 }
     57 
     58 static bool res_conserved(const Game *g)
     59 {
     60     for (int r = 0; r < NRES; r++) {
     61         int n = g->bank[r];
     62         for (int p = 0; p < g->o.np; p++) n += g->p[p].res[r];
     63         if (n != bank_start(&g->o)) return false;
     64     }
     65     return true;
     66 }
     67 
     68 /* ------------------------------------------------------------------ tests */
     69 
     70 static void topology_of(int board, int nhex, int nvert, int nedge, int ncoast, int nport, int inner)
     71 {
     72     const Topo *t = topo_of(board);
     73     CHECK(t->board == board && t->nhex == nhex && t->nvert == nvert && t->nedge == nedge);
     74     CHECK(t->ncoast == ncoast && t->nport == nport);
     75     int deg = 0, coast = 0, three = 0;
     76     for (int v = 0; v < t->nvert; v++) {
     77         int nh = 0;
     78         for (int k = 0; k < 3; k++) {
     79             if (t->vert_adj[v][k] >= 0) deg++;
     80             if (t->vert_hex[v][k] >= 0) nh++;
     81         }
     82         CHECK(nh >= 1);
     83         if (nh == 3) three++;
     84     }
     85     CHECK(deg == 2 * t->nedge);
     86     CHECK(three == inner);
     87     for (int e = 0; e < t->nedge; e++) if (t->edge_hex[e][1] < 0) coast++;
     88     CHECK(coast == t->ncoast);
     89     for (int i = 0; i < t->ncoast; i++)
     90         for (int j = i + 1; j < t->ncoast; j++) CHECK(t->coast[i] != t->coast[j]);
     91     /* cada arista costera consecutiva comparte un vertice */
     92     for (int i = 0; i < t->ncoast; i++) {
     93         int a = t->coast[i], b = t->coast[(i + 1) % t->ncoast];
     94         bool share = t->edge_v[a][0] == t->edge_v[b][0] || t->edge_v[a][0] == t->edge_v[b][1] ||
     95                      t->edge_v[a][1] == t->edge_v[b][0] || t->edge_v[a][1] == t->edge_v[b][1];
     96         CHECK(share);
     97     }
     98     int ports = 0;
     99     for (int v = 0; v < t->nvert; v++) if (t->vert_port[v] >= 0) ports++;
    100     CHECK(ports == 2 * t->nport);               /* ningun vertice con dos puertos */
    101 }
    102 
    103 static void t_topology(void)
    104 {
    105     topology_of(BOARD_CLASSIC, 19, 54, 72, 30, 9, 24);
    106     topology_of(BOARD_LARGE, 30, 80, 109, 38, 11, 42);
    107     /* Navegantes: mar alrededor, isla de 19 y dos de 3, puertos en la orilla principal */
    108     const Topo *t = topo_of(BOARD_SEA1);
    109     CHECK(t->board == BOARD_SEA1 && t->nhex == 63 && t->nvert == 158 && t->nedge == 220 && t->nregion == 3);
    110     int size[4] = { 0 }, sea = 0;
    111     for (int h = 0; h < t->nhex; h++) {
    112         if (t->region[h] < 0) sea++;
    113         else if (t->region[h] < 4) size[t->region[h]]++;
    114     }
    115     CHECK(size[0] == 19 && size[1] == 3 && size[2] == 3 && sea == 38);
    116     CHECK(t->sea_start >= 0 && t->region[t->sea_start] < 0);
    117     CHECK(t->nport == 9);
    118     for (int i = 0; i < t->nport; i++) {
    119         int e = t->port_edge[i], a = t->edge_hex[e][0], b = t->edge_hex[e][1];
    120         CHECK(a >= 0 && b >= 0 && ((t->region[a] == 0 && t->region[b] < 0) || (t->region[b] == 0 && t->region[a] < 0)));
    121         for (int j = 0; j < i; j++) {
    122             int f = t->port_edge[j];
    123             CHECK(t->edge_v[e][0] != t->edge_v[f][0] && t->edge_v[e][0] != t->edge_v[f][1] &&
    124                   t->edge_v[e][1] != t->edge_v[f][0] && t->edge_v[e][1] != t->edge_v[f][1]);
    125         }
    126     }
    127     int deg = 0;
    128     for (int v = 0; v < t->nvert; v++)
    129         for (int k = 0; k < 3; k++) if (t->vert_adj[v][k] >= 0) deg++;
    130     CHECK(deg == 2 * t->nedge);
    131 }
    132 
    133 static void t_setup_and_distance(void)
    134 {
    135     Game g;
    136     fresh(&g, 3, false);
    137     const Topo *t = topo_of(BOARD_CLASSIC);
    138     CHECK(g.phase == PH_SETUP && g.cur == 0 && g.setup_n == 6);
    139     int v = t->hex_vert[9][0];                       /* un vertice del centro */
    140     CHECK(do_move(&g, mv(M_SETTLE, 0, v, 0, 0)) == E_OK);
    141     CHECK(do_move(&g, mv(M_SETTLE, 0, v, 0, 0)) == E_PHASE);   /* ahora toca ruta */
    142     int bad = -1;
    143     for (int e = 0; e < t->nedge; e++)
    144         if (t->edge_v[e][0] != v && t->edge_v[e][1] != v) { bad = e; break; }
    145     CHECK(do_move(&g, mv(M_ROAD, 0, bad, 0, 0)) == E_PLACE);
    146     CHECK(do_move(&g, mv(M_ROAD, 0, t->vert_edge[v][0], 0, 0)) == E_OK);
    147     CHECK(g.cur == 1);
    148     int adj = t->vert_adj[v][0];
    149     CHECK(do_move(&g, mv(M_SETTLE, 1, adj, 0, 0)) == E_PLACE); /* regla de distancia */
    150     CHECK(do_move(&g, mv(M_SETTLE, 0, 0, 0, 0)) == E_TURN);
    151 }
    152 
    153 static void t_setup_order_and_resources(void)
    154 {
    155     Game g;
    156     fresh(&g, 2, false);
    157     const Topo *t = topo_of(BOARD_CLASSIC);
    158     int order[4] = { 0, 1, 1, 0 };
    159     int spots[4] = { 0, 10, 30, 45 };
    160     for (int i = 0; i < 4; i++) {
    161         CHECK(g.cur == order[i]);
    162         int v = spots[i];
    163         while (!game_can_settle(&g, g.cur, v, true)) v++;
    164         uint8_t before = (uint8_t)hand_size(&g, g.cur);
    165         CHECK(do_move(&g, mv(M_SETTLE, g.cur, v, 0, 0)) == E_OK);
    166         int expect = 0;
    167         if (i >= 2)
    168             for (int k = 0; k < 3; k++) {
    169                 int h = t->vert_hex[v][k];
    170                 if (h >= 0 && g.terrain[h] != T_DESERT) expect++;
    171             }
    172         CHECK(hand_size(&g, order[i]) == before + expect);
    173         int e = -1;
    174         for (int k = 0; k < 3; k++)
    175             if (t->vert_edge[v][k] >= 0 && g.eown[t->vert_edge[v][k]] < 0) { e = t->vert_edge[v][k]; break; }
    176         CHECK(do_move(&g, mv(M_ROAD, g.cur, e, 0, 0)) == E_OK);
    177     }
    178     CHECK(g.phase == PH_ROLL && g.cur == 0);
    179     CHECK(res_conserved(&g));
    180 }
    181 
    182 /* humano (asiento 0) primero contra 3 bots: 0 0 1 2 3 3 2 1; cada uno cobra con su segundo pueblo */
    183 static void t_setup_first(void)
    184 {
    185     Game g;
    186     Opts o;
    187     opts_default(&o, 4, false);
    188     o.beginner = 1;
    189     o.setup_first = 1;
    190     board_beginner(&g);
    191     game_init(&g, &o);
    192     const Topo *t = topo_of(BOARD_CLASSIC);
    193     int order[8] = { 0, 0, 1, 2, 3, 3, 2, 1 };
    194     CHECK(g.setup_n == 8);
    195     for (int i = 0; i < 8; i++) {
    196         CHECK(g.cur == order[i]);
    197         bool second = false;
    198         for (int k = 0; k < i; k++) second |= order[k] == order[i];
    199         int v = 0;
    200         while (!game_can_settle(&g, g.cur, v, true)) v++;
    201         int before = hand_size(&g, g.cur);
    202         CHECK(do_move(&g, mv(M_SETTLE, g.cur, v, 0, 0)) == E_OK);
    203         CHECK((hand_size(&g, order[i]) > before) == second);
    204         int e = -1;
    205         for (int k = 0; k < 3; k++)
    206             if (t->vert_edge[v][k] >= 0 && g.eown[t->vert_edge[v][k]] < 0) { e = t->vert_edge[v][k]; break; }
    207         CHECK(do_move(&g, mv(M_ROAD, g.cur, e, 0, 0)) == E_OK);
    208     }
    209     CHECK(g.phase == PH_ROLL && g.cur == 0);
    210     /* la cabecera conserva la opcion */
    211     char buf[400];
    212     Game h;
    213     hdr_fmt(&g, buf, sizeof buf);
    214     CHECK(hdr_parse(buf, &h) && h.o.setup_first == 1 && h.setup_order[1] == 0);
    215 }
    216 
    217 static void t_production(void)
    218 {
    219     Game g;
    220     fresh(&g, 2, false);
    221     int h = 4;                                   /* cerro con 6 en el tablero de principiante */
    222     CHECK(g.terrain[h] == T_HILLS && g.num[h] == 6);
    223     const Topo *t = topo_of(BOARD_CLASSIC);
    224     place(&g, 0, t->hex_vert[h][0], 2);          /* ciudad */
    225     place(&g, 1, t->hex_vert[h][3], 1);          /* pueblo */
    226     g.phase = PH_ROLL; g.cur = 0;
    227     Move m = mv(M_ROLL, 0, 0, 0, 0);
    228     m.r[0] = 3; m.r[1] = 3;
    229     CHECK(do_move(&g, m) == E_OK);
    230     CHECK(g.p[0].res[BRICK] >= 2 && g.p[1].res[BRICK] >= 1);
    231     int b0 = g.p[0].res[BRICK];
    232     /* con el ladron encima no produce */
    233     g.robber = (uint8_t)h; g.phase = PH_ROLL;
    234     CHECK(do_move(&g, m) == E_OK);
    235     CHECK(g.p[0].res[BRICK] == b0);
    236     CHECK(res_conserved(&g));
    237 }
    238 
    239 static void t_seven_discard_robber(void)
    240 {
    241     Game g;
    242     fresh(&g, 3, false);
    243     const Topo *t = topo_of(BOARD_CLASSIC);
    244     g.phase = PH_ROLL; g.cur = 0;
    245     g.p[1].res[WOOL] = 9; g.bank[WOOL] -= 9;
    246     g.p[2].res[ORE] = 7; g.bank[ORE] -= 7;
    247     int h = 0;
    248     place(&g, 2, t->hex_vert[h][0], 1);
    249     Move r = mv(M_ROLL, 0, 0, 0, 0);
    250     r.r[0] = 3; r.r[1] = 4;
    251     CHECK(do_move(&g, r) == E_OK);
    252     CHECK(g.phase == PH_DISCARD && g.p[1].discard == 4 && g.p[2].discard == 0);
    253     CHECK(game_needs(&g, 1) && !game_needs(&g, 2) && !game_needs(&g, 0));
    254     Move d = mv(M_DISCARD, 1, 0, 0, 0);
    255     d.r[WOOL] = 3;
    256     CHECK(do_move(&g, d) == E_ARGS);
    257     d.r[WOOL] = 4;
    258     CHECK(do_move(&g, d) == E_OK);
    259     CHECK(g.phase == PH_ROBBER && g.p[1].res[WOOL] == 5);
    260     CHECK(do_move(&g, mv(M_ROBBER, 0, h, -1, -1)) == E_ARGS);   /* hay a quien robar */
    261     CHECK(do_move(&g, mv(M_ROBBER, 0, g.robber, 2, ORE)) == E_ROBBER);
    262     CHECK(do_move(&g, mv(M_ROBBER, 0, h, 2, ORE)) == E_OK);
    263     CHECK(g.p[0].res[ORE] == 1 && g.p[2].res[ORE] == 6 && g.phase == PH_MAIN);
    264     CHECK(res_conserved(&g));
    265 }
    266 
    267 static void t_build_costs(void)
    268 {
    269     Game g;
    270     fresh(&g, 2, false);
    271     const Topo *t = topo_of(BOARD_CLASSIC);
    272     int v = 20;
    273     place(&g, 0, v, 1);
    274     int e = t->vert_edge[v][0];
    275     to_main(&g, 0);
    276     CHECK(do_move(&g, mv(M_ROAD, 0, e, 0, 0)) == E_RES);
    277     g.p[0].res[BRICK] = 1; g.p[0].res[LUMBER] = 1; g.bank[BRICK]--; g.bank[LUMBER]--;
    278     CHECK(do_move(&g, mv(M_ROAD, 0, e, 0, 0)) == E_OK);
    279     CHECK(g.p[0].roads == MAX_ROADS - 1 && hand_size(&g, 0) == 0);
    280     g.p[0].res[GRAIN] = 2; g.p[0].res[ORE] = 3; g.bank[GRAIN] -= 2; g.bank[ORE] -= 3;
    281     CHECK(do_move(&g, mv(M_CITY, 0, v + 1, 0, 0)) == E_PLACE);
    282     CHECK(do_move(&g, mv(M_CITY, 0, v, 0, 0)) == E_OK);
    283     CHECK(g.vlev[v] == 2 && g.p[0].settles == MAX_SETTLES && g.p[0].cities == MAX_CITIES - 1);
    284     CHECK(game_vp(&g, 0) == 2);
    285     CHECK(res_conserved(&g));
    286 }
    287 
    288 static void t_bank_ratios(void)
    289 {
    290     Game g;
    291     fresh(&g, 2, false);
    292     const Topo *t = topo_of(BOARD_CLASSIC);
    293     to_main(&g, 0);
    294     g.p[0].res[WOOL] = 4; g.bank[WOOL] -= 4;
    295     CHECK(game_bank_ratio(&g, 0, WOOL) == 4);
    296     Move m = mv(M_BANK, 0, WOOL, ORE, 1);
    297     CHECK(do_move(&g, m) == E_OK);
    298     CHECK(g.p[0].res[WOOL] == 0 && g.p[0].res[ORE] == 1);
    299     /* puerto 3:1 y 2:1 */
    300     int any = -1, wool = -1;
    301     for (int i = 0; i < t->nport; i++) {
    302         if (g.port[i] == PORT_ANY && any < 0) any = i;
    303         if (g.port[i] == WOOL) wool = i;
    304     }
    305     place(&g, 0, t->edge_v[t->port_edge[any]][0], 1);
    306     CHECK(game_bank_ratio(&g, 0, BRICK) == 3);
    307     CHECK(game_bank_ratio(&g, 0, WOOL) == 3);
    308     place(&g, 0, t->edge_v[t->port_edge[wool]][1], 1);
    309     CHECK(game_bank_ratio(&g, 0, WOOL) == 2);
    310     CHECK(game_bank_ratio(&g, 0, BRICK) == 3);
    311     g.p[0].res[WOOL] = 2; g.bank[WOOL] -= 2;
    312     CHECK(do_move(&g, mv(M_BANK, 0, WOOL, GRAIN, 1)) == E_OK);
    313     CHECK(g.p[0].res[GRAIN] == 1);
    314     CHECK(res_conserved(&g));
    315 }
    316 
    317 static void t_longest_road(void)
    318 {
    319     Game g;
    320     fresh(&g, 2, false);
    321     const Topo *t = topo_of(BOARD_CLASSIC);
    322     /* camino a lo largo de la costa: 6 aristas seguidas */
    323     int path[7];
    324     int e0 = t->coast[0];
    325     int v = t->edge_v[e0][0];
    326     for (int i = 0; i < 6; i++) {
    327         int e = t->coast[i];
    328         road(&g, 0, e);
    329         path[i] = v;
    330         v = t->edge_v[e][0] == v ? t->edge_v[e][1] : t->edge_v[e][0];
    331     }
    332     path[6] = v;
    333     CHECK(game_road_len(&g, 0) == 6);
    334     /* forzar recalculo con una movida real */
    335     place(&g, 0, path[0], 1);
    336     to_main(&g, 0);
    337     g.p[0].res[BRICK] = 1; g.p[0].res[LUMBER] = 1; g.bank[BRICK]--; g.bank[LUMBER]--;
    338     int ext = -1;
    339     for (int k = 0; k < 3; k++) {
    340         int e = t->vert_edge[path[6]][k];
    341         if (e >= 0 && g.eown[e] < 0) { ext = e; break; }
    342     }
    343     CHECK(do_move(&g, mv(M_ROAD, 0, ext, 0, 0)) == E_OK);
    344     CHECK(g.longest == 0 && g.p[0].road_len == 7);
    345     CHECK(game_vp(&g, 0) == 3);
    346     /* un pueblo rival en el medio corta el camino: 3 y 4 -> nadie */
    347     g.cur = 1;
    348     g.p[1].res[BRICK] = 1; g.p[1].res[LUMBER] = 1; g.p[1].res[WOOL] = 1; g.p[1].res[GRAIN] = 1;
    349     g.bank[BRICK]--; g.bank[LUMBER]--; g.bank[WOOL]--; g.bank[GRAIN]--;
    350     int mid = path[3];
    351     int spur = -1;
    352     for (int k = 0; k < 3; k++) {
    353         int e = t->vert_edge[mid][k];
    354         if (e >= 0 && g.eown[e] < 0) spur = e;
    355     }
    356     if (spur >= 0) {
    357         road(&g, 1, spur);
    358         g.vown[path[2]] = -1; /* por las dudas */
    359         CHECK(do_move(&g, mv(M_SETTLE, 1, mid, 0, 0)) == E_OK || true);
    360         if (g.vown[mid] == 1) {
    361             CHECK(g.p[0].road_len == 4);
    362             CHECK(g.longest == -1);
    363         }
    364     }
    365 }
    366 
    367 static void t_dev_cards_and_army(void)
    368 {
    369     Game g;
    370     fresh(&g, 2, false);
    371     to_main(&g, 0);
    372     g.p[0].res[WOOL] = 1; g.p[0].res[GRAIN] = 1; g.p[0].res[ORE] = 1;
    373     g.bank[WOOL]--; g.bank[GRAIN]--; g.bank[ORE]--;
    374     Move b = mv(M_BUYDEV, 0, D_KNIGHT, 0, 0);
    375     CHECK(do_move(&g, b) == E_OK);
    376     CHECK(g.p[0].dev_new[D_KNIGHT] == 1 && g.deck_n == 24);
    377     CHECK(do_move(&g, mv(M_KNIGHT, 0, 0, 0, 0)) == E_NODEV);   /* recien comprada */
    378     CHECK(do_move(&g, mv(M_END, 0, 0, 0, 0)) == E_OK);
    379     CHECK(g.cur == 1 && g.p[0].dev[D_KNIGHT] == 1);
    380     /* ejercito: 3 caballeros */
    381     g.p[0].dev[D_KNIGHT] = 3;
    382     for (int i = 0; i < 3; i++) {
    383         g.cur = 0; g.phase = PH_ROLL; g.rolled = 0; g.dev_played = 0;
    384         CHECK(do_move(&g, mv(M_KNIGHT, 0, 0, 0, 0)) == E_OK);
    385         CHECK(g.phase == PH_ROBBER);
    386         int h = g.robber == 0 ? 1 : 0;
    387         CHECK(do_move(&g, mv(M_ROBBER, 0, h, -1, -1)) == E_OK);
    388         CHECK(g.phase == PH_ROLL);
    389         CHECK(do_move(&g, mv(M_KNIGHT, 0, 0, 0, 0)) == (i < 2 ? E_DEVPLAYED : E_DEVPLAYED));
    390     }
    391     CHECK(g.army == 0 && g.p[0].knights == 3);
    392     g.p[1].knights = 3;
    393     g.p[1].dev[D_KNIGHT] = 1;
    394     g.cur = 1; g.phase = PH_ROLL; g.dev_played = 0;
    395     CHECK(do_move(&g, mv(M_KNIGHT, 1, 0, 0, 0)) == E_OK);
    396     CHECK(g.army == 1);
    397     /* monopolio */
    398     to_main(&g, 0); g.dev_played = 0;
    399     g.p[0].dev[D_MONO] = 1;
    400     g.p[1].res[WOOL] = 3; g.bank[WOOL] -= 3;
    401     CHECK(do_move(&g, mv(M_MONO, 0, WOOL, 0, 0)) == E_OK);
    402     CHECK(g.p[0].res[WOOL] == 3 && g.p[1].res[WOOL] == 0);
    403     CHECK(res_conserved(&g));
    404 }
    405 
    406 static void t_offer(void)
    407 {
    408     Game g;
    409     fresh(&g, 3, false);
    410     to_main(&g, 0);
    411     g.p[0].res[BRICK] = 2; g.bank[BRICK] -= 2;
    412     g.p[2].res[ORE] = 1; g.bank[ORE]--;
    413     Move o = mv(M_OFFER, 0, -1, 0, 0);
    414     o.r[BRICK] = 1; o.r2[ORE] = 1;
    415     CHECK(do_move(&g, o) == E_OK);
    416     CHECK(game_needs(&g, 1) && game_needs(&g, 2));
    417     CHECK(do_move(&g, mv(M_ACCEPT, 1, 0, 0, 0)) == E_RES);   /* 1 no tiene mineral */
    418     CHECK(do_move(&g, mv(M_REJECT, 1, 0, 0, 0)) == E_OK);
    419     CHECK(do_move(&g, mv(M_CONFIRM, 0, 2, 0, 0)) == E_OFFER);
    420     CHECK(do_move(&g, mv(M_ACCEPT, 2, 0, 0, 0)) == E_OK);
    421     CHECK(do_move(&g, mv(M_CONFIRM, 0, 2, 0, 0)) == E_OK);
    422     CHECK(g.p[0].res[ORE] == 1 && g.p[2].res[BRICK] == 1 && !g.offer.active);
    423     /* oferta solo al asiento 1: el 2 no puede aceptar ni tiene que responder */
    424     Move o2 = mv(M_OFFER, 0, 1 << 1, 0, 0);
    425     o2.r[BRICK] = 1; o2.r2[WOOL] = 1;
    426     CHECK(do_move(&g, o2) == E_OK);
    427     CHECK(game_needs(&g, 1) && !game_needs(&g, 2));
    428     CHECK(do_move(&g, mv(M_ACCEPT, 2, 0, 0, 0)) == E_TURN);
    429     Move bad = mv(M_OFFER, 0, 1 << 0, 0, 0);
    430     bad.r[BRICK] = 1; bad.r2[WOOL] = 1;
    431     CHECK(game_check(&g, &bad) == E_ARGS);          /* a uno mismo no */
    432     CHECK(res_conserved(&g));
    433 }
    434 
    435 /* contraofertas: quien responde propone otros terminos y el que ofrecio elige */
    436 static int n_counter, n_confirm, n_offer;
    437 static void count_trades(void *ctx, const Move *m)
    438 {
    439     (void)ctx;
    440     if (m->type == M_COUNTER) n_counter++;
    441     if (m->type == M_CONFIRM) n_confirm++;
    442     if (m->type == M_OFFER) n_offer++;
    443 }
    444 
    445 static void t_counter(void)
    446 {
    447     Game g;
    448     fresh(&g, 3, false);
    449     to_main(&g, 0);
    450     g.p[0].res[BRICK] = 2; g.bank[BRICK] -= 2;
    451     g.p[1].res[WOOL] = 2; g.bank[WOOL] -= 2;
    452     g.p[2].res[ORE] = 1; g.bank[ORE]--;
    453     Move o = mv(M_OFFER, 0, -1, 0, 0);
    454     o.r[BRICK] = 1; o.r2[ORE] = 1;
    455     CHECK(do_move(&g, o) == E_OK);
    456     Move k = mv(M_COUNTER, 1, 0, 0, 0);       /* 1: te doy 2 lanas por 2 ladrillos */
    457     k.r[WOOL] = 2; k.r2[BRICK] = 2;
    458     Move bad = k;
    459     bad.r[WOOL] = 3;
    460     CHECK(game_check(&g, &bad) == E_RES);      /* no tiene 3 lanas */
    461     bad = k; bad.r2[WOOL] = 1;
    462     CHECK(game_check(&g, &bad) == E_ARGS);     /* dar y pedir lo mismo no */
    463     bad = k; bad.p = 0;
    464     CHECK(game_check(&g, &bad) == E_TURN);     /* el que ofrecio no contraoferta */
    465     CHECK(do_move(&g, k) == E_OK);
    466     CHECK(g.offer.resp[1] == 2 && !game_needs(&g, 1) && game_needs(&g, 2));
    467     char buf[96];
    468     Move back;
    469     CHECK(move_fmt(&k, buf, sizeof buf) > 0 && move_parse(buf, &back) && !memcmp(back.r, k.r, NRES) && !memcmp(back.r2, k.r2, NRES));
    470     CHECK(do_move(&g, mv(M_REJECT, 2, 0, 0, 0)) == E_OK);
    471     CHECK(do_move(&g, mv(M_CONFIRM, 0, 1, 0, 0)) == E_OK);     /* con los terminos de 1 */
    472     CHECK(g.p[0].res[BRICK] == 0 && g.p[0].res[WOOL] == 2 && g.p[1].res[BRICK] == 2 && g.p[1].res[WOOL] == 0);
    473     CHECK(!g.offer.active && res_conserved(&g));
    474     /* una oferta nueva borra las contraofertas viejas */
    475     Move o2 = mv(M_OFFER, 0, -1, 0, 0);
    476     o2.r[WOOL] = 1; o2.r2[ORE] = 1;
    477     CHECK(do_move(&g, o2) == E_OK && g.offer.resp[1] == 0 && g.offers == 2);
    478     CHECK(do_move(&g, mv(M_CONFIRM, 0, 1, 0, 0)) == E_OFFER);
    479     /* los bots ofrecen, contraofertan y cierran tratos */
    480     static Auth a;
    481     n_counter = n_confirm = n_offer = 0;
    482     for (int seed = 1; seed <= 20; seed++) {
    483         Opts op;
    484         opts_default(&op, 4, false);
    485         auth_new(&a, &op, (uint64_t)seed, count_trades, 0);
    486         Rng br;
    487         rng_seed(&br, (uint64_t)seed, 9);
    488         for (int step = 0; step < 20000 && a.g.phase != PH_OVER; step++) {
    489             bool acted = false;
    490             for (int s = 0; s < 4 && !acted; s++) {
    491                 Move m;
    492                 if (bot_decide(&a.g, s, &br, &m) && auth_submit(&a, &m) == E_OK) acted = true;
    493             }
    494             if (!acted) break;
    495         }
    496     }
    497     CHECK(n_offer > 0 && n_counter > 0 && n_confirm > 0);
    498 }
    499 
    500 /* el registro no cuenta que carta se robo salvo al ladron y a la victima */
    501 static void t_move_public(void)
    502 {
    503     Move m = mv(M_ROBBER, 1, 5, 2, GRAIN), o;
    504     move_public(&m, 1u << 0, &o);
    505     CHECK(o.c == -1 && o.b == 2 && o.a == 5);          /* el 0 no participa */
    506     move_public(&m, 1u << 1, &o);
    507     CHECK(o.c == GRAIN);                               /* el ladron */
    508     move_public(&m, 1u << 2, &o);
    509     CHECK(o.c == GRAIN);                               /* la victima */
    510     move_public(&m, 0, &o);
    511     CHECK(o.c == -1);
    512     static Game g;
    513     fresh(&g, 3, false);
    514     static const char *nm[MAXP] = { "Ana", "Pepe", "Caro", "D" };
    515     char t[160];
    516     for (int v = 1; v <= 2; v++) {                           /* el ladron y la victima ven la carta */
    517         move_describe_for(&g, &m, nm, 1u << v, t, sizeof t);
    518         CHECK(strstr(t, "Pepe") == t && strstr(t, res_name(GRAIN)) && strstr(t, "Caro"));
    519     }
    520     move_describe_for(&g, &m, nm, 1u << 0, t, sizeof t);    /* Ana, un tercero */
    521     CHECK(strstr(t, "Caro") && strstr(t, tr(S_STOLE_CARD)) && !strstr(t, res_name(GRAIN)));
    522     Move b = mv(M_BANK, 1, BRICK, ORE, 1);
    523     move_public(&b, 0, &o);
    524     CHECK(!memcmp(&o, &b, sizeof o));                 /* lo demas es publico */
    525 }
    526 
    527 /* 5-6: tablero grande, banco 24, mazo 34, y "jugadores en pareja" (asiento +3) */
    528 static void t_paired(void)
    529 {
    530     static Game g;
    531     Opts o;
    532     opts_default(&o, 5, false);
    533     Rng r;
    534     rng_seed(&r, 5, 5);
    535     board_random(&g, BOARD_LARGE, &r);
    536     game_init(&g, &o);
    537     const Topo *t = game_topo(&g);
    538     CHECK(g.o.board == BOARD_LARGE && t->nhex == 30 && g.bank[ORE] == 24 && g.deck_n == 34);
    539     int deserts = 0, nums = 0, red = 0;
    540     for (int h = 0; h < t->nhex; h++) {
    541         if (g.terrain[h] == T_DESERT) deserts++;
    542         else nums++;
    543         if (g.num[h] == 6 || g.num[h] == 8)
    544             for (int d = 0; d < 6; d++) {
    545                 int n = t->hex_adj[h][d];
    546                 if (n >= 0 && (g.num[n] == 6 || g.num[n] == 8)) red++;
    547             }
    548     }
    549     CHECK(deserts == 2 && nums == 28 && red == 0);
    550     CHECK(game_pair(&g, 0) == 3 && game_pair(&g, 4) == 2);
    551     to_main(&g, 0);
    552     CHECK(do_move(&g, mv(M_END, 0, 0, 0, 0)) == E_OK);          /* la pareja (3) no tiene nada: se saltea */
    553     CHECK(g.phase == PH_ROLL && g.cur == 1);
    554     to_main(&g, 1);
    555     g.p[4].res[ORE] = 4; g.bank[ORE] -= 4;
    556     CHECK(do_move(&g, mv(M_END, 1, 0, 0, 0)) == E_OK);
    557     CHECK(g.phase == PH_PAIRED && g.cur == 4 && g.owner == 1);   /* la pareja de 1 es 4 */
    558     Move off = mv(M_OFFER, 4, -1, 0, 0);
    559     off.r[ORE] = 1; off.r2[WOOL] = 1;
    560     CHECK(game_check(&g, &off) == E_PHASE);                     /* en pareja no hay trueques */
    561     CHECK(do_move(&g, mv(M_BANK, 4, ORE, GRAIN, 1)) == E_OK);   /* con el banco si */
    562     CHECK(do_move(&g, mv(M_END, 4, 0, 0, 0)) == E_OK);
    563     CHECK(g.phase == PH_ROLL && g.cur == 2 && res_conserved(&g));   /* sigue el que seguia al dueƱo */
    564     char hdr[600];
    565     static Game h;
    566     CHECK(hdr_fmt(&g, hdr, sizeof hdr) > 0 && hdr_parse(hdr, &h));
    567     CHECK(!memcmp(h.terrain, g.terrain, NHEX) && !memcmp(h.num, g.num, NHEX) && !memcmp(h.port, g.port, NPORT));
    568 }
    569 
    570 /* Los 25 mapas: limites, islas, puertos, reparto; cabecera con mapa; semilla del tablero */
    571 static void t_maps(void)
    572 {
    573     static const int PORTS[BF__COUNT] = { 9, 11, 9, 8, 8 };
    574     for (int b = 0; b < BOARD__COUNT; b++) {
    575         const Topo *t = topo_of(b);
    576         int fam = b / NMAPS;
    577         CHECK(t->board == b && t->family == fam && board_name(b)[0]);
    578         CHECK(t->nvert <= NVERT && t->nedge <= NEDGE && t->nhex <= NHEX && t->nregion <= 8 && t->home);
    579         CHECK(t->sea == (fam >= BF_SEA1));
    580         if (fam == BF_CLASSIC) CHECK(t->nhex == 19);
    581         if (fam == BF_LARGE) CHECK(t->nhex == 30);
    582         CHECK(t->nport == PORTS[fam]);
    583         for (int v = 0; v < t->nvert; v++) {
    584             int n = 0;
    585             for (int k = 0; k < 3; k++) n += t->vert_adj[v][k] >= 0;
    586             CHECK(n >= 2);
    587         }
    588         for (int i = 0; i < t->nport; i++)          /* puertos en costa de tierra, sin vertices compartidos */
    589             for (int j = i + 1; j < t->nport; j++)
    590                 for (int a = 0; a < 2; a++) for (int c = 0; c < 2; c++)
    591                     CHECK(t->edge_v[t->port_edge[i]][a] != t->edge_v[t->port_edge[j]][c]);
    592         if (t->sea) CHECK(t->sea_start >= 0 && t->region[t->sea_start] < 0);
    593         static Game g;
    594         for (int seed = 1; seed <= 20; seed++) {
    595             Rng r;
    596             rng_seed(&r, (uint64_t)seed, 5);
    597             memset(&g, 0, sizeof g);
    598             board_random(&g, b, &r);
    599             int des = 0, gold = 0, bad = 0;
    600             for (int h = 0; h < t->nhex; h++) {
    601                 int ter = g.terrain[h];
    602                 if (ter == T_DESERT) des++;
    603                 if (ter == T_GOLD) gold++;
    604                 if ((ter == T_SEA) != (t->region[h] < 0)) bad++;
    605                 if ((ter_res(ter) || ter == T_GOLD) != (g.num[h] != 0)) bad++;
    606                 if (g.num[h] == 6 || g.num[h] == 8)
    607                     for (int d = 0; d < 6; d++) {
    608                         int n = t->hex_adj[h][d];
    609                         if (n >= 0 && (g.num[n] == 6 || g.num[n] == 8)) bad++;
    610                     }
    611             }
    612             CHECK(des >= 1 && !bad);
    613             if (fam >= BF_SEA1) CHECK(gold >= 1 || fam == BF_SEA2);
    614         }
    615     }
    616     /* cabecera con mapa y escenario, ida y vuelta */
    617     static Game g, h;
    618     Opts o;
    619     opts_default(&o, 4, false);
    620     opts_scenario(&o, SCEN_SEA2);
    621     o.map = 3;
    622     o.bseed = 12345;
    623     Rng r;
    624     rng_seed(&r, 7, 7);
    625     board_make(&g, &o, &r);
    626     CHECK(g.o.board == BOARD_SEA1 + NMAPS + 3 && game_has_sea(&g));
    627     char buf[1024];
    628     hdr_fmt(&g, buf, sizeof buf);
    629     CHECK(buf[0] == 'M' && hdr_parse(buf, &h));
    630     CHECK(h.o.board == g.o.board && h.o.map == 3 && h.o.scenario == SCEN_SEA2 && !memcmp(h.terrain, g.terrain, NHEX) &&
    631           !memcmp(h.num, g.num, NHEX) && !memcmp(h.port, g.port, NPORT));
    632     /* la semilla del tablero manda: con otra semilla de partida, el mismo tablero */
    633     rng_seed(&r, 999, 1);
    634     board_make(&h, &o, &r);
    635     CHECK(!memcmp(h.terrain, g.terrain, NHEX) && !memcmp(h.num, g.num, NHEX) && !memcmp(h.port, g.port, NPORT));
    636     static Auth a;
    637     auth_new(&a, &o, 4242, 0, 0);
    638     CHECK(!memcmp(a.g.terrain, g.terrain, NHEX) && !memcmp(a.g.num, g.num, NHEX));
    639     /* Cuatro islas: se arranca en cualquier isla grande, no en el islote del desierto; la otra isla da +2 */
    640     const Topo *t = game_topo(&g);
    641     int va = -1, vb = -1, vd = -1, ra = -1;
    642     for (int v = 0; v < t->nvert; v++) {
    643         int reg = -1;
    644         bool desert = false;
    645         for (int k = 0; k < 3; k++) {
    646             int hx = t->vert_hex[v][k];
    647             if (hx >= 0 && t->region[hx] >= 0) { reg = t->region[hx]; if (t->kind[hx] == HK_DESERT) desert = true; }
    648         }
    649         if (desert && vd < 0) vd = v;
    650         if (reg < 0 || desert || !game_can_settle(&g, 0, v, true)) continue;
    651         if (va < 0) { va = v; ra = reg; }
    652         else if (vb < 0 && reg != ra) {
    653             bool far = true;
    654             for (int k = 0; k < 3; k++) if (t->vert_adj[v][k] >= 0 && g.vown[t->vert_adj[v][k]] >= 0) far = false;
    655             if (far) vb = v;
    656         }
    657     }
    658     CHECK(va >= 0 && vb >= 0 && vd >= 0);
    659     CHECK(!game_can_settle(&g, 0, vd, true));
    660     if (va >= 0 && vb >= 0) {
    661         CHECK(do_move(&g, mv(M_SETTLE, 0, va, 0, 0)) == E_OK && g.p[0].bonus == 0);
    662         to_main(&g, 0);
    663         for (int k = 0; k < NRES; k++) { g.p[0].res[k] = 3; g.bank[k] -= 3; }
    664         int e = t->vert_edge[vb][0];
    665         g.eown[e] = 0;                               /* un camino que llega */
    666         CHECK(do_move(&g, mv(M_SETTLE, 0, vb, 0, 0)) == E_OK && g.p[0].bonus == 2);
    667     }
    668 }
    669 
    670 /* Navegantes: barcos, pirata, oro, islas nuevas */
    671 static int n_ship, n_gold, n_pirate, n_bonus;
    672 static const Game *sea_game;
    673 static void count_sea(void *ctx, const Move *m)
    674 {
    675     (void)ctx;
    676     if (m->type == M_SHIP) n_ship++;
    677     if (m->type == M_GOLD) n_gold++;
    678     if (m->type == M_ROBBER && sea_game && !ter_land(sea_game->terrain[m->a])) n_pirate++;
    679 }
    680 
    681 static int edge_kind(const Game *g, int e)      /* 1 tierra-tierra, 2 costa, 3 mar-mar */
    682 {
    683     const Topo *t = game_topo(g);
    684     int land = 0, sea = 0;
    685     for (int s = 0; s < 2; s++) {
    686         int h = t->edge_hex[e][s];
    687         if (h < 0) continue;
    688         if (ter_land(g->terrain[h])) land++; else sea++;
    689     }
    690     return !sea ? 1 : !land ? 3 : 2;
    691 }
    692 
    693 static void t_sea(void)
    694 {
    695     static Game g;
    696     Opts o;
    697     opts_default(&o, 3, false);
    698     opts_scenario(&o, SCEN_SEA1);
    699     Rng r;
    700     rng_seed(&r, 9, 9);
    701     board_random(&g, BOARD_SEA1, &r);
    702     game_init(&g, &o);
    703     const Topo *t = game_topo(&g);
    704     CHECK(g.o.board == BOARD_SEA1 && g.o.vp_target == 13 && g.p[0].ships == MAX_SHIPS);
    705     CHECK(g.pirate < t->nhex && !ter_land(g.terrain[g.pirate]) && ter_land(g.terrain[g.robber]));
    706     int gold = 0;
    707     for (int h = 0; h < t->nhex; h++) if (g.terrain[h] == T_GOLD) gold++;
    708     CHECK(gold == 2);
    709     /* un vertice de la isla grande con un solo hexagono de tierra: tiene una arista de mar */
    710     int v = -1, es = -1, ec = -1;
    711     for (int u = 0; u < t->nvert && v < 0; u++) {
    712         int land = 0, main = 0;
    713         for (int k = 0; k < 3; k++) {
    714             int h = t->vert_hex[u][k];
    715             if (h >= 0 && ter_land(g.terrain[h])) { land++; if (t->region[h] == 0) main++; }
    716         }
    717         if (land != 1 || main != 1) continue;
    718         int a = -1, b = -1;
    719         for (int k = 0; k < 3; k++) {
    720             int e = t->vert_edge[u][k];
    721             if (e >= 0 && edge_kind(&g, e) == 3) a = e;
    722             if (e >= 0 && edge_kind(&g, e) == 2) b = e;
    723         }
    724         bool far = true;                       /* que el pirata no moleste */
    725         for (int k = 0; k < 3; k++) {
    726             int e = t->vert_edge[u][k];
    727             if (e >= 0 && (t->edge_hex[e][0] == g.pirate || t->edge_hex[e][1] == g.pirate)) far = false;
    728         }
    729         if (a >= 0 && b >= 0 && far) { v = u; es = a; ec = b; }
    730     }
    731     CHECK(v >= 0);
    732     if (v < 0) return;
    733     CHECK(game_can_settle(&g, 0, v, true));              /* el setup va en la isla grande */
    734     place(&g, 0, v, 1);
    735     to_main(&g, 0);
    736     for (int k = 0; k < NRES; k++) { g.p[0].res[k] = 6; g.bank[k] -= 6; }
    737     CHECK(!game_can_road(&g, 0, es) && game_can_ship(&g, 0, es, false));   /* agua: solo barco */
    738     CHECK(game_can_road(&g, 0, ec) && game_can_ship(&g, 0, ec, false));     /* costa: las dos */
    739     CHECK(do_move(&g, mv(M_SHIP, 0, es, 0, 0)) == E_OK && g.ship[es] && g.p[0].ships == MAX_SHIPS - 1);
    740     CHECK(g.p[0].res[LUMBER] == 5 && g.p[0].res[WOOL] == 5);
    741     CHECK(!game_ship_movable(&g, 0, es));                 /* recien puesto */
    742     /* del otro lado del barco sigue otro barco, no una ruta */
    743     int w = t->edge_v[es][0] == v ? t->edge_v[es][1] : t->edge_v[es][0], next = -1;
    744     for (int k = 0; k < 3; k++) {
    745         int e = t->vert_edge[w][k];
    746         if (e >= 0 && e != es && g.eown[e] < 0) {
    747             CHECK(!game_can_road(&g, 0, e));
    748             if (game_can_ship(&g, 0, e, false)) next = e;
    749         }
    750     }
    751     CHECK(next >= 0);
    752     /* el pirata al lado bloquea */
    753     int ph = t->edge_hex[next][0] >= 0 && !ter_land(g.terrain[t->edge_hex[next][0]]) ? t->edge_hex[next][0] : t->edge_hex[next][1];
    754     int keep = g.pirate;
    755     g.pirate = (uint8_t)ph;
    756     CHECK(!game_can_ship(&g, 0, next, false));
    757     g.pirate = (uint8_t)keep;
    758     /* mover: en otro turno, una vez */
    759     memset(g.ship_new, 0, sizeof g.ship_new);
    760     CHECK(game_ship_movable(&g, 0, es));
    761     CHECK(do_move(&g, mv(M_MOVESHIP, 0, es, ec, 0)) == E_OK && g.ship[ec] && !g.ship[es] && g.eown[es] < 0);
    762     CHECK(do_move(&g, mv(M_MOVESHIP, 0, ec, es, 0)) == E_PIECES);   /* uno por turno */
    763     /* oro: el que esta al lado elige */
    764     int gh = -1;
    765     for (int h = 0; h < t->nhex && gh < 0; h++) if (g.terrain[h] == T_GOLD && h != g.robber) gh = h;
    766     CHECK(gh >= 0 && g.num[gh] > 1);
    767     int gv = t->hex_vert[gh][0];
    768     place(&g, 1, gv, 1);
    769     g.phase = PH_ROLL; g.rolled = 0; g.cur = 0;
    770     int d1 = g.num[gh] > 7 ? 6 : 1, d2 = g.num[gh] - d1;
    771     Move roll = mv(M_ROLL, 0, 0, 0, 0);
    772     roll.r[0] = (uint8_t)d1; roll.r[1] = (uint8_t)d2;
    773     CHECK(do_move(&g, roll) == E_OK && g.phase == PH_GOLD && g.p[1].gold == 1 && game_needs(&g, 1));
    774     Move gm = mv(M_GOLD, 1, 0, 0, 0);
    775     gm.r[ORE] = 2;
    776     CHECK(do_move(&g, gm) == E_ARGS);                     /* tiene que ser exactamente lo que le toca */
    777     gm.r[ORE] = 1;
    778     int ore = g.p[1].res[ORE];
    779     CHECK(do_move(&g, gm) == E_OK && g.p[1].res[ORE] == ore + 1 && g.phase == PH_MAIN && res_conserved(&g));
    780     /* isla nueva: +2 la primera vez */
    781     int iv = -1, ie = -1;
    782     for (int u = 0; u < t->nvert && iv < 0; u++) {
    783         bool isle = false;
    784         for (int k = 0; k < 3; k++) { int h = t->vert_hex[u][k]; if (h >= 0 && t->region[h] >= 1) isle = true; }
    785         if (!isle || !game_can_settle(&g, 0, u, false) == false) continue;
    786         for (int k = 0; k < 3 && iv < 0; k++) {
    787             int e = t->vert_edge[u][k];
    788             if (e >= 0 && edge_kind(&g, e) >= 2 && g.eown[e] < 0) { iv = u; ie = e; }
    789         }
    790     }
    791     CHECK(iv >= 0);
    792     if (iv < 0) return;
    793     g.eown[ie] = 0; g.ship[ie] = 1;                       /* un barco que llega a la isla */
    794     int vp = game_vp_public(&g, 0);
    795     CHECK(do_move(&g, mv(M_SETTLE, 0, iv, 0, 0)) == E_OK && g.p[0].bonus == 2 && game_vp_public(&g, 0) == vp + 3);
    796     CHECK(res_conserved(&g));
    797     /* los bots usan todo esto */
    798     static Auth a;
    799     n_ship = n_gold = n_pirate = n_bonus = 0;
    800     for (int seed = 1; seed <= 8; seed++) {
    801         Opts so;
    802         opts_default(&so, 4, false);
    803         opts_scenario(&so, SCEN_SEA1);
    804         auth_new(&a, &so, (uint64_t)seed, count_sea, 0);
    805         sea_game = &a.g;
    806         Rng br;
    807         rng_seed(&br, (uint64_t)seed, 4);
    808         for (int step = 0; step < 30000 && a.g.phase != PH_OVER; step++) {
    809             bool acted = false;
    810             for (int s = 0; s < 4 && !acted; s++) {
    811                 Move m;
    812                 if (bot_decide(&a.g, s, &br, &m) && auth_submit(&a, &m) == E_OK) acted = true;
    813             }
    814             if (!acted) break;
    815         }
    816         for (int p = 0; p < 4; p++) if (a.g.p[p].bonus) n_bonus++;
    817         sea_game = 0;
    818     }
    819     CHECK(n_ship > 0 && n_gold > 0 && n_pirate > 0 && n_bonus > 0);
    820     printf("  navegantes (8 partidas de bots): %d barcos, %d oros, %d piratas, %d bonus de isla\n", n_ship, n_gold, n_pirate, n_bonus);
    821 }
    822 
    823 static void t_variant2p(void)
    824 {
    825     Auth a;
    826     Opts o;
    827     opts_default(&o, 2, true);
    828     o.beginner = 1;
    829     auth_new(&a, &o, 42, NULL, NULL);
    830     Game *g = &a.g;
    831     CHECK(g->o.np == 4 && g->p[2].neutral && g->p[3].neutral);
    832     CHECK(g->p[2].settles == MAX_SETTLES - 2 && g->p[3].settles == MAX_SETTLES - 2);
    833     CHECK(g->p[2].roads == MAX_ROADS - 2);
    834     CHECK(g->setup_n == 4 && g->cur == 0);
    835     /* setup de los 2 humanos con bots */
    836     Rng r;
    837     rng_seed(&r, 1, 1);
    838     Move m;
    839     for (int i = 0; i < 40 && g->phase == PH_SETUP; i++) {
    840         CHECK(bot_decide(g, g->cur, &r, &m));
    841         CHECK(auth_submit(&a, &m) == E_OK);
    842     }
    843     CHECK(g->phase == PH_ROLL);
    844     for (int i = 0; i < 30; i++) {
    845         g->phase = PH_ROLL; g->cur = 0;
    846         Move roll = mv(M_ROLL, 0, 0, 0, 0);
    847         CHECK(auth_submit(&a, &roll) == E_OK);
    848         CHECK(roll.r[0] + roll.r[1] != roll.r[2] + roll.r[3]);
    849         CHECK(roll.r[2] >= 1);
    850     }
    851     /* construir una ruta obliga a construir para un neutral */
    852     g->phase = PH_MAIN; g->cur = 0; g->rolled = 1; g->offer.active = 0;
    853     for (int r2 = 0; r2 < NRES; r2++) { g->bank[r2] += g->p[0].res[r2]; g->p[0].res[r2] = 0; }
    854     g->p[0].res[BRICK] = 1; g->p[0].res[LUMBER] = 1; g->bank[BRICK]--; g->bank[LUMBER]--;
    855     int e = -1;
    856     for (int k = 0; k < NEDGE; k++) if (game_can_road(g, 0, k)) { e = k; break; }
    857     Move rd = mv(M_ROAD, 0, e, 0, 0);
    858     CHECK(auth_submit(&a, &rd) == E_OK);
    859     CHECK(g->phase == PH_NEUTRAL && g->neutral_need == NEED_ROAD);
    860     Move end = mv(M_END, 0, 0, 0, 0);
    861     CHECK(auth_submit(&a, &end) == E_PHASE);
    862     CHECK(bot_decide(g, 0, &r, &m) && m.type == M_NEUTRAL);
    863     CHECK(auth_submit(&a, &m) == E_OK);
    864     CHECK(g->phase == PH_MAIN);
    865     /* fichas: comercio forzado */
    866     g->p[0].tokens = 2;
    867     g->p[1].res[ORE] += 3; g->bank[ORE] -= 3;
    868     Move tt = mv(M_TOKTRADE, 0, 0, 0, 0);
    869     int before1 = hand_size(g, 1);
    870     CHECK(auth_submit(&a, &tt) == E_OK);
    871     CHECK(g->phase == PH_GIVEBACK && g->give_back == 2);
    872     CHECK(hand_size(g, 1) == before1 - 2);
    873     Move gb = mv(M_GIVEBACK, 0, 0, 0, 0);
    874     gb.r[tt.a]++; gb.r[tt.b]++;
    875     CHECK(auth_submit(&a, &gb) == E_OK);
    876     CHECK(hand_size(g, 1) == before1 && g->phase == PH_MAIN);
    877     CHECK(res_conserved(g));
    878 }
    879 
    880 static void t_undo(void)
    881 {
    882     static Auth a;
    883     Opts o;
    884     opts_default(&o, 2, true);
    885     o.beginner = 1;
    886     auth_new(&a, &o, 9, NULL, NULL);
    887     Game *g = &a.g;
    888     Rng r;
    889     rng_seed(&r, 2, 2);
    890     Move m;
    891     while (g->phase == PH_SETUP && bot_decide(g, g->cur, &r, &m)) auth_submit(&a, &m);
    892     g->phase = PH_MAIN; g->cur = 0; g->rolled = 1; g->undo_n = 0; a.undo.n = 0;
    893     for (int k = 0; k < NRES; k++) { g->bank[k] -= 3; g->p[0].res[k] += 3; }
    894     g->p[0].res[BRICK] += 2; g->bank[BRICK] -= 2;
    895     static Game before;
    896     before = *g;
    897     Move u = mv(M_UNDO, 0, 0, 0, 0);
    898     CHECK(auth_submit(&a, &u) == E_PHASE);               /* nada para deshacer */
    899     int e = -1;
    900     for (int k = 0; k < NEDGE; k++) if (game_can_road(g, 0, k)) { e = k; break; }
    901     Move rd = mv(M_ROAD, 0, e, 0, 0);
    902     CHECK(auth_submit(&a, &rd) == E_OK);
    903     CHECK(g->phase == PH_NEUTRAL && g->undo_n == 1);
    904     CHECK(bot_decide(g, 0, &r, &m) && auth_submit(&a, &m) == E_OK);
    905     CHECK(g->undo_n == 2 && g->phase == PH_MAIN);
    906     Move bk = mv(M_BANK, 0, BRICK, ORE, 1);
    907     bk.c = 1;
    908     CHECK(auth_submit(&a, &bk) == E_OK && g->undo_n == 3);
    909     u = mv(M_UNDO, 0, 0, 0, 0); CHECK(auth_submit(&a, &u) == E_OK);      /* banco */
    910     u = mv(M_UNDO, 0, 0, 0, 0); CHECK(auth_submit(&a, &u) == E_OK);      /* ruta neutral */
    911     CHECK(g->phase == PH_NEUTRAL);
    912     u = mv(M_UNDO, 0, 0, 0, 0); CHECK(auth_submit(&a, &u) == E_OK);      /* ruta propia */
    913     CHECK(g->phase == PH_MAIN && g->undo_n == 0 && g->eown[e] < 0);
    914     CHECK(!memcmp(g->p, before.p, sizeof g->p) && !memcmp(g->eown, before.eown, sizeof g->eown));
    915     CHECK(res_conserved(g));
    916     /* tirar dados no se puede deshacer: END limpia */
    917     CHECK(auth_submit(&a, &rd) == E_OK);
    918     Move end = mv(M_END, 0, 0, 0, 0);
    919     if (g->phase == PH_NEUTRAL) { CHECK(bot_decide(g, 0, &r, &m) && auth_submit(&a, &m) == E_OK); }
    920     CHECK(auth_submit(&a, &end) == E_OK && g->undo_n == 0 && a.undo.n == 0);
    921 }
    922 
    923 /* la interfaz de la contraoferta: responder con 'o', armar, mandar; y elegirla al cerrar */
    924 static void t_ctrl_counter(void)
    925 {
    926     Game g;
    927     fresh(&g, 3, false);
    928     to_main(&g, 0);
    929     g.p[0].res[BRICK] = 1; g.bank[BRICK]--;
    930     g.p[1].res[WOOL] = 2; g.bank[WOOL] -= 2;
    931     Move o = mv(M_OFFER, 0, -1, 0, 0);
    932     o.r[BRICK] = 1; o.r2[ORE] = 1;
    933     CHECK(do_move(&g, o) == E_OK);
    934     static const char *nm[MAXP] = { "Ana", "Beto", "Caro", "Dani" };
    935     Ctrl c;
    936     ctrl_init(&c, 1 << 1, false);
    937     for (int i = 0; i < MAXP; i++) c.names[i] = nm[i];
    938     ctrl_sync(&c, &g);
    939     CHECK(c.mode == CM_MENU && c.intent == I_RESPOND && c.nitems == 3);
    940     Move m;
    941     CHECK(!ctrl_key(&c, 'o', &m) && c.mode == CM_CARDS && c.intent == I_COUNTER);
    942     CHECK(c.sel2[BRICK] == 1 && c.sel[ORE] == 0);          /* arranca de la oferta al reves (no tiene mineral) */
    943     CHECK(!ctrl_key(&c, '3', &m) && c.sel[WOOL] == 1);       /* doy 1 lana */
    944     CHECK(ctrl_key(&c, K_OK, &m) && m.type == M_COUNTER && m.p == 1 && m.r[WOOL] == 1 && m.r2[BRICK] == 1);
    945     CHECK(do_move(&g, m) == E_OK);
    946     CHECK(do_move(&g, mv(M_REJECT, 2, 0, 0, 0)) == E_OK);
    947     Ctrl d;
    948     ctrl_init(&d, 1 << 0, false);
    949     for (int i = 0; i < MAXP; i++) d.names[i] = nm[i];
    950     ctrl_sync(&d, &g);
    951     int k = -1;
    952     for (int i = 0; i < d.nactions; i++) if (d.actions[i].action == A_CONFIRM && d.actions[i].arg == 1) k = i;
    953     CHECK(k >= 0 && d.actions[k].enabled && strstr(d.actions[k].label, "Beto") != 0);
    954     CHECK(ctrl_pick(&d, PK_ACTION, k, &m) && m.type == M_CONFIRM && m.a == 1);
    955     CHECK(do_move(&g, m) == E_OK && g.p[0].res[WOOL] == 1 && g.p[1].res[BRICK] == 1);
    956 }
    957 
    958 /* el cursor camina por vecinos del tablero; puede pararse donde no se puede construir */
    959 static void t_ctrl_nav(void)
    960 {
    961     static Auth a;
    962     Opts o;
    963     opts_default(&o, 3, false);
    964     o.beginner = 1;
    965     auth_new(&a, &o, 7, NULL, NULL);
    966     static const char *nm[MAXP] = { "A", "B", "C", "D" };
    967     static Ctrl c;
    968     ctrl_init(&c, 1, false);
    969     for (int i = 0; i < MAXP; i++) c.names[i] = nm[i];
    970     ctrl_sync(&c, &a.g);
    971     CHECK(c.mode == CM_VERT && ctrl_cursor_target(&c) >= 0);
    972     const Topo *t = topo_of(BOARD_CLASSIC);
    973     static const int KEYS[] = { K_RIGHT, K_DOWN, K_LEFT, K_UP, K_RIGHT, K_RIGHT, K_UP, K_LEFT };
    974     bool moved = false;
    975     Move m;
    976     for (int r = 0; r < 6; r++)
    977         for (int i = 0; i < 8; i++) {
    978             int before = ctrl_cursor_pos(&c);
    979             ctrl_key(&c, KEYS[i], &m);
    980             int after = ctrl_cursor_pos(&c);
    981             bool adj = after == before;
    982             for (int k = 0; k < 3; k++) if (t->vert_adj[before][k] == after) adj = true;
    983             CHECK(adj);
    984             if (after != before) moved = true;
    985         }
    986     CHECK(moved);
    987     /* forzar una esquina invalida: vecina de un pueblo ya puesto */
    988     a.g.vown[0] = 1; a.g.vlev[0] = 1;
    989     ctrl_sync(&c, &a.g);
    990     c.pos = t->vert_adj[0][0] >= 0 ? t->vert_adj[0][0] : t->vert_adj[0][1];
    991     CHECK(ctrl_cursor_target(&c) < 0);
    992     CHECK(!ctrl_key(&c, K_OK, &m) && c.msg[0]);
    993     ctrl_key(&c, K_TAB, &m);
    994     CHECK(ctrl_cursor_target(&c) >= 0 && ctrl_cursor_target(&c) == ctrl_cursor_pos(&c));
    995     int at = ctrl_cursor_pos(&c);
    996     CHECK(!ctrl_key(&c, K_OK, &m) && c.intent == I_SETUP_R && c.draft_v == at);   /* queda en borrador */
    997 }
    998 
    999 /* colocacion inicial: pueblo y ruta en borrador, u deshace, se manda al confirmar */
   1000 static void t_ctrl_setup(void)
   1001 {
   1002     static Auth a;
   1003     Opts o;
   1004     opts_default(&o, 3, false);
   1005     o.beginner = 1;
   1006     auth_new(&a, &o, 3, NULL, NULL);
   1007     static const char *nm[MAXP] = { "A", "B", "C", "D" };
   1008     static Ctrl c;
   1009     ctrl_init(&c, 1, false);
   1010     for (int i = 0; i < MAXP; i++) c.names[i] = nm[i];
   1011     ctrl_sync(&c, &a.g);
   1012     Move m;
   1013     int v0 = ctrl_cursor_target(&c);
   1014     CHECK(c.intent == I_SETUP_S && v0 >= 0);
   1015     CHECK(!ctrl_key(&c, K_OK, &m) && c.intent == I_SETUP_R && a.g.vown[v0] < 0);   /* nada mandado */
   1016     int dv, de;
   1017     ctrl_draft(&c, &dv, &de);
   1018     CHECK(dv == v0 && de < 0);
   1019     CHECK(!ctrl_key(&c, 'u', &m) && c.intent == I_SETUP_S && c.draft_v < 0);        /* u: otro pueblo */
   1020     ctrl_key(&c, K_TAB, &m);
   1021     int v1 = ctrl_cursor_target(&c);
   1022     CHECK(!ctrl_key(&c, K_OK, &m) && c.intent == I_SETUP_R);
   1023     int e1 = ctrl_cursor_target(&c);
   1024     CHECK(e1 >= 0 && !ctrl_key(&c, K_OK, &m) && c.intent == I_SETUP_OK);
   1025     CHECK(!ctrl_key(&c, 'u', &m) && c.intent == I_SETUP_R && c.draft_v == v1);      /* u: otra ruta */
   1026     CHECK(!ctrl_key(&c, K_OK, &m) && c.intent == I_SETUP_OK);
   1027     ctrl_sync(&c, &a.g);                       /* sincronizar no pierde el borrador */
   1028     CHECK(c.intent == I_SETUP_OK && c.draft_v == v1);
   1029     CHECK(ctrl_key(&c, K_OK, &m) && m.type == M_SETTLE && m.a == v1);
   1030     CHECK(auth_submit(&a, &m) == E_OK);
   1031     CHECK(ctrl_next(&c, &m) && m.type == M_ROAD && auth_submit(&a, &m) == E_OK && !ctrl_next(&c, &m));
   1032     CHECK(a.g.vown[v1] == 0 && a.g.cur != 0);  /* le toca al siguiente */
   1033 }
   1034 
   1035 static void t_ctrl_undo(void)
   1036 {
   1037     static Auth a;
   1038     Opts o;
   1039     opts_default(&o, 3, false);
   1040     o.beginner = 1;
   1041     auth_new(&a, &o, 5, NULL, NULL);
   1042     Game *g = &a.g;
   1043     Rng r;
   1044     rng_seed(&r, 4, 4);
   1045     Move m;
   1046     while (g->phase == PH_SETUP && bot_decide(g, g->cur, &r, &m)) auth_submit(&a, &m);
   1047     g->phase = PH_MAIN; g->cur = 0; g->rolled = 1;
   1048     g->p[0].res[BRICK] += 1; g->p[0].res[LUMBER] += 1; g->bank[BRICK]--; g->bank[LUMBER]--;
   1049     static Ctrl c;
   1050     ctrl_init(&c, 1, false);
   1051     const char *nm[MAXP] = { "A", "B", "C", "D" };
   1052     for (int i = 0; i < MAXP; i++) c.names[i] = nm[i];
   1053     ctrl_sync(&c, g);
   1054     CHECK(!ctrl_key(&c, '1', &m) && c.mode == CM_EDGE);
   1055     CHECK(ctrl_key(&c, K_OK, &m) && m.type == M_ROAD);
   1056     CHECK(auth_submit(&a, &m) == E_OK);
   1057     ctrl_sync(&c, g);
   1058     CHECK(c.mode == CM_IDLE && g->undo_n == 1);
   1059     CHECK(!ctrl_key(&c, 'e', &m) && c.mode == CM_MENU && c.intent == I_CONFIRM_END);
   1060     CHECK(ctrl_key(&c, 'u', &m) && m.type == M_UNDO);
   1061     CHECK(auth_submit(&a, &m) == E_OK);
   1062     ctrl_sync(&c, g);
   1063     CHECK(g->undo_n == 0 && g->p[0].res[BRICK] >= 1);
   1064     CHECK(ctrl_key(&c, 'e', &m) && m.type == M_END);         /* sin nada que deshacer: directo */
   1065     /* trueque con destinatarios */
   1066     g->p[0].res[WOOL] += 1; g->bank[WOOL]--;
   1067     ctrl_sync(&c, g);
   1068     CHECK(!ctrl_key(&c, 't', &m) && c.mode == CM_CARDS);
   1069     ctrl_key(&c, '3', &m);                 /* doy lana */
   1070     ctrl_key(&c, K_TAB, &m);
   1071     ctrl_key(&c, '5', &m);                 /* pido mineral */
   1072     CHECK(!ctrl_key(&c, K_OK, &m) && c.intent == I_OFFER_TO && c.offer_mask == 6);
   1073     CHECK(!ctrl_key(&c, '2', &m) && c.offer_mask == 2);      /* destildo al asiento 2 */
   1074     CHECK(ctrl_key(&c, 'e', &m) && m.type == M_OFFER && m.a == 2 && m.r[WOOL] == 1 && m.r2[ORE] == 1);
   1075     CHECK(auth_submit(&a, &m) == E_OK && g->offer.tomask == 2);
   1076 }
   1077 
   1078 static void t_serialize(void)
   1079 {
   1080     char buf[256];
   1081     for (int t = 1; t < M__COUNT; t++) {
   1082         Move m = mv(t, 2, 17, -1, 3), back;
   1083         for (int i = 0; i < NRES; i++) { m.r[i] = (uint8_t)(i + 1); m.r2[i] = (uint8_t)(5 - i); }
   1084         if (t == M_ROLL) m.r[4] = 0;
   1085         int n = move_fmt(&m, buf, sizeof buf);
   1086         CHECK(n > 0);
   1087         CHECK(move_parse(buf, &back));
   1088         char buf2[256];
   1089         move_fmt(&back, buf2, sizeof buf2);
   1090         CHECK(!strcmp(buf, buf2));
   1091         CHECK(back.type == t && back.p == 2);
   1092     }
   1093     Move x;
   1094     CHECK(!move_parse("0 FOO 1", &x));
   1095     CHECK(!move_parse("9 END", &x));
   1096     CHECK(!move_parse("0 ROAD", &x));
   1097     CHECK(!move_parse("0 END extra", &x));
   1098     Game g, h;
   1099     fresh(&g, 4, false);
   1100     CHECK(hdr_fmt(&g, buf, sizeof buf) > 0);
   1101     CHECK(hdr_parse(buf, &h));
   1102     CHECK(!memcmp(h.terrain, g.terrain, NHEX) && !memcmp(h.num, g.num, NHEX) && h.o.np == 4);
   1103 }
   1104 
   1105 /* Replay: una partida entera de bots, reproducida desde el log, da el mismo estado. */
   1106 typedef struct { char lines[8000][64]; int n; } Log;
   1107 static Log LOG;
   1108 
   1109 static void log_emit(void *ctx, const Move *m)
   1110 {
   1111     Log *l = ctx;
   1112     if (l->n < 8000) move_fmt(m, l->lines[l->n++], 64);
   1113 }
   1114 
   1115 int sim_one(uint64_t seed, int players, bool v2p, bool verbose, Auth *out, EmitFn fn, void *ctx);
   1116 
   1117 static void t_replay(void)
   1118 {
   1119     static Auth a;
   1120     LOG.n = 0;
   1121     sim_undo_pct = 15;
   1122     int turns = sim_one(7, 4, false, false, &a, log_emit, &LOG);
   1123     sim_undo_pct = 0;
   1124     int undos = 0;
   1125     for (int i = 0; i < LOG.n; i++) if (strstr(LOG.lines[i], "UNDO")) undos++;
   1126     CHECK(undos > 10);
   1127     CHECK(turns > 0);
   1128     char hdr[256];
   1129     Game g;
   1130     static Auth b;
   1131     Opts o = a.g.o;
   1132     auth_new(&b, &o, 7, NULL, NULL);   /* mismo tablero */
   1133     hdr_fmt(&b.g, hdr, sizeof hdr);
   1134     CHECK(hdr_parse(hdr, &g));
   1135     Game red;
   1136     hdr_parse(hdr, &red);
   1137     static Undo ug, ur;
   1138     ug.n = ur.n = 0;
   1139     for (int i = 0; i < LOG.n; i++) {
   1140         Move m, r;
   1141         CHECK(move_parse(LOG.lines[i], &m));
   1142         game_apply_u(&g, &ug, &m);
   1143         move_redact(&m, 0, &r);
   1144         game_apply_u(&red, &ur, &r);
   1145     }
   1146     CHECK(!memcmp(&g, &a.g, sizeof g));
   1147     CHECK(red.winner == a.g.winner && red.phase == PH_OVER);
   1148     for (int p = 0; p < 4; p++) {
   1149         CHECK(game_vp_public(&red, p) == game_vp_public(&a.g, p));
   1150         CHECK(!memcmp(red.p[p].res, a.g.p[p].res, NRES));
   1151         if (p) CHECK(red.p[p].dev[D_KNIGHT] + red.p[p].dev_new[D_KNIGHT] <= a.g.p[p].dev[D_KNIGHT] + a.g.p[p].dev_new[D_KNIGHT]);
   1152     }
   1153     CHECK(!memcmp(red.p[0].dev, a.g.p[0].dev, NDEV));
   1154     CHECK(game_vp(&red, a.g.winner) >= a.g.o.vp_target);
   1155 }
   1156 
   1157 static bool invariants(const Game *g, char *why);
   1158 
   1159 /* juego libre: se anuncia un solo ganador y se sigue jugando 30 turnos mas */
   1160 static int n_wins;
   1161 static void count_wins(void *ctx, const Move *m) { (void)ctx; if (m->type == M_WIN) n_wins++; }
   1162 
   1163 static void t_freeplay(void)
   1164 {
   1165     static Auth a;
   1166     Opts o;
   1167     opts_default(&o, 3, false);
   1168     o.freeplay = 1;
   1169     n_wins = 0;
   1170     auth_new(&a, &o, 11, count_wins, 0);
   1171     Rng br;
   1172     rng_seed(&br, 77, 3);
   1173     Game *g = &a.g;
   1174     char why[64];
   1175     int win_turn = -1;
   1176     for (int step = 0; step < 80000 && g->turn < 600; step++) {
   1177         if (g->winner >= 0 && win_turn < 0) win_turn = g->turn;
   1178         if (win_turn >= 0 && g->turn > win_turn + 30) break;
   1179         bool acted = false;
   1180         for (int p = 0; p < g->o.np && !acted; p++) {
   1181             Move m;
   1182             if (!bot_decide(g, p, &br, &m)) continue;
   1183             CHECK(auth_submit(&a, &m) == E_OK);
   1184             acted = true;
   1185         }
   1186         CHECK(acted);
   1187         if (!acted || !invariants(g, why)) break;
   1188     }
   1189     CHECK(win_turn >= 0 && g->turn > win_turn + 30);
   1190     CHECK(g->phase != PH_OVER && n_wins == 1);
   1191     char buf[400];
   1192     static Game h;
   1193     hdr_fmt(g, buf, sizeof buf);
   1194     CHECK(hdr_parse(buf, &h) && h.o.freeplay == 1 && h.o.setup_first == 0);
   1195 }
   1196 
   1197 /* ---------------------------------------------------------------- websocket (comun/server/ws.c) */
   1198 
   1199 static void hex20(const uint8_t *d, char *out) { for (int i = 0; i < 20; i++) sprintf(out + 2 * i, "%02x", d[i]); }
   1200 
   1201 /* enmascara un frame del servidor como lo haria un cliente (RFC 6455: el cliente siempre enmascara) */
   1202 static int client_frame(int op, const char *data, int n, uint8_t *out, int cap)
   1203 {
   1204     int len = ws_wrap(op, data, n, out, cap);
   1205     if (len < 0) return -1;
   1206     int hdr = len - n;
   1207     uint8_t mask[4] = { 0x12, 0x34, 0x56, 0x78 };
   1208     memmove(out + hdr + 4, out + hdr, (size_t)n);
   1209     out[1] |= 0x80;
   1210     memcpy(out + hdr, mask, 4);
   1211     for (int i = 0; i < n; i++) out[hdr + 4 + i] ^= mask[i & 3];
   1212     return len + 4;
   1213 }
   1214 
   1215 static uint8_t t_ping[WS_CTL_MAX];
   1216 static int t_pinglen;
   1217 static int unwrap(const uint8_t *buf, int n, char *out, int cap, int *got, int *ctl)
   1218 {
   1219     return ws_unwrap(buf, n, out, cap, got, ctl, t_ping, &t_pinglen);
   1220 }
   1221 
   1222 static void t_websocket(void)
   1223 {
   1224     uint8_t dig[20];
   1225     char hex[41], b64[64];
   1226     sha1("abc", 3, dig); hex20(dig, hex);
   1227     CHECK(!strcmp(hex, "a9993e364706816aba3e25717850c26c9cd0d89d"));
   1228     sha1("", 0, dig); hex20(dig, hex);
   1229     CHECK(!strcmp(hex, "da39a3ee5e6b4b0d3255bfef95601890afd80709"));
   1230     static const char *long_msg = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";   /* 56 bytes: cae en dos bloques */
   1231     sha1(long_msg, strlen(long_msg), dig); hex20(dig, hex);
   1232     CHECK(!strcmp(hex, "84983e441c3bd26ebaae4aa1f95129e5e54670f1"));
   1233     /* los largos alrededor del relleno (55/56 y 63/64 cambian de uno a dos bloques): 'a' x n */
   1234     static const struct { int n; const char *h; } A[] = {
   1235         { 55, "c1c8bbdc22796e28c0e15163d20899b65621d65a" }, { 56, "c2db330f6083854c99d4b5bfb6e8f29f201be699" },
   1236         { 63, "03f09f5b158a7a8cdad920bddc29b81c18a551f5" }, { 64, "0098ba824b5c16427bd7a1122a5a442a25ec644d" },
   1237         { 119, "ee971065aaa017e0632a8ca6c77bb3bf8b1dfc56" }, { 120, "f34c1488385346a55709ba056ddd08280dd4c6d6" } };
   1238     for (int i = 0; i < 6; i++) {
   1239         char a[128];
   1240         memset(a, 'a', (size_t)A[i].n);
   1241         sha1(a, (size_t)A[i].n, dig); hex20(dig, hex);
   1242         CHECK(!strcmp(hex, A[i].h));
   1243     }
   1244     CHECK(base64_encode("", 0, b64, sizeof b64) == 0 && !strcmp(b64, ""));
   1245     CHECK(base64_encode("f", 1, b64, sizeof b64) == 4 && !strcmp(b64, "Zg=="));
   1246     CHECK(base64_encode("fo", 2, b64, sizeof b64) == 4 && !strcmp(b64, "Zm8="));
   1247     CHECK(base64_encode("foobar", 6, b64, sizeof b64) == 8 && !strcmp(b64, "Zm9vYmFy"));
   1248     CHECK(base64_encode("foobar", 6, b64, 8) == -1);
   1249 
   1250     /* handshake del ejemplo de la RFC 6455 (con espacios en la lista de subprotocolos) */
   1251     const char *req = "GET /ws HTTP/1.1\r\nHost: x\r\nX-Sec-WebSocket-Key: nope\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\n"
   1252                       "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Protocol:  binary , base64\r\n"
   1253                       "Sec-WebSocket-Version: 13\r\n\r\n";
   1254     char resp[512];
   1255     int used = 0, n = (int)strlen(req);
   1256     CHECK(ws_handshake(req, n - 2, &used, resp, sizeof resp) == 0);           /* incompleto */
   1257     int r = ws_handshake(req, n, &used, resp, sizeof resp);
   1258     CHECK(r > 0 && used == n);
   1259     CHECK(strstr(resp, "HTTP/1.1 101 ") == resp);
   1260     CHECK(strstr(resp, "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n") != NULL);
   1261     CHECK(strstr(resp, "Sec-WebSocket-Protocol: binary\r\n") != NULL);
   1262     CHECK(r >= 4 && !strcmp(resp + r - 4, "\r\n\r\n"));
   1263     const char *bad = "GET / HTTP/1.1\r\nHost: x\r\n\r\n";
   1264     CHECK(ws_handshake(bad, (int)strlen(bad), &used, resp, sizeof resp) == -1 && strstr(resp, "400") != NULL);
   1265     const char *bad2 = "GET / HTTP/1.1\r\nUpgrade: websocket\r\nX-Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n";   /* el nombre en medio no cuenta */
   1266     CHECK(ws_handshake(bad2, (int)strlen(bad2), &used, resp, sizeof resp) == -1);
   1267 
   1268     /* ws_wrap: los limites de cada forma del largo */
   1269     static char big[70000];
   1270     for (int i = 0; i < (int)sizeof big; i++) big[i] = (char)('a' + i % 26);
   1271     static uint8_t buf[80000];
   1272     static char out[80000];
   1273     CHECK(ws_wrap(WS_TEXT, big, 125, buf, sizeof buf) == 127 && buf[1] == 125);
   1274     CHECK(ws_wrap(WS_TEXT, big, 126, buf, sizeof buf) == 130 && buf[1] == 126 && buf[2] == 0 && buf[3] == 126);
   1275     CHECK(ws_wrap(WS_TEXT, big, 65535, buf, sizeof buf) == 65539 && buf[1] == 126 && buf[2] == 255 && buf[3] == 255);
   1276     CHECK(ws_wrap(WS_TEXT, big, 65536, buf, sizeof buf) == 65546 && buf[1] == 127 && buf[7] == 1 && buf[8] == 0 && buf[9] == 0);
   1277     CHECK(ws_wrap(WS_TEXT, big, 300, buf, 303) == -1);
   1278 
   1279     /* frames: chico, mediano (largo de 16 bits) y grande (64 bits), partidos en dos lecturas */
   1280     int got = 0, ctl = 0;
   1281     int len = client_frame(WS_TEXT, "HELLO 1 x\n", 10, buf, sizeof buf);
   1282     CHECK(len == 16);
   1283     CHECK(unwrap(buf, len, out, sizeof out, &got, &ctl) == len && got == 10 && !memcmp(out, "HELLO 1 x\n", 10) && ctl == 0);
   1284     CHECK(unwrap(buf, 1, out, sizeof out, &got, &ctl) == 0 && got == 0);              /* cabecera cortada */
   1285     len = client_frame(WS_BINARY, big, 300, buf, sizeof buf);
   1286     CHECK(len == 308);
   1287     CHECK(unwrap(buf, len, out, sizeof out, &got, &ctl) == len && got == 300 && !memcmp(out, big, 300));
   1288     CHECK(unwrap(buf, 3, out, sizeof out, &got, &ctl) == 0 && got == 0);              /* largo de 16 bits a medias */
   1289     len = client_frame(WS_BINARY, big, (int)sizeof big, buf, sizeof buf);
   1290     CHECK(len == (int)sizeof big + 14);
   1291     CHECK(unwrap(buf, 6, out, sizeof out, &got, &ctl) == 0 && got == 0);              /* largo de 64 bits a medias */
   1292     int half = len / 2;
   1293     CHECK(unwrap(buf, half, out, sizeof out, &got, &ctl) == 0 && got == 0);          /* incompleto: no consume */
   1294     CHECK(unwrap(buf, len, out, sizeof out, &got, &ctl) == len && got == (int)sizeof big && !memcmp(out, big, sizeof big));
   1295     /* dos frames seguidos, un ping con payload, y uno a medias al final */
   1296     int a = client_frame(WS_TEXT, "AB\n", 3, buf, sizeof buf);
   1297     int b = client_frame(WS_CONT, "CD\n", 3, buf + a, sizeof buf - (size_t)a);
   1298     int c = client_frame(WS_PING, "hola", 4, buf + a + b, sizeof buf - (size_t)(a + b));
   1299     int d = client_frame(WS_TEXT, "EF\n", 3, buf + a + b + c, sizeof buf - (size_t)(a + b + c));
   1300     CHECK(unwrap(buf, a + b + c + d - 1, out, sizeof out, &got, &ctl) == a + b + c && got == 6 && !memcmp(out, "AB\nCD\n", 6));
   1301     CHECK(ctl == (1 << WS_PING) && t_pinglen == 4 && !memcmp(t_ping, "hola", 4));
   1302     /* CLOSE seguido de PING: el CLOSE no se pierde y lo de despues no se procesa */
   1303     a = client_frame(WS_CLOSE, "", 0, buf, sizeof buf);
   1304     b = client_frame(WS_PING, "", 0, buf + a, sizeof buf - (size_t)a);
   1305     CHECK(unwrap(buf, a + b, out, sizeof out, &got, &ctl) == a && (ctl & (1 << WS_CLOSE)) && !(ctl & (1 << WS_PING)));
   1306     /* si out se llena, se devuelve lo consumido hasta ahi y el resto queda para despues */
   1307     a = client_frame(WS_TEXT, big, 60, buf, sizeof buf);
   1308     b = client_frame(WS_TEXT, big, 60, buf + a, sizeof buf - (size_t)a);
   1309     CHECK(unwrap(buf, a + b, out, 100, &got, &ctl) == a && got == 60);
   1310     CHECK(unwrap(buf + a, b, out, 100, &got, &ctl) == b && got == 60);
   1311     /* invalidos: sin mascara, extensiones, opcode desconocido, control sin FIN o largo, mas de WS_MAX_FRAME, nunca entra */
   1312     CHECK(ws_wrap(WS_TEXT, "AB\n", 3, buf, sizeof buf) == 5);
   1313     CHECK(unwrap(buf, 5, out, sizeof out, &got, &ctl) == -1);
   1314     len = client_frame(WS_TEXT, big, 300, buf, sizeof buf);
   1315     CHECK(unwrap(buf, len, out, 100, &got, &ctl) == -1);
   1316     buf[0] |= 0x40;
   1317     CHECK(unwrap(buf, len, out, sizeof out, &got, &ctl) == -1);
   1318     len = client_frame(3, "x", 1, buf, sizeof buf);
   1319     CHECK(unwrap(buf, len, out, sizeof out, &got, &ctl) == -1);
   1320     len = client_frame(WS_PING, "x", 1, buf, sizeof buf);
   1321     buf[0] &= 0x7F;                                                                   /* control sin FIN */
   1322     CHECK(unwrap(buf, len, out, sizeof out, &got, &ctl) == -1);
   1323     len = client_frame(WS_PING, big, 126, buf, sizeof buf);
   1324     CHECK(unwrap(buf, len, out, sizeof out, &got, &ctl) == -1);
   1325     len = client_frame(WS_BINARY, big, 1000, buf, sizeof buf);
   1326     buf[2] = 0; buf[3] = 0x10; buf[4] = 0; buf[5] = 0;                                 /* largo de 64 bits: 2^36 */
   1327     buf[1] = 0x80 | 127; memmove(buf + 10, buf + 4, 1004); memset(buf + 2, 0, 8); buf[5] = 0x10;
   1328     CHECK(unwrap(buf, 1014, out, sizeof out, &got, &ctl) == -1);
   1329 }
   1330 
   1331 /* El ladron se confirma antes de robar (un toque de mas no se puede deshacer: el robo es
   1332  * al azar): hexagono -> (victima) -> confirmar; Esc o "otro lugar" vuelven al tablero. */
   1333 static void t_ctrl_robber(void)
   1334 {
   1335     static Auth a;
   1336     Opts o;
   1337     opts_default(&o, 3, false);
   1338     o.beginner = 1;
   1339     auth_new(&a, &o, 11, NULL, NULL);
   1340     Game *g = &a.g;
   1341     const Topo *t = game_topo(g);
   1342     static const char *nm[MAXP] = { "A", "B", "C", "D" };
   1343     /* un hex sin nadie (h0) y otro con un pueblo de B con cartas (h1), ninguno con el ladron */
   1344     int h0 = -1, h1 = -1;
   1345     for (int h = 0; h < t->nhex && (h0 < 0 || h1 < 0); h++) {
   1346         if (h == g->robber || !ter_land(g->terrain[h])) continue;
   1347         if (h0 < 0) { h0 = h; continue; }
   1348         h1 = h;
   1349     }
   1350     place(g, 1, t->hex_vert[h1][0], 1);
   1351     g->p[1].res[GRAIN] += 2; g->bank[GRAIN] -= 2;
   1352     g->phase = PH_ROBBER; g->cur = 0; g->rolled = 1;
   1353     static Ctrl c;
   1354     ctrl_init(&c, 1, false);
   1355     for (int i = 0; i < MAXP; i++) c.names[i] = nm[i];
   1356     ctrl_sync(&c, g);
   1357     CHECK(c.mode == CM_HEX && c.intent == I_ROBBER);
   1358     Move m;
   1359     CHECK(!ctrl_pick(&c, PK_HEX, h0, &m) && c.mode == CM_MENU && c.intent == I_ROBBER_OK && c.pending_victim < 0);
   1360     CHECK(ctrl_can_back(&c));
   1361     CHECK(!ctrl_key(&c, K_BACK, &m) && c.mode == CM_HEX && c.intent == I_ROBBER);      /* otro lugar */
   1362     CHECK(!ctrl_pick(&c, PK_HEX, h1, &m) && c.mode == CM_MENU && c.intent == I_ROBBER_OK && c.pending_victim == 1);
   1363     CHECK(!ctrl_key(&c, 'v', &m) && c.mode == CM_HEX);                                  /* "elegir otro lugar" */
   1364     CHECK(!ctrl_pick(&c, PK_HEX, h1, &m) && c.mode == CM_MENU);
   1365     CHECK(ctrl_key(&c, 'e', &m) && m.type == M_ROBBER && m.a == h1 && m.b == 1);          /* confirma: recien ahi sale la movida */
   1366     CHECK(auth_submit(&a, &m) == E_OK && g->robber == h1 && g->p[1].res[GRAIN] == 1 && g->p[0].res[GRAIN] == 1);
   1367 }
   1368 
   1369 /* Navegantes: el oro lo elige cada uno aunque el turno sea de otro (igual que el descarte). */
   1370 static void t_ctrl_gold(void)
   1371 {
   1372     static Auth a;
   1373     Opts o;
   1374     opts_default(&o, 3, false);
   1375     opts_scenario(&o, SCEN_SEA1);
   1376     auth_new(&a, &o, 5, NULL, NULL);
   1377     Game *g = &a.g;
   1378     static const char *nm[MAXP] = { "A", "B", "C", "D" };
   1379     g->phase = PH_GOLD; g->cur = 1; g->rolled = 1;
   1380     g->p[0].gold = 1;
   1381     static Ctrl c;
   1382     ctrl_init(&c, 1, false);                 /* solo el asiento 0 es local */
   1383     for (int i = 0; i < MAXP; i++) c.names[i] = nm[i];
   1384     ctrl_sync(&c, g);
   1385     CHECK(c.actor == 0 && c.mode == CM_CARDS && c.intent == I_GOLD);
   1386     Move m;
   1387     CHECK(!ctrl_key(&c, '1', &m));            /* elige un recurso */
   1388     CHECK(ctrl_key(&c, K_OK, &m) && m.type == M_GOLD && m.p == 0);
   1389 }
   1390 
   1391 /* Quien empieza se sortea con la semilla (igual en todos los nodos), viaja en la cabecera
   1392  * como "F <asiento>" y las cabeceras viejas sin F arrancan por el asiento 0. */
   1393 static void t_first(void)
   1394 {
   1395     Opts o;
   1396     opts_default(&o, 4, false);
   1397     o.beginner = 1;
   1398     o.first = OPTS_FIRST_RANDOM;
   1399     uint8_t kinds[MAXP] = { SEAT_BOT, SEAT_BOT, SEAT_BOT, SEAT_BOT };
   1400     static Host h;
   1401     int seen = 0;
   1402     for (uint64_t seed = 1; seed <= 8; seed++) {
   1403         host_init(&h, &o, seed, kinds, NULL, NULL);
   1404         Game *g = &h.a.g;
   1405         int f = (int)(seed % 4);
   1406         CHECK(g->o.first == f && g->cur == f && g->setup_order[0] == f && g->setup_order[1] == (f + 1) % 4);
   1407         CHECK(g->setup_order[7] == f);                 /* la serpiente vuelve al que empezo */
   1408         seen |= 1 << f;
   1409         char hdr[512];
   1410         Game g2;
   1411         hdr_fmt(g, hdr, sizeof hdr);
   1412         CHECK((f == 0) != (strncmp(hdr, "F ", 2) == 0));   /* el prefijo solo si no es el 0 */
   1413         CHECK(hdr_parse(hdr, &g2) && g2.o.first == f && g2.cur == f && !memcmp(&g2, g, sizeof g2));
   1414     }
   1415     CHECK(seen == 15);
   1416     Game g3;
   1417     CHECK(hdr_parse("G 4 0 10 0 2 2 1 " "4 1 0 3 1 2 4 3 1 0 2 4 2 3 0 4 3 2 1 " "5 2 6 3 8 10 9 12 11 4 8 10 9 4 5 6 3 11 0 " "4 5 5 5 5 5 1 2 3", &g3) && g3.cur == 0);
   1418 }
   1419 
   1420 int app_selftest(void)
   1421 {
   1422     printf("selftest del nucleo\n");
   1423     t_topology();
   1424     t_setup_and_distance();
   1425     t_setup_order_and_resources();
   1426     t_setup_first();
   1427     t_production();
   1428     t_seven_discard_robber();
   1429     t_build_costs();
   1430     t_bank_ratios();
   1431     t_longest_road();
   1432     t_dev_cards_and_army();
   1433     t_offer();
   1434     t_counter();
   1435     t_move_public();
   1436     t_paired();
   1437     t_sea();
   1438     t_maps();
   1439     t_variant2p();
   1440     t_undo();
   1441     t_ctrl_undo();
   1442     t_ctrl_counter();
   1443     t_ctrl_nav();
   1444     t_ctrl_setup();
   1445     t_ctrl_robber();
   1446     t_ctrl_gold();
   1447     t_first();
   1448     t_serialize();
   1449     t_replay();
   1450     t_freeplay();
   1451     t_websocket();
   1452     printf("%d chequeos, %d fallas\n", checks, fails);
   1453     return fails ? 1 : 0;
   1454 }
   1455 
   1456 /* --------------------------------------------------------------- simulacion */
   1457 
   1458 static bool invariants(const Game *g, char *why)
   1459 {
   1460     if (!res_conserved(g)) { strcpy(why, "recursos no conservados"); return false; }
   1461     int devs = g->deck_n;
   1462     for (int p = 0; p < g->o.np; p++) {
   1463         const Player *pl = &g->p[p];
   1464         if (pl->roads > MAX_ROADS || pl->settles > MAX_SETTLES || pl->cities > MAX_CITIES) { strcpy(why, "piezas"); return false; }
   1465         for (int d = 0; d < NDEV; d++) devs += pl->dev[d] + pl->dev_new[d];
   1466         devs += pl->knights;
   1467     }
   1468     uint8_t cnt[NDEV];
   1469     if (devs > dev_counts(&g->o, cnt)) { strcpy(why, "mazo"); return false; }
   1470     int onboard[MAXP] = { 0 };
   1471     for (int v = 0; v < NVERT; v++) if (g->vown[v] >= 0) onboard[g->vown[v]]++;
   1472     for (int p = 0; p < g->o.np; p++) {
   1473         const Player *pl = &g->p[p];
   1474         if (onboard[p] != (MAX_SETTLES - pl->settles) + (MAX_CITIES - pl->cities)) { strcpy(why, "edificios"); return false; }
   1475     }
   1476     return true;
   1477 }
   1478 
   1479 int g_sim_scenario;           /* --sim ... mar: Navegantes */
   1480 int g_sim_map;                /* --sim ... <mapa> (0..NMAPS-1) */
   1481 
   1482 int sim_one(uint64_t seed, int players, bool v2p, bool verbose, Auth *a, EmitFn fn, void *ctx)
   1483 {
   1484     Opts o;
   1485     opts_default(&o, players, v2p);
   1486     if (g_sim_scenario) opts_scenario(&o, g_sim_scenario);
   1487     o.map = (uint8_t)g_sim_map;
   1488     auth_new(a, &o, seed, fn, ctx);
   1489     Rng br;
   1490     rng_seed(&br, seed ^ 0x9E3779B97F4A7C15ULL, 3);
   1491     Game *g = &a->g;
   1492     char why[64];
   1493     for (int step = 0; step < 50000; step++) {
   1494         if (g->phase == PH_OVER) return g->turn;
   1495         if (g->turn > 600) { if (verbose) printf("seed %llu: sin ganador tras 600 turnos\n", (unsigned long long)seed); return 0; }
   1496         bool acted = false;
   1497         if (sim_undo_pct && (int)rng_below(&br, 100) < sim_undo_pct) {
   1498             Move u = { .type = M_UNDO, .p = (int8_t)g->cur };
   1499             if (auth_submit(a, &u) == E_OK) acted = true;
   1500         }
   1501         for (int s = 0; s < g->o.np && !acted; s++) {
   1502             Move m;
   1503             if (!bot_decide(g, s, &br, &m)) continue;
   1504             int e = auth_submit(a, &m);
   1505             if (e) {
   1506                 char buf[128];
   1507                 move_fmt(&m, buf, sizeof buf);
   1508                 printf("seed %llu: movida de bot rechazada '%s': %s (fase %d)\n",
   1509                        (unsigned long long)seed, buf, err_text(e), g->phase);
   1510                 return -1;
   1511             }
   1512             acted = true;
   1513         }
   1514         if (!acted) {
   1515             printf("seed %llu: nadie puede actuar (fase %d)\n", (unsigned long long)seed, g->phase);
   1516             if (getenv("SIM_DEBUG")) {
   1517                 int ns = 0;
   1518                 for (int v = 0; v < game_topo(g)->nvert; v++) if (game_can_settle(g, g->cur, v, true)) ns++;
   1519                 printf("  cur %d sub %d setup_v %d lugares %d\n", g->cur, g->setup_sub, g->setup_v, ns);
   1520                 if (g->setup_v >= 0) for (int k = 0; k < 3; k++) {
   1521                     int e = game_topo(g)->vert_edge[g->setup_v][k];
   1522                     Move tm = { .type = M_ROAD, .p = (int8_t)g->cur, .a = (int16_t)e };
   1523                     printf("   arista %d own %d check %d\n", e, e >= 0 ? g->eown[e] : -9, e >= 0 ? game_check(g, &tm) : -9);
   1524                 }
   1525             }
   1526             return -1;
   1527         }
   1528         if (!invariants(g, why)) { printf("seed %llu: invariante rota: %s\n", (unsigned long long)seed, why); return -1; }
   1529     }
   1530     printf("seed %llu: demasiados pasos\n", (unsigned long long)seed);
   1531     return -1;
   1532 }
   1533 
   1534 #ifdef DUEL_OLD
   1535 /* la search.c de la version publicada (core/search_old.c, la genera el Makefile) */
   1536 bool search_old_decide(const Game *g, int seat, Rng *rng, const SearchCfg *cfg, Move *out);
   1537 #endif
   1538 
   1539 /* Duelo: en la partida i el asiento i % jugadores usa la busqueda, el resto la heuristica
   1540  * (o, en catan-duel, la busqueda de la version anterior: nueva contra vieja). */
   1541 int app_duel(int n, uint64_t seed0, int players, bool v2p, const SearchCfg *cfg)
   1542 {
   1543     static Auth a;
   1544     int wins = 0, done = 0, bad = 0, humans = 0;
   1545     double ms = 0;
   1546     long decisions = 0;
   1547     for (int i = 0; i < n; i++) {
   1548         uint64_t seed = seed0 + (uint64_t)i;
   1549         Opts o;
   1550         opts_default(&o, players, v2p);
   1551         if (g_sim_scenario) opts_scenario(&o, g_sim_scenario);
   1552     o.map = (uint8_t)g_sim_map;
   1553         auth_new(&a, &o, seed, NULL, NULL);
   1554         Game *g = &a.g;
   1555         humans = 0;
   1556         for (int s = 0; s < g->o.np; s++) if (!g->p[s].neutral) humans++;
   1557         int strong = (int)(i % (uint64_t)humans);
   1558         Rng br;
   1559         rng_seed(&br, seed ^ 0x9E3779B97F4A7C15ULL, 3);
   1560         char why[64];
   1561         bool ok = false;
   1562         for (int step = 0; step < 50000; step++) {
   1563             if (g->phase == PH_OVER) { ok = true; break; }
   1564             if (g->turn > 600) break;
   1565             bool acted = false;
   1566             for (int s = 0; s < g->o.np && !acted; s++) {
   1567                 Move m;
   1568                 bool d;
   1569                 if (s == strong) {
   1570                     struct timespec t0, t1;
   1571                     clock_gettime(CLOCK_MONOTONIC, &t0);
   1572                     d = search_decide(g, s, &br, cfg, &m);
   1573                     clock_gettime(CLOCK_MONOTONIC, &t1);
   1574                     if (d) { ms += (t1.tv_sec - t0.tv_sec) * 1e3 + (t1.tv_nsec - t0.tv_nsec) / 1e6; decisions++; }
   1575                 } else {
   1576 #ifdef DUEL_OLD
   1577                     d = search_old_decide(g, s, &br, cfg, &m);
   1578 #else
   1579                     d = bot_decide(g, s, &br, &m);
   1580 #endif
   1581                 }
   1582                 if (!d) continue;
   1583                 int e = auth_submit(&a, &m);
   1584                 if (e) {
   1585                     char buf[128];
   1586                     move_fmt(&m, buf, sizeof buf);
   1587                     printf("seed %llu: movida rechazada '%s' de %d: %s\n", (unsigned long long)seed, buf, s, err_text(e));
   1588                     bad++;
   1589                     step = 50000;
   1590                     break;
   1591                 }
   1592                 acted = true;
   1593             }
   1594             if (!acted) break;
   1595             if (!invariants(g, why)) { printf("seed %llu: invariante rota: %s\n", (unsigned long long)seed, why); bad++; break; }
   1596         }
   1597         if (ok) { done++; if (g->winner == strong) wins++; }
   1598     }
   1599 #ifdef DUEL_OLD
   1600     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",
   1601 #else
   1602     printf("%d partidas (%d jugadores%s, %d iter, horizonte %d): busqueda gano %d de %d (%.1f%%, base %.1f%%), %d errores\n",
   1603 #endif
   1604            n, players, v2p ? ", 2p" : "", cfg->iters, cfg->horizon, wins, done,
   1605            done ? 100.0 * wins / done : 0.0, 100.0 / humans, bad);
   1606     printf("%.2f ms por decision (%ld decisiones)\n", decisions ? ms / (double)decisions : 0.0, decisions);
   1607     return bad ? 1 : 0;
   1608 }
   1609 
   1610 int app_sim(int n, uint64_t seed0, int players, bool v2p)
   1611 {
   1612     static Auth a;
   1613     int bad = 0, stuck = 0;
   1614     long turns = 0;
   1615     int wins[MAXP] = { 0 };
   1616     for (int i = 0; i < n; i++) {
   1617         int t = sim_one(seed0 + (uint64_t)i, players, v2p, true, &a, NULL, NULL);
   1618         if (t < 0) bad++;
   1619         else if (t == 0) stuck++;
   1620         else { turns += t; wins[a.g.winner]++; }
   1621     }
   1622     int ok = n - bad - stuck;
   1623     printf("%d partidas (%d jugadores%s%s): %d ok, %d trabadas, %d errores; turnos promedio %.1f\n",
   1624            n, players, v2p ? ", variante 2p" : "", g_sim_scenario ? ", Navegantes" : "", ok, stuck, bad, ok ? (double)turns / ok : 0.0);
   1625     if (g_sim_map || g_sim_scenario > SCEN_SEA1) {
   1626         Opts o;
   1627         opts_default(&o, players, v2p);
   1628         opts_scenario(&o, g_sim_scenario);
   1629         o.map = (uint8_t)g_sim_map;
   1630         printf("mapa: %s\n", board_name(opts_board(&o)));
   1631     }
   1632     printf("victorias por asiento:");
   1633     for (int p = 0; p < MAXP; p++) printf(" %d", wins[p]);
   1634     printf("\n");
   1635     return bad ? 1 : 0;
   1636 }