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

move.c (8103B)


      1 /* move.c - movidas y cabecera de partida <-> texto. Sin stdio.
      2  *
      3  * Formato de movida:  "<asiento> <NOMBRE> args..."   ej. "0 ROAD 41", "1 ROLL 3 4 0 0"
      4  * Cabecera:           "G <7 opciones> <terrenos> <numeros> <puertos>": 19/19/9 con el
      5  *                     tablero clasico, 30/30/11 con el grande (mas de 4 jugadores)  */
      6 #include <string.h>
      7 #include "catan.h"
      8 
      9 /* Por tipo: nombre y argumentos (a b c = campos, 4 = r[0..3], R = r[], S = r2[]) */
     10 static const struct { const char *name, *args; } SPEC[M__COUNT] = {
     11     [M_NONE] = { "NONE", "" },       [M_SETTLE] = { "SETTLE", "a" },
     12     [M_ROAD] = { "ROAD", "a" },      [M_CITY] = { "CITY", "a" },
     13     [M_ROLL] = { "ROLL", "4" },      [M_DISCARD] = { "DISCARD", "R" },
     14     [M_ROBBER] = { "ROBBER", "abc" },[M_BUYDEV] = { "BUYDEV", "a" },
     15     [M_KNIGHT] = { "KNIGHT", "" },   [M_ROADS] = { "ROADS", "" },
     16     [M_PLENTY] = { "PLENTY", "ab" }, [M_MONO] = { "MONO", "a" },
     17     [M_BANK] = { "BANK", "abc" },    [M_OFFER] = { "OFFER", "aRS" },
     18     [M_ACCEPT] = { "ACCEPT", "" },   [M_REJECT] = { "REJECT", "" },
     19     [M_CONFIRM] = { "CONFIRM", "a" },[M_CANCEL] = { "CANCEL", "" },
     20     [M_END] = { "END", "" },         [M_NEUTRAL] = { "NEUTRAL", "abc" },
     21     [M_TOKTRADE] = { "TOKTRADE", "ab" }, [M_GIVEBACK] = { "GIVEBACK", "R" },
     22     [M_TOKROBBER] = { "TOKROBBER", "" }, [M_WIN] = { "WIN", "a" },
     23     [M_UNDO] = { "UNDO", "" },       [M_COUNTER] = { "COUNTER", "RS" },
     24     [M_SHIP] = { "SHIP", "a" },      [M_MOVESHIP] = { "MOVESHIP", "ab" },
     25     [M_GOLD] = { "GOLD", "R" },
     26 };
     27 
     28 const char *move_name(int type)
     29 {
     30     return (type >= 0 && type < M__COUNT) ? SPEC[type].name : "?";
     31 }
     32 
     33 static int put_sp_int(char *buf, int n, int pos, long v)
     34 {
     35     pos = str_put(buf, n, pos, " ");
     36     return str_int(buf, n, pos, v);
     37 }
     38 
     39 int move_fmt(const Move *m, char *buf, int n)
     40 {
     41     if (m->type >= M__COUNT) return -1;
     42     int pos = str_int(buf, n, 0, m->p);
     43     pos = str_put(buf, n, pos, " ");
     44     pos = str_put(buf, n, pos, SPEC[m->type].name);
     45     for (const char *a = SPEC[m->type].args; *a && pos >= 0; a++) {
     46         switch (*a) {
     47         case 'a': pos = put_sp_int(buf, n, pos, m->a); break;
     48         case 'b': pos = put_sp_int(buf, n, pos, m->b); break;
     49         case 'c': pos = put_sp_int(buf, n, pos, m->c); break;
     50         case '4': for (int i = 0; i < 4; i++) pos = put_sp_int(buf, n, pos, m->r[i]); break;
     51         case 'R': for (int i = 0; i < NRES; i++) pos = put_sp_int(buf, n, pos, m->r[i]); break;
     52         case 'S': for (int i = 0; i < NRES; i++) pos = put_sp_int(buf, n, pos, m->r2[i]); break;
     53         }
     54     }
     55     return pos;
     56 }
     57 
     58 static const char *scan_small(const char *s, long lo, long hi, long *v)
     59 {
     60     if (!s) return 0;
     61     s = str_scan_int(s, v);
     62     if (!s || *v < lo || *v > hi) return 0;
     63     return s;
     64 }
     65 
     66 bool move_parse(const char *s, Move *m)
     67 {
     68     long v;
     69     memset(m, 0, sizeof *m);
     70     s = scan_small(s, 0, MAXP - 1, &v);
     71     if (!s) return false;
     72     m->p = (int8_t)v;
     73     while (*s == ' ') s++;
     74     const char *w = s;
     75     while (*s && *s != ' ') s++;
     76     int len = (int)(s - w), type = -1;
     77     for (int t = 1; t < M__COUNT; t++)
     78         if ((int)strlen(SPEC[t].name) == len && !strncmp(SPEC[t].name, w, (size_t)len)) { type = t; break; }
     79     if (type < 0) return false;
     80     m->type = (uint8_t)type;
     81     for (const char *a = SPEC[type].args; *a; a++) {
     82         switch (*a) {
     83         case 'a': if (!(s = scan_small(s, -1, 999, &v))) return false; m->a = (int16_t)v; break;
     84         case 'b': if (!(s = scan_small(s, -1, 999, &v))) return false; m->b = (int16_t)v; break;
     85         case 'c': if (!(s = scan_small(s, -1, 999, &v))) return false; m->c = (int16_t)v; break;
     86         case '4':
     87             for (int i = 0; i < 4; i++) { if (!(s = scan_small(s, 0, 6, &v))) return false; m->r[i] = (uint8_t)v; }
     88             break;
     89         case 'R':
     90             for (int i = 0; i < NRES; i++) { if (!(s = scan_small(s, 0, 99, &v))) return false; m->r[i] = (uint8_t)v; }
     91             break;
     92         case 'S':
     93             for (int i = 0; i < NRES; i++) { if (!(s = scan_small(s, 0, 99, &v))) return false; m->r2[i] = (uint8_t)v; }
     94             break;
     95         }
     96     }
     97     while (*s == ' ' || *s == '\r' || *s == '\n') s++;
     98     return *s == 0;
     99 }
    100 
    101 void move_redact(const Move *m, int viewer, Move *out)
    102 {
    103     *out = *m;
    104     if (m->type == M_BUYDEV && m->p != viewer) out->a = -1;
    105 }
    106 
    107 void move_public(const Move *m, unsigned viewers, Move *out)
    108 {
    109     *out = *m;
    110     if (m->type == M_ROBBER && m->b >= 0) {
    111         bool sees = ((viewers >> m->p) & 1) || ((viewers >> m->b) & 1);
    112         if (!sees) out->c = -1;
    113     }
    114 }
    115 
    116 bool move_is_auto(const Move *m)
    117 {
    118     return m->type == M_WIN || (m->type == M_NEUTRAL && m->p == m->a);
    119 }
    120 
    121 /* ---------------------------------------------------------------- cabecera */
    122 
    123 int hdr_fmt(const Game *g, char *buf, int n)
    124 {
    125     const Opts *o = &g->o;
    126     /* con escenario: "S <escenario> ..." (el escenario define el mapa, va antes que el tablero);
    127      * con un mapa que no es el de siempre, antes "M <mapa> " */
    128     int pos = 0;
    129     if (o->map) { pos = str_put(buf, n, 0, "M"); pos = put_sp_int(buf, n, pos, o->map); pos = str_put(buf, n, pos, " "); }
    130     if (o->first && o->first < o->np) {      /* quien empieza, si no es el asiento 0: "F <asiento> " */
    131         pos = str_put(buf, n, pos, "F"); pos = put_sp_int(buf, n, pos, o->first); pos = str_put(buf, n, pos, " ");
    132     }
    133     pos = str_put(buf, n, pos, o->scenario ? "S" : "G");
    134     if (o->scenario) pos = put_sp_int(buf, n, pos, o->scenario);
    135     long ov[7] = { o->np, o->variant2p, o->vp_target, o->friendly_robber, o->tokens_start, o->neutral_setup, o->beginner };
    136     for (int i = 0; i < 7; i++) pos = put_sp_int(buf, n, pos, ov[i]);
    137     for (int h = 0; h < game_topo(g)->nhex; h++) pos = put_sp_int(buf, n, pos, g->terrain[h]);
    138     for (int h = 0; h < game_topo(g)->nhex; h++) pos = put_sp_int(buf, n, pos, g->num[h]);
    139     for (int i = 0; i < game_topo(g)->nport; i++) pos = put_sp_int(buf, n, pos, g->port[i]);
    140     if (o->setup_first || o->freeplay) {     /* opcionales, al final: compatible con cabeceras viejas */
    141         pos = put_sp_int(buf, n, pos, o->setup_first);
    142         pos = put_sp_int(buf, n, pos, o->freeplay);
    143     }
    144     return pos;
    145 }
    146 
    147 bool hdr_parse(const char *s, Game *g)
    148 {
    149     long v, ov[7], scen = 0, map = 0, first = 0;
    150     static const long HI[7] = { MAXP, 1, 30, 1, 20, 4, 1 };
    151     while (*s == ' ') s++;
    152     while (*s == 'M' || *s == 'F') {         /* prefijos opcionales: mapa y quien empieza */
    153         if (*s == 'M') { if (!(s = scan_small(s + 1, 0, NMAPS - 1, &map))) return false; }
    154         else if (!(s = scan_small(s + 1, 0, MAXP - 1, &first))) return false;
    155         while (*s == ' ') s++;
    156     }
    157     char tag = *s++;
    158     if (tag != 'G' && tag != 'S') return false;
    159     if (tag == 'S' && !(s = scan_small(s, 1, SCEN__COUNT - 1, &scen))) return false;
    160     for (int i = 0; i < 7; i++)
    161         if (!(s = scan_small(s, 0, HI[i], &ov[i]))) return false;
    162     if (ov[0] < 2) return false;
    163     Opts o;
    164     memset(&o, 0, sizeof o);                 /* tambien el relleno: Game se compara byte a byte en los tests */
    165     o.np = (uint8_t)ov[0]; o.variant2p = (uint8_t)ov[1]; o.vp_target = (uint8_t)ov[2]; o.friendly_robber = (uint8_t)ov[3];
    166     o.tokens_start = (uint8_t)ov[4]; o.neutral_setup = (uint8_t)ov[5]; o.beginner = (uint8_t)ov[6];
    167     o.scenario = (uint8_t)scen;
    168     o.map = (uint8_t)map;
    169     o.first = (uint8_t)first;
    170     const Topo *tp = topo_of(opts_board(&o));     /* el tablero sale de la cantidad de jugadores */
    171     memset(g, 0, sizeof *g);
    172     for (int h = 0; h < tp->nhex; h++) { if (!(s = scan_small(s, 0, T__COUNT - 1, &v))) return false; g->terrain[h] = (uint8_t)v; }
    173     for (int h = 0; h < tp->nhex; h++) { if (!(s = scan_small(s, 0, 12, &v))) return false; g->num[h] = (uint8_t)v; }
    174     for (int i = 0; i < tp->nport; i++) { if (!(s = scan_small(s, 0, PORT_ANY, &v))) return false; g->port[i] = (uint8_t)v; }
    175     const char *t = scan_small(s, 0, (1 << MAXP) - 1, &v);
    176     if (t) o.setup_first = (uint8_t)v;
    177     if (t && (t = scan_small(t, 0, 1, &v))) o.freeplay = (uint8_t)v;
    178     game_init(g, &o);
    179     return true;
    180 }