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 }