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 }