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

chess_game.c (7687B)


      1 /* chess_game.c - el ajedrez como GameModule del servidor de comun/: el servidor
      2  * es la autoridad, lleva el reloj y hace jugar a los bots. */
      3 #include <stdio.h>
      4 #include <string.h>
      5 #include "server.h"
      6 
      7 typedef struct {
      8     Game      g;
      9     int       wseat;             /* asiento que juega con blancas */
     10     bool      bot[2];            /* por color */
     11     int       level;
     12     long long turn_start;
     13     long long next_bot;
     14     SrvEmit   emit;
     15     void     *ctx;
     16 } ChessSt;
     17 
     18 /* un solo motor para todas las partidas del servidor (se usa de a una) */
     19 static TTEntry tt[1 << 18];
     20 static Engine eng;
     21 static bool eng_ready;
     22 
     23 static uint32_t now32(void) { return (uint32_t)srv_now_ms(); }
     24 
     25 static int color_of(const ChessSt *c, int seat) { return seat == c->wseat ? WHITE : BLACK; }
     26 
     27 static void emit(ChessSt *c, const Act *a)
     28 {
     29     char line[64];
     30     act_fmt(a, line, sizeof line);
     31     c->emit(c->ctx, line, false);      /* todo va al log: los bots dependen del tiempo */
     32 }
     33 
     34 static void apply(ChessSt *c, const Act *a, long long now)
     35 {
     36     int before = c->g.nply;
     37     game_apply(&c->g, a);
     38     emit(c, a);
     39     if (c->g.nply != before) { c->turn_start = now; c->next_bot = now + 300; }
     40 }
     41 
     42 /* el bot contesta: tablas solo si viene perdiendo, deshacer siempre */
     43 static void bot_answer(ChessSt *c, long long now)
     44 {
     45     Game *g = &c->g;
     46     if (g->draw_offer && g->result == R_NONE && c->bot[(g->draw_offer - 1) ^ 1]) {
     47         int to = (g->draw_offer - 1) ^ 1;
     48         int ev = evaluate(&g->pos);
     49         if (g->pos.side != to) ev = -ev;
     50         Act a = { (uint8_t)(ev < -300 || pos_insufficient(&g->pos) ? ACT_DRAWOK : ACT_DRAWNO), (int8_t)to, 0, 0 };
     51         apply(c, &a, now);
     52     }
     53     if (g->undo_req && c->bot[(g->undo_req - 1) ^ 1]) {
     54         Act a = { ACT_UNDOOK, (int8_t)((g->undo_req - 1) ^ 1), 0, 0 };
     55         apply(c, &a, now);
     56     }
     57 }
     58 
     59 static int cg_create(const char *args, char *opts, int optsn, char *seats)
     60 {
     61     int color, base, inc, per, undo, level;
     62     char st[8];
     63     if (sscanf(args, "%d %d %d %d %d %d %7s", &color, &base, &inc, &per, &undo, &level, st) != 7) return -1;
     64     if (color < 0 || color > 2 || base < 0 || base > 3 * 3600000 || inc < 0 || inc > 60000 ||
     65         per < 0 || per > 14 * 86400000 || (base && per) || level < 0 || level >= LEVEL_COUNT || strlen(st) != 2 ||
     66         (st[1] != 'H' && st[1] != 'B'))
     67         return -1;
     68     snprintf(opts, (size_t)optsn, "%d %d %d %d %d %d", color, base, inc, per, undo != 0, level);
     69     memcpy(seats, st, 3);
     70     return 2;
     71 }
     72 
     73 static bool cg_start(void *vst, const char *opts, uint64_t seed, char *kinds, char (*names)[20], SrvEmit em, void *ctx)
     74 {
     75     ChessSt *c = vst;
     76     int color, base, inc, per, undo, level;
     77     if (sscanf(opts, "%d %d %d %d %d %d", &color, &base, &inc, &per, &undo, &level) != 6) return false;
     78     memset(c, 0, sizeof *c);
     79     c->emit = em;
     80     c->ctx = ctx;
     81     c->wseat = color == 2 ? (int)(seed & 1) : color == 0 ? 0 : 1;
     82     c->level = level;
     83     TimeCtl tc = { base, inc, per };
     84     game_init(&c->g, 0, &tc, undo != 0);
     85     for (int seat = 0; seat < 2; seat++) {
     86         c->bot[color_of(c, seat)] = kinds[seat] == 'B';
     87         if (kinds[seat] == 'B') snprintf(names[seat], 20, "Bot (%d)", level + 1);
     88     }
     89     c->turn_start = srv_now_ms();
     90     c->next_bot = c->turn_start + 300;
     91     if (!eng_ready) { engine_init(&eng, tt, 1 << 18, now32); eng_ready = true; }
     92     return true;
     93 }
     94 
     95 static int cg_submit(void *vst, int seat, const char *line)
     96 {
     97     ChessSt *c = vst;
     98     Act a;
     99     if (!act_parse(line, &a) || a.type == ACT_FLAG) return E_ARGS;
    100     if (a.c != color_of(c, seat)) return E_TURN;
    101     if (a.type == ACT_MOVE && a.c != c->g.pos.side) return c->g.result != R_NONE ? E_OVER : E_TURN;
    102     if (!act_resolve(&c->g, &a)) return E_ILLEGAL;
    103     long long now = srv_now_ms();
    104     if (a.type == ACT_MOVE) a.ms = (int32_t)(now - c->turn_start);
    105     int e = game_check(&c->g, &a);
    106     if (e) return e;
    107     apply(c, &a, now);
    108     bot_answer(c, now);
    109     return 0;
    110 }
    111 
    112 static int cg_replay(void *vst, const char *line)
    113 {
    114     ChessSt *c = vst;
    115     Act a;
    116     if (!act_parse(line, &a)) return 0;
    117     if (!act_resolve(&c->g, &a)) return E_ILLEGAL;
    118     int e = game_check(&c->g, &a);
    119     if (e) return e;
    120     apply(c, &a, srv_now_ms());
    121     return 0;
    122 }
    123 
    124 static void cg_redact(const char *line, int viewer, char *out, int n)
    125 {
    126     (void)viewer;                      /* en el ajedrez no hay nada oculto */
    127     snprintf(out, (size_t)n, "%s", line);
    128 }
    129 
    130 static void cg_header(const void *vst, char *out, int n)
    131 {
    132     const ChessSt *c = vst;
    133     char h[160];
    134     game_hdr_fmt(&c->g, h, sizeof h);
    135     snprintf(out, (size_t)n, "S %d %lld %s", c->wseat, srv_now_ms() - c->turn_start, h);
    136 }
    137 
    138 static void cg_status(const void *vst, int seat, char *out, int n)
    139 {
    140     const ChessSt *c = vst;
    141     const Game *g = &c->g;
    142     int me = color_of(c, seat);
    143     bool needs = g->result == R_NONE &&
    144                  (g->pos.side == me || (g->draw_offer && g->draw_offer - 1 != me) || (g->undo_req && g->undo_req - 1 != me));
    145     snprintf(out, (size_t)n, "%d %d %d %d", g->nply, needs ? 1 : 0, g->result, me);
    146 }
    147 
    148 static bool cg_is_over(const void *vst) { return ((const ChessSt *)vst)->g.result != R_NONE; }
    149 
    150 static long long cg_next_wake(const void *vst)
    151 {
    152     const ChessSt *c = vst;
    153     const Game *g = &c->g;
    154     if (g->result != R_NONE) return -1;
    155     long long t = -1;
    156     if (game_has_clock(g)) t = c->turn_start + game_clock(g, g->pos.side) + 1;
    157     if (c->bot[g->pos.side] && (t < 0 || c->next_bot < t)) t = c->next_bot;
    158     return t;
    159 }
    160 
    161 static void cg_wake(void *vst, long long now)
    162 {
    163     ChessSt *c = vst;
    164     Game *g = &c->g;
    165     if (g->result != R_NONE) return;
    166     if (game_has_clock(g) && game_clock(g, g->pos.side) - (now - c->turn_start) <= 0) {
    167         Act a = { ACT_FLAG, (int8_t)g->pos.side, 0, (int32_t)(now - c->turn_start) };
    168         apply(c, &a, now);
    169         return;
    170     }
    171     if (c->bot[g->pos.side] && now >= c->next_bot) {
    172         SearchLimits lim;
    173         level_limits(c->level, &lim);
    174         if (!lim.movetime || lim.movetime > 800) lim.movetime = 800;    /* el servidor no se puede colgar pensando */
    175         if (game_has_clock(g)) {
    176             int32_t left = game_clock(g, g->pos.side) - (int32_t)(now - c->turn_start);
    177             int32_t budget = g->tc.per_move > 0 ? left / 4 : left / 30 + g->tc.inc / 2;
    178             if (budget < 50) budget = 50;
    179             if (lim.movetime > budget) lim.movetime = budget;
    180         }
    181         SearchResult r;
    182         engine_search(&eng, g, &lim, &r);
    183         long long t2 = srv_now_ms();
    184         Act a = { ACT_MOVE, (int8_t)g->pos.side, r.best, (int32_t)(t2 - c->turn_start) };
    185         if (r.best && game_check(g, &a) == E_OK) apply(c, &a, t2);
    186         else c->next_bot = t2 + 1000;
    187     }
    188 }
    189 
    190 static void cg_summary(const void *vst, char *out, int n)
    191 {
    192     const ChessSt *c = vst;
    193     const TimeCtl *tc = &c->g.tc;
    194     if (tc->per_move) snprintf(out, (size_t)n, "correspondencia, %d dias por jugada%s", tc->per_move / 86400000, c->bot[0] || c->bot[1] ? ", contra bot" : "");
    195     else if (tc->base) snprintf(out, (size_t)n, "%ds+%ds%s", tc->base / 1000, tc->inc / 1000, c->bot[0] || c->bot[1] ? ", contra bot" : "");
    196     else snprintf(out, (size_t)n, "sin reloj%s", c->bot[0] || c->bot[1] ? ", contra bot" : "");
    197 }
    198 
    199 const GameModule chess_module = {
    200     .magic = "AJEDREZ-GAME 1",
    201     .state_size = sizeof(ChessSt),
    202     .err_args = E_ARGS,
    203     .err_turn = E_TURN,
    204     .create = cg_create,
    205     .start = cg_start,
    206     .submit = cg_submit,
    207     .replay = cg_replay,
    208     .redact = cg_redact,
    209     .header = cg_header,
    210     .status = cg_status,
    211     .is_over = cg_is_over,
    212     .next_wake = cg_next_wake,
    213     .wake = cg_wake,
    214     .summary = cg_summary,
    215 };