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

bot.c (24640B)


      1 /* bot.c - IA heuristica. Solo mira informacion publica + su propia mano. */
      2 #include <string.h>
      3 #include "catan.h"
      4 #include "bot_int.h"
      5 
      6 static const int PIPS[13] = { 0, 0, 1, 2, 3, 4, 5, 0, 5, 4, 3, 2, 1 };
      7 
      8 static void production(const Game *g, int p, int prod[NRES])
      9 {
     10     const Topo *t = game_topo(g);
     11     memset(prod, 0, sizeof(int) * NRES);
     12     for (int v = 0; v < game_topo(g)->nvert; v++) {
     13         if (g->vown[v] != p) continue;
     14         for (int k = 0; k < 3; k++) {
     15             int h = t->vert_hex[v][k];
     16             if (h >= 0 && ter_res(g->terrain[h])) prod[g->terrain[h]] += PIPS[g->num[h]] * g->vlev[v];
     17         }
     18     }
     19 }
     20 
     21 /* Valor de un vertice para p: pips ponderados, diversidad, puertos.
     22  * prod = production(g, p) ya calculada (no depende de v). */
     23 static int vert_value_with(const Game *g, const int prod[NRES], int v)
     24 {
     25     const Topo *t = game_topo(g);
     26     int score = 0, seen = 0;
     27     for (int k = 0; k < 3; k++) {
     28         int h = t->vert_hex[v][k];
     29         if (h < 0) continue;
     30         if (g->terrain[h] == T_GOLD) { score += PIPS[g->num[h]] * 15; continue; }   /* oro: lo que haga falta */
     31         if (!ter_res(g->terrain[h])) continue;
     32         int r = g->terrain[h], w = 10;
     33         if (prod[r] == 0) w += 4;                   /* recurso que no produzco */
     34         if (r == BRICK || r == LUMBER) w += 1;
     35         score += PIPS[g->num[h]] * w;
     36         if (h == g->robber) score -= PIPS[g->num[h]] * 4;
     37         if (!(seen & (1 << r))) { seen |= 1 << r; score += 6; }
     38     }
     39     int port = t->vert_port[v];
     40     if (port >= 0) {
     41         int kind = g->port[port];
     42         score += kind == PORT_ANY ? 6 : (prod[kind] >= 5 ? 14 : 4);
     43     }
     44     return score;
     45 }
     46 
     47 static int vert_value(const Game *g, int p, int v)
     48 {
     49     int prod[NRES];
     50     production(g, p, prod);
     51     return vert_value_with(g, prod, v);
     52 }
     53 
     54 int bot_vert_value(const Game *g, int p, int v) { return vert_value(g, p, v); }
     55 
     56 int bot_pips(const Game *g, int p, int prod[NRES])
     57 {
     58     production(g, p, prod);
     59     int n = 0;
     60     for (int r = 0; r < NRES; r++) n += prod[r];
     61     return n;
     62 }
     63 
     64 static int best_settle_spot(const Game *g, int p, bool setup)
     65 {
     66     int best = -1, bs = -1, prod[NRES];
     67     production(g, p, prod);
     68     for (int v = 0; v < game_topo(g)->nvert; v++)
     69         if (game_can_settle(g, p, v, setup)) {
     70             int s = vert_value_with(g, prod, v);
     71             if (s > bs) { bs = s; best = v; }
     72         }
     73     return best;
     74 }
     75 
     76 /* Para cada arista e que p puede construir: el mejor lugar libre alcanzable
     77  * (<=3 rutas) por un camino que empieza en e, como vert_value - 12*distancia.
     78  * -1000 si no lleva a ningun lugar. */
     79 void bot_road_scores(const Game *g, int p, int score[NEDGE])
     80 {
     81     const Topo *t = game_topo(g);
     82     int8_t dist[NVERT];
     83     int16_t first[NVERT];
     84     uint8_t queue[NVERT];
     85     int qh = 0, qt = 0, prod[NRES];
     86     production(g, p, prod);
     87     for (int e = 0; e < game_topo(g)->nedge; e++) score[e] = -1000;
     88     for (int v = 0; v < game_topo(g)->nvert; v++) { dist[v] = -1; first[v] = -1; }
     89     for (int v = 0; v < game_topo(g)->nvert; v++) {
     90         if (g->vown[v] >= 0 && g->vown[v] != p) continue;
     91         bool src = g->vown[v] == p;
     92         for (int k = 0; k < 3 && !src; k++) {
     93             int e = t->vert_edge[v][k];
     94             if (e >= 0 && g->eown[e] == p) src = true;
     95         }
     96         if (src) { dist[v] = 0; queue[qt++] = (uint8_t)v; }
     97     }
     98     while (qh < qt) {
     99         int u = queue[qh++];
    100         if (dist[u] >= 3) continue;
    101         if (dist[u] > 0 && g->vown[u] >= 0 && g->vown[u] != p) continue;
    102         for (int k = 0; k < 3; k++) {
    103             int e = t->vert_edge[u][k];
    104             if (e < 0 || g->eown[e] >= 0) continue;
    105             int w = t->edge_v[e][0] == u ? t->edge_v[e][1] : t->edge_v[e][0];
    106             if (dist[w] >= 0) continue;
    107             dist[w] = (int8_t)(dist[u] + 1);
    108             first[w] = (int16_t)(dist[u] == 0 ? e : first[u]);
    109             queue[qt++] = (uint8_t)w;
    110         }
    111     }
    112     for (int v = 0; v < game_topo(g)->nvert; v++) {
    113         if (dist[v] < 1 || first[v] < 0) continue;
    114         bool free = g->vown[v] < 0;
    115         for (int k = 0; k < 3 && free; k++) {
    116             int u = t->vert_adj[v][k];
    117             if (u >= 0 && g->vown[u] >= 0) free = false;
    118         }
    119         if (!free) continue;
    120         int s = vert_value_with(g, prod, v) - 12 * dist[v];
    121         if (s > score[first[v]] && game_can_road(g, p, first[v])) score[first[v]] = s;
    122     }
    123 }
    124 
    125 /* vertice libre (y sin vecinos ocupados) que toca tierra: donde podria ir un pueblo */
    126 static bool free_land(const Game *g, int v)
    127 {
    128     const Topo *t = game_topo(g);
    129     if (g->vown[v] >= 0) return false;
    130     bool land = false;
    131     for (int k = 0; k < 3; k++) {
    132         int u = t->vert_adj[v][k], h = t->vert_hex[v][k];
    133         if (u >= 0 && g->vown[u] >= 0) return false;
    134         if (h >= 0 && ter_land(g->terrain[h])) land = true;
    135     }
    136     return land;
    137 }
    138 
    139 /* Navegantes: igual que bot_road_scores pero por agua (hasta 5 barcos); los lugares en
    140  * una isla donde todavia no cobro los 2 puntos valen mas. */
    141 void bot_ship_scores(const Game *g, int p, int score[NEDGE])
    142 {
    143     const Topo *t = game_topo(g);
    144     int8_t dist[NVERT];
    145     int16_t first[NVERT];
    146     uint8_t queue[NVERT];
    147     int qh = 0, qt = 0, prod[NRES];
    148     production(g, p, prod);
    149     for (int e = 0; e < t->nedge; e++) score[e] = -1000;
    150     if (!game_has_sea(g) || !g->p[p].ships) return;
    151     for (int v = 0; v < t->nvert; v++) { dist[v] = -1; first[v] = -1; }
    152     for (int v = 0; v < t->nvert; v++) {          /* salen de edificios propios y barcos propios */
    153         bool src = g->vown[v] == p;
    154         for (int k = 0; k < 3 && !src && g->vown[v] < 0; k++) {
    155             int e = t->vert_edge[v][k];
    156             if (e >= 0 && g->eown[e] == p && g->ship[e]) src = true;
    157         }
    158         if (src) { dist[v] = 0; queue[qt++] = (uint8_t)v; }
    159     }
    160     while (qh < qt) {
    161         int u = queue[qh++];
    162         if (dist[u] >= 5) continue;
    163         if (dist[u] > 0 && g->vown[u] >= 0) continue;
    164         for (int k = 0; k < 3; k++) {
    165             int e = t->vert_edge[u][k];
    166             if (e < 0 || g->eown[e] >= 0) continue;
    167             bool sea = false;
    168             for (int s = 0; s < 2; s++) { int h = t->edge_hex[e][s]; if (h >= 0 && !ter_land(g->terrain[h]) && h != g->pirate) sea = true; }
    169             if (!sea || t->edge_hex[e][0] == g->pirate || t->edge_hex[e][1] == g->pirate) continue;
    170             int w = t->edge_v[e][0] == u ? t->edge_v[e][1] : t->edge_v[e][0];
    171             if (dist[w] >= 0) continue;
    172             dist[w] = (int8_t)(dist[u] + 1);
    173             first[w] = (int16_t)(dist[u] == 0 ? e : first[u]);
    174             queue[qt++] = (uint8_t)w;
    175         }
    176     }
    177     for (int v = 0; v < t->nvert; v++) {
    178         if (dist[v] < 1 || first[v] < 0 || !free_land(g, v)) continue;     /* lugar libre con tierra */
    179         int s = vert_value_with(g, prod, v) - 10 * dist[v];
    180         for (int k = 0; k < 3; k++) {
    181             int h = t->vert_hex[v][k], r = h >= 0 ? t->region[h] : -1;
    182             if (r >= 0 && r < 8 && !((g->p[p].isles >> r) & 1)) { s += 40; break; }
    183         }
    184         if (s > score[first[v]] && game_can_ship(g, p, first[v], false)) score[first[v]] = s;
    185     }
    186 }
    187 
    188 static int ship_toward_spot_s(const Game *g, int p, int *best_score)
    189 {
    190     int score[NEDGE], best = -1, bs = -1000;
    191     bot_ship_scores(g, p, score);
    192     for (int e = 0; e < game_topo(g)->nedge; e++)
    193         if (score[e] > bs) { bs = score[e]; best = e; }
    194     if (best_score) *best_score = bs;
    195     return best;
    196 }
    197 
    198 static int ship_toward_spot(const Game *g, int p) { return ship_toward_spot_s(g, p, 0); }
    199 
    200 static int road_toward_spot_s(const Game *g, int p, int *best_score);
    201 
    202 /* conviene ir por agua: el mejor destino en barco (las islas nuevas dan 2 puntos) le
    203  * gana al mejor por tierra, o por tierra ya no queda nada */
    204 static bool ship_better(const Game *g, int p)
    205 {
    206     if (!game_has_sea(g) || !g->p[p].ships || !g->p[p].settles) return false;
    207     int ss, rs;
    208     if (ship_toward_spot_s(g, p, &ss) < 0) return false;
    209     if (road_toward_spot_s(g, p, &rs) < 0 || !g->p[p].roads) return true;
    210     return ss > rs + 10;
    211 }
    212 
    213 /* Primera arista del camino hacia el mejor lugar libre alcanzable (<=3 rutas). */
    214 static int road_toward_spot_s(const Game *g, int p, int *best_score)
    215 {
    216     int score[NEDGE], best = -1, bs = -1000;
    217     bot_road_scores(g, p, score);
    218     for (int e = 0; e < game_topo(g)->nedge; e++)
    219         if (score[e] > bs) { bs = score[e]; best = e; }
    220     if (best_score) *best_score = bs;
    221     return best;
    222 }
    223 
    224 static int road_toward_spot(const Game *g, int p) { return road_toward_spot_s(g, p, 0); }
    225 
    226 static int any_legal_road(const Game *g, int p, Rng *rng)
    227 {
    228     int cand[NEDGE], n = 0;
    229     for (int e = 0; e < game_topo(g)->nedge; e++)
    230         if (game_can_road(g, p, e)) cand[n++] = e;
    231     return n ? cand[rng_below(rng, (uint32_t)n)] : -1;
    232 }
    233 
    234 /* Que esta juntando: 0 pueblo, 1 ciudad, 2 carta, 3 ruta */
    235 int bot_goal(const Game *g, int p, uint8_t cost[NRES])
    236 {
    237     const Player *pl = &g->p[p];
    238     if (pl->settles > 0 && best_settle_spot(g, p, false) >= 0) { memcpy(cost, COST_SETTLE, NRES); return 0; }
    239     if (ship_better(g, p)) { memcpy(cost, COST_SHIP, NRES); return 4; }     /* Navegantes: a las islas */
    240     if (pl->cities > 0)
    241         for (int v = 0; v < game_topo(g)->nvert; v++)
    242             if (g->vown[v] == p && g->vlev[v] == 1) { memcpy(cost, COST_CITY, NRES); return 1; }
    243     if (pl->roads > 0 && pl->settles > 0 && road_toward_spot(g, p) >= 0) { memcpy(cost, COST_ROAD, NRES); return 3; }
    244     memcpy(cost, COST_DEV, NRES);
    245     return 2;
    246 }
    247 
    248 void bot_pick_discard(const Game *g, int p, int n, uint8_t out[NRES])
    249 {
    250     uint8_t cost[NRES], hand[NRES];
    251     bot_goal(g, p, cost);
    252     memcpy(hand, g->p[p].res, NRES);
    253     memset(out, 0, NRES);
    254     while (n-- > 0) {
    255         int best = -1, bs = -1000;
    256         for (int r = 0; r < NRES; r++) {
    257             if (!hand[r]) continue;
    258             int s = hand[r] * 2 - (hand[r] <= cost[r] ? 5 : 0);
    259             if (s > bs) { bs = s; best = r; }
    260         }
    261         if (best < 0) break;
    262         hand[best]--;
    263         out[best]++;
    264     }
    265 }
    266 
    267 /* Cuanto le conviene a p poner el ladron en h (mas = peor para los rivales). */
    268 int bot_robber_score(const Game *g, int p, int h)
    269 {
    270     const Topo *t = game_topo(g);
    271     int s = 0;
    272     if (!ter_land(g->terrain[h])) {               /* pirata: barcos ajenos alrededor */
    273         for (int k = 0; k < 6; k++) {
    274             int e = t->hex_edge[h][k], o = g->eown[e];
    275             if (o < 0 || !g->ship[e]) continue;
    276             s += o == p ? -60 : 8 * (3 + game_vp_public(g, o));
    277         }
    278         return s - 1;                               /* a igualdad, mejor el ladron en tierra */
    279     }
    280     for (int k = 0; k < 6; k++) {
    281         int v = t->hex_vert[h][k], o = g->vown[v];
    282         if (o < 0) continue;
    283         if (o == p) s -= 100;
    284         else if (!g->p[o].neutral) s += PIPS[g->num[h]] * g->vlev[v] * (3 + game_vp_public(g, o));
    285     }
    286     return s;
    287 }
    288 
    289 /* A quien robarle en h: el vecino con mas cartas (-1 nadie). */
    290 int bot_robber_victim(const Game *g, int p, int h)
    291 {
    292     const Topo *t = game_topo(g);
    293     int most = 0, who = -1;
    294     bool sea = !ter_land(g->terrain[h]);
    295     for (int k = 0; k < 6; k++) {
    296         int e = t->hex_edge[h][k];
    297         int o = sea ? (g->ship[e] ? g->eown[e] : -1) : g->vown[t->hex_vert[h][k]];
    298         if (o >= 0 && o != p && !g->p[o].neutral && hand_size(g, o) > most) { most = hand_size(g, o); who = o; }
    299     }
    300     return who;
    301 }
    302 
    303 static void pick_robber(const Game *g, int p, Move *m)
    304 {
    305     int best = -1, bs = -100000;
    306     for (int h = 0; h < game_topo(g)->nhex; h++) {
    307         if (!game_can_robber(g, p, h)) continue;
    308         int s = bot_robber_score(g, p, h);
    309         if (s > bs) { bs = s; best = h; }
    310     }
    311     m->type = M_ROBBER;
    312     m->a = (int16_t)best;
    313     m->b = (int16_t)(best >= 0 ? bot_robber_victim(g, p, best) : -1);
    314 }
    315 
    316 bool bot_robber_on_me(const Game *g, int p)
    317 {
    318     const Topo *t = game_topo(g);
    319     for (int k = 0; k < 6; k++)
    320         if (g->vown[t->hex_vert[g->robber][k]] == p) return true;
    321     return false;
    322 }
    323 
    324 /* Cambio con el banco hacia lo que falta para el objetivo. */
    325 static bool bank_toward(const Game *g, int p, const uint8_t cost[NRES], Move *m)
    326 {
    327     const Player *pl = &g->p[p];
    328     for (int want = 0; want < NRES; want++) {
    329         if (pl->res[want] >= cost[want] || !g->bank[want]) continue;
    330         for (int give = 0; give < NRES; give++) {
    331             if (give == want) continue;
    332             int ratio = game_bank_ratio(g, p, give);
    333             if (pl->res[give] >= cost[give] + ratio) {
    334                 m->type = M_BANK; m->a = (int16_t)give; m->b = (int16_t)want; m->c = 1;
    335                 return true;
    336             }
    337         }
    338     }
    339     return false;
    340 }
    341 
    342 /* Cuanto le sirve a p dar give y recibir get: lo que le falta para su objetivo vale triple. */
    343 static int trade_value(const Game *g, int p, const uint8_t give[NRES], const uint8_t get[NRES])
    344 {
    345     uint8_t cost[NRES];
    346     bot_goal(g, p, cost);
    347     int v = 0;
    348     for (int r = 0; r < NRES; r++) {
    349         v += get[r] * (g->p[p].res[r] < cost[r] ? 3 : 1);
    350         v -= give[r] * (g->p[p].res[r] - give[r] < cost[r] ? 3 : 1);
    351     }
    352     return v;
    353 }
    354 
    355 /* 1 por 1: lo que mas le falta a p (y tiene "of") contra lo que mas le sobra.
    356  * give/get desde p. false si no hay nada razonable. */
    357 static bool one_for_one(const Game *g, int p, int of, uint8_t give[NRES], uint8_t get[NRES])
    358 {
    359     uint8_t cost[NRES];
    360     bot_goal(g, p, cost);
    361     const Player *me = &g->p[p];
    362     int want = -1, ws = 0, put = -1, ps = 0;
    363     for (int r = 0; r < NRES; r++) {
    364         int need = cost[r] - me->res[r];
    365         bool has = false;
    366         for (int q = 0; q < g->o.np; q++)
    367             if (q != p && !g->p[q].neutral && (of < 0 || q == of) && g->p[q].res[r]) has = true;
    368         if (need > ws && has) { ws = need; want = r; }
    369     }
    370     for (int r = 0; r < NRES; r++) {
    371         int spare = me->res[r] - cost[r];
    372         if (r != want && spare > ps) { ps = spare; put = r; }
    373     }
    374     if (want < 0 || put < 0) return false;
    375     memset(give, 0, NRES);
    376     memset(get, 0, NRES);
    377     give[put] = 1;
    378     get[want] = 1;
    379     return true;
    380 }
    381 
    382 /* Contraoferta a la oferta abierta (false: no tiene una mejor que proponer). */
    383 bool bot_counter(const Game *g, int p, Move *m)
    384 {
    385     const Offer *o = &g->offer;
    386     if (!o->active || game_vp_public(g, o->from) >= 8) return false;
    387     memset(m, 0, sizeof *m);
    388     m->p = (int8_t)p;
    389     m->type = M_COUNTER;
    390     if (!one_for_one(g, p, o->from, m->r, m->r2)) return false;
    391     if (!memcmp(m->r, o->get, NRES) && !memcmp(m->r2, o->give, NRES)) return false;   /* es la misma */
    392     return trade_value(g, p, m->r, m->r2) > 0 && game_check(g, m) == E_OK;
    393 }
    394 
    395 static bool respond_offer(const Game *g, int p, Move *m)
    396 {
    397     const Offer *o = &g->offer;
    398     bool ok = trade_value(g, p, o->get, o->give) > 0 && game_vp_public(g, o->from) < 8 && game_can_afford(g, p, o->get);
    399     if (ok) { m->type = M_ACCEPT; return true; }
    400     if (bot_counter(g, p, m)) return true;
    401     m->type = M_REJECT;
    402     return true;
    403 }
    404 
    405 /* Oferta propia de 1 por 1 a los que tienen lo que falta (una por turno). */
    406 bool bot_offer(const Game *g, int p, Move *m)
    407 {
    408     if (g->offers || g->offer.active) return false;
    409     uint8_t give[NRES], get[NRES];
    410     if (!one_for_one(g, p, -1, give, get) || trade_value(g, p, give, get) <= 0) return false;
    411     int mask = 0, want = 0;
    412     for (int r = 0; r < NRES; r++) if (get[r]) want = r;
    413     for (int q = 0; q < g->o.np; q++)
    414         if (q != p && !g->p[q].neutral && g->p[q].res[want] && game_vp_public(g, q) < 8) mask |= 1 << q;
    415     if (!mask) return false;
    416     memset(m, 0, sizeof *m);
    417     m->p = (int8_t)p;
    418     m->type = M_OFFER;
    419     m->a = (int16_t)mask;
    420     memcpy(m->r, give, NRES);
    421     memcpy(m->r2, get, NRES);
    422     return game_check(g, m) == E_OK;
    423 }
    424 
    425 /* ¿falta que alguien conteste la oferta de p? */
    426 bool bot_offer_pending(const Game *g, int p)
    427 {
    428     if (!g->offer.active || g->offer.from != p) return false;
    429     for (int q = 0; q < g->o.np; q++)
    430         if (q != p && ((g->offer.tomask >> q) & 1) && !g->p[q].neutral && g->offer.resp[q] == 0) return true;
    431     return false;
    432 }
    433 
    434 /* Cierra la oferta de p con la mejor respuesta (aceptacion o contraoferta) o la cancela. */
    435 bool bot_close_offer(const Game *g, int p, Move *m)
    436 {
    437     const Offer *o = &g->offer;
    438     int best = -1, bv = 0;
    439     for (int q = 0; q < g->o.np; q++) {
    440         int v;
    441         if (o->resp[q] == 1) v = trade_value(g, p, o->give, o->get);
    442         else if (o->resp[q] == 2) v = trade_value(g, p, o->cget[q], o->cgive[q]);
    443         else continue;
    444         Move t = { .type = M_CONFIRM, .p = (int8_t)p, .a = (int16_t)q };
    445         if (v > bv && game_check(g, &t) == E_OK) { bv = v; best = q; }
    446     }
    447     memset(m, 0, sizeof *m);
    448     m->p = (int8_t)p;
    449     m->type = best >= 0 ? M_CONFIRM : M_CANCEL;
    450     m->a = (int16_t)(best >= 0 ? best : 0);
    451     return true;
    452 }
    453 
    454 static bool neutral_build(const Game *g, int p, Rng *rng, Move *m)
    455 {
    456     int rival = game_rival(g, p);
    457     m->type = M_NEUTRAL;
    458     if (g->neutral_need == NEED_SETTLE && game_neutral_possible(g, NEED_SETTLE)) {
    459         int bs = -1, prod[NRES];
    460         if (rival >= 0) production(g, rival, prod);
    461         for (int n = 0; n < g->o.np; n++) {
    462             if (!g->p[n].neutral || !g->p[n].settles) continue;
    463             for (int v = 0; v < game_topo(g)->nvert; v++)
    464                 if (game_can_settle(g, n, v, false)) {
    465                     int s = rival >= 0 ? vert_value_with(g, prod, v) : 0;   /* bloquear al rival */
    466                     if (s > bs) { bs = s; m->a = (int16_t)n; m->b = 1; m->c = (int16_t)v; }
    467                 }
    468         }
    469         return bs >= 0;
    470     }
    471     for (int n = 0; n < g->o.np; n++) {
    472         if (!g->p[n].neutral || !g->p[n].roads) continue;
    473         int e = any_legal_road(g, n, rng);
    474         if (e >= 0) { m->a = (int16_t)n; m->b = 0; m->c = (int16_t)e; return true; }
    475     }
    476     return false;
    477 }
    478 
    479 static bool main_turn(const Game *g, int p, Rng *rng, Move *m)
    480 {
    481     const Player *pl = &g->p[p];
    482     uint8_t cost[NRES];
    483 
    484     if (g->offer.active && g->offer.from == p) {
    485         if (bot_offer_pending(g, p)) return false;        /* esperando respuestas */
    486         return bot_close_offer(g, p, m);
    487     }
    488 
    489     if (!g->dev_played && g->phase == PH_MAIN) {        /* en el turno de pareja no hay cartas */
    490         if (pl->dev[D_KNIGHT] && (bot_robber_on_me(g, p) ||
    491             (pl->knights + 1 >= 3 && (g->army < 0 || (g->army != p && pl->knights + 1 > g->p[g->army].knights))))) {
    492             m->type = M_KNIGHT; return true;
    493         }
    494         if (pl->dev[D_MONO]) {
    495             int best = -1, bn = 3;
    496             for (int r = 0; r < NRES; r++) {
    497                 int n = 0;
    498                 for (int q = 0; q < g->o.np; q++) if (q != p) n += g->p[q].res[r];
    499                 if (n > bn) { bn = n; best = r; }
    500             }
    501             if (best >= 0) { m->type = M_MONO; m->a = (int16_t)best; return true; }
    502         }
    503         if (pl->dev[D_PLENTY]) {
    504             bot_goal(g, p, cost);
    505             int a = -1, b = -1;
    506             uint8_t have[NRES];
    507             memcpy(have, pl->res, NRES);
    508             for (int k = 0; k < 2; k++)
    509                 for (int r = 0; r < NRES; r++)
    510                     if (have[r] < cost[r] && g->bank[r] > (a == r ? 1 : 0)) {
    511                         if (a < 0) a = r; else b = r;
    512                         have[r]++;
    513                         break;
    514                     }
    515             if (a >= 0) {
    516                 if (b < 0) b = a == GRAIN ? ORE : GRAIN;
    517                 m->type = M_PLENTY; m->a = (int16_t)a; m->b = (int16_t)b;
    518                 if (game_check(g, m) == E_OK) return true;
    519             }
    520         }
    521         if (pl->dev[D_ROADS] && pl->roads >= 2 && road_toward_spot(g, p) >= 0) { m->type = M_ROADS; return true; }
    522     }
    523 
    524     if (pl->cities && game_can_afford(g, p, COST_CITY)) {
    525         int best = -1, bs = -1;
    526         for (int v = 0; v < game_topo(g)->nvert; v++)
    527             if (game_can_city(g, p, v)) {
    528                 int s = vert_value(g, p, v);
    529                 if (s > bs) { bs = s; best = v; }
    530             }
    531         if (best >= 0) { m->type = M_CITY; m->a = (int16_t)best; return true; }
    532     }
    533     if (pl->settles && game_can_afford(g, p, COST_SETTLE)) {
    534         int v = best_settle_spot(g, p, false);
    535         if (v >= 0) { m->type = M_SETTLE; m->a = (int16_t)v; return true; }
    536     }
    537     int kind = bot_goal(g, p, cost);
    538     if (kind == 3 && game_can_afford(g, p, COST_ROAD)) {
    539         int e = road_toward_spot(g, p);
    540         if (e >= 0) { m->type = M_ROAD; m->a = (int16_t)e; return true; }
    541     }
    542     if (kind == 4 && game_can_afford(g, p, COST_SHIP)) {
    543         int e = ship_toward_spot(g, p);
    544         if (e >= 0) { m->type = M_SHIP; m->a = (int16_t)e; return true; }
    545     }
    546     if (g->deck_n && game_can_afford(g, p, COST_DEV) && (kind == 2 || hand_size(g, p) > 7)) {
    547         m->type = M_BUYDEV; return true;
    548     }
    549     if (g->o.variant2p && bot_robber_on_me(g, p) && pl->tokens >= game_token_cost(g, p) &&
    550         g->terrain[g->robber] != T_DESERT) {
    551         m->type = M_TOKROBBER; return true;
    552     }
    553     if (bot_offer(g, p, m)) return true;
    554     if (bank_toward(g, p, cost, m)) return true;
    555     (void)rng;
    556     m->type = M_END;
    557     return true;
    558 }
    559 
    560 /* el oro: lo que falta para el objetivo */
    561 static bool pick_gold(const Game *g, int seat, Move *m)
    562 {
    563     const Player *pl = &g->p[seat];
    564     uint8_t cost[NRES], have[NRES];
    565     bot_goal(g, seat, cost);
    566     memcpy(have, pl->res, NRES);
    567     m->type = M_GOLD;
    568     for (int i = 0; i < pl->gold; i++) {
    569         int best = -1, bs = -100;
    570         for (int r = 0; r < NRES; r++) {
    571             if (m->r[r] >= g->bank[r]) continue;
    572             int s = (have[r] < cost[r] ? 10 : 0) - have[r];
    573             if (s > bs) { bs = s; best = r; }
    574         }
    575         if (best < 0) break;
    576         m->r[best]++;
    577         have[best]++;
    578     }
    579     return true;
    580 }
    581 
    582 bool bot_decide(const Game *g, int seat, Rng *rng, Move *m)
    583 {
    584     memset(m, 0, sizeof *m);
    585     m->p = (int8_t)seat;
    586     if (!game_needs(g, seat)) return false;
    587     const Player *pl = &g->p[seat];
    588 
    589     if (g->phase == PH_DISCARD) {
    590         m->type = M_DISCARD;
    591         bot_pick_discard(g, seat, pl->discard, m->r);
    592         return true;
    593     }
    594     if (g->phase == PH_GOLD) return pick_gold(g, seat, m);   /* le toca a cualquiera, no solo al del turno */
    595     if (seat != g->cur) return respond_offer(g, seat, m);
    596 
    597     switch (g->phase) {
    598     case PH_SETUP:
    599         if (g->setup_sub == 0) {
    600             m->type = M_SETTLE;
    601             m->a = (int16_t)best_settle_spot(g, seat, true);
    602             return m->a >= 0;
    603         } else {
    604             const Topo *t = game_topo(g);
    605             int best = -1, bs = -1000;
    606             for (int k = 0; k < 3; k++) {
    607                 int e = t->vert_edge[g->setup_v][k];
    608                 Move tm = { .type = M_ROAD, .p = (int8_t)seat, .a = (int16_t)e };
    609                 if (e < 0 || g->eown[e] >= 0 || game_check(g, &tm) != E_OK) continue;
    610                 int u = t->edge_v[e][0] == g->setup_v ? t->edge_v[e][1] : t->edge_v[e][0];
    611                 int s = 0;
    612                 for (int j = 0; j < 3; j++) {
    613                     int w = t->vert_adj[u][j];
    614                     if (w >= 0 && w != g->setup_v && game_can_settle(g, seat, w, true)) {
    615                         int val = vert_value(g, seat, w);
    616                         if (val > s) s = val;
    617                     }
    618                 }
    619                 if (s > bs) { bs = s; best = e; }
    620             }
    621             m->type = M_ROAD;
    622             m->a = (int16_t)best;
    623             return best >= 0;
    624         }
    625     case PH_ROLL:
    626         if (pl->dev[D_KNIGHT] && !g->dev_played && bot_robber_on_me(g, seat)) { m->type = M_KNIGHT; return true; }
    627         if (g->o.variant2p && bot_robber_on_me(g, seat) && pl->tokens >= game_token_cost(g, seat) &&
    628             g->terrain[g->robber] != T_DESERT) { m->type = M_TOKROBBER; return true; }
    629         m->type = M_ROLL;
    630         return true;
    631     case PH_ROBBER:
    632         pick_robber(g, seat, m);
    633         return m->a >= 0;
    634     case PH_ROADBUILD: {
    635         int e = road_toward_spot(g, seat);
    636         if (e < 0 && (e = ship_toward_spot(g, seat)) >= 0) { m->type = M_SHIP; m->a = (int16_t)e; return true; }
    637         if (e < 0) e = any_legal_road(g, seat, rng);
    638         if (e < 0) { m->type = M_END; return true; }
    639         m->type = M_ROAD; m->a = (int16_t)e;
    640         return true;
    641     }
    642 
    643     case PH_NEUTRAL:
    644         return neutral_build(g, seat, rng, m);
    645     case PH_GIVEBACK:
    646         m->type = M_GIVEBACK;
    647         bot_pick_discard(g, seat, g->give_back, m->r);
    648         return true;
    649     case PH_MAIN: case PH_PAIRED:            /* pareja: lo que no vale lo filtra game_check */
    650         return main_turn(g, seat, rng, m);
    651     }
    652     return false;
    653 }