export.c (10248B)
1 /* export.c - la partida como texto legible (ver export.h). */ 2 #include <stdarg.h> 3 #include <stdio.h> 4 #include <string.h> 5 #include <time.h> 6 #include "export.h" 7 #include "../core/ctrl.h" 8 9 static const char *const COLOR_ES[] = { "rojo", "azul", "verde", "naranja", "blanco", "marrón" }; 10 static const char *const COLOR_EN[] = { "red", "blue", "green", "orange", "white", "brown" }; 11 12 static void putf(ExportPut put, void *ctx, const char *fmt, ...) __attribute__((format(printf, 3, 4))); 13 14 static void putf(ExportPut put, void *ctx, const char *fmt, ...) 15 { 16 char line[1024]; 17 va_list ap; 18 va_start(ap, fmt); 19 vsnprintf(line, sizeof line, fmt, ap); 20 va_end(ap); 21 put(ctx, line); 22 } 23 24 static void hex_name(const Game *g, int h, char *out, size_t n) 25 { 26 if (g->terrain[h] == T_SEA) snprintf(out, n, "~"); 27 else if (g->terrain[h] == T_DESERT) snprintf(out, n, "%s", terrain_name(T_DESERT)); 28 else snprintf(out, n, "%s %d", terrain_name(g->terrain[h]), g->num[h]); 29 } 30 31 static void port_name(const Game *g, int i, char *out, size_t n) 32 { 33 if (g->port[i] == PORT_ANY) snprintf(out, n, "3:1"); 34 else snprintf(out, n, "2:1 %s", res_name(g->port[i])); 35 } 36 37 /* " (junto a Bosque 8, Campo 5; puerto 3:1)" */ 38 static void where_vert(const Game *g, int v, char *out, size_t n) 39 { 40 const Topo *t = game_topo(g); 41 int k = snprintf(out, n, " (%s ", g_lang ? "next to" : "junto a"); 42 bool first = true; 43 for (int i = 0; i < 3; i++) { 44 int h = t->vert_hex[v][i]; 45 if (h < 0 || k >= (int)n) continue; 46 char hn[40]; 47 hex_name(g, h, hn, sizeof hn); 48 k += snprintf(out + k, n - (size_t)k, "%s%s", first ? "" : ", ", hn); 49 first = false; 50 } 51 if (t->vert_port[v] >= 0 && k < (int)n) { 52 char pn[24]; 53 port_name(g, t->vert_port[v], pn, sizeof pn); 54 k += snprintf(out + k, n - (size_t)k, "; %s %s", g_lang ? "port" : "puerto", pn); 55 } 56 if (k < (int)n) snprintf(out + k, n - (size_t)k, ")"); 57 } 58 59 /* " (entre Bosque 8 y Campo 5)" o " (costa de Bosque 8)" */ 60 static void where_edge(const Game *g, int e, char *out, size_t n) 61 { 62 const Topo *t = game_topo(g); 63 char a[40], b[40]; 64 hex_name(g, t->edge_hex[e][0], a, sizeof a); 65 if (t->edge_hex[e][1] >= 0) { 66 hex_name(g, t->edge_hex[e][1], b, sizeof b); 67 snprintf(out, n, " (%s %s %s %s)", g_lang ? "between" : "entre", a, g_lang ? "and" : "y", b); 68 } else { 69 snprintf(out, n, " (%s %s)", g_lang ? "coast of" : "costa de", a); 70 } 71 } 72 73 void export_header(const Game *g, const char *code, const char *const names[MAXP], const char *kinds, 74 ExportPut put, void *ctx) 75 { 76 bool en = g_lang; 77 const Opts *o = &g->o; 78 char buf[512]; 79 time_t now = time(0); 80 char when[32]; 81 strftime(when, sizeof when, "%Y-%m-%d %H:%M", localtime(&now)); 82 putf(put, ctx, "# Catan%s%s", code ? " — " : "", code ? code : ""); 83 putf(put, ctx, "[%s \"%s\"]", en ? "Exported" : "Exportada", when); 84 int k = 0; 85 buf[0] = 0; 86 for (int p = 0; p < o->np && k < (int)sizeof buf; p++) { 87 const char *kind = g->p[p].neutral ? (en ? ", neutral" : ", neutral") 88 : kinds && kinds[p] == 'B' ? ", bot" : ""; 89 k += snprintf(buf + k, sizeof buf - (size_t)k, "%s%d %s (%s%s)", p ? " · " : "", p + 1, 90 names[p] ? names[p] : "?", (en ? COLOR_EN : COLOR_ES)[p], kind); 91 } 92 putf(put, ctx, "[%s \"%s\"]", en ? "Players" : "Jugadores", buf); 93 static const char *const SCEN_ES[] = { "", "Navegantes: Nuevas costas", "Navegantes: Cuatro islas", "Navegantes: Archipiélago" }; 94 static const char *const SCEN_EN[] = { "", "Seafarers: New shores", "Seafarers: Four islands", "Seafarers: Archipelago" }; 95 int fam = game_topo(g)->family; 96 char kind[96]; 97 if (o->scenario && o->scenario < SCEN__COUNT) 98 snprintf(kind, sizeof kind, "%s, %s %s", (en ? SCEN_EN : SCEN_ES)[o->scenario], en ? "map" : "mapa", board_name(o->board)); 99 else if (fam == BF_LARGE) 100 snprintf(kind, sizeof kind, "%s, %s %s", en ? "5-6 board, paired players" : "tablero 5-6, jugadores en pareja", 101 en ? "map" : "mapa", board_name(o->board)); 102 else if (o->beginner) 103 snprintf(kind, sizeof kind, "%s", en ? "beginner board" : "tablero de principiante"); 104 else 105 snprintf(kind, sizeof kind, "%s %s", en ? "random board, map" : "tablero al azar, mapa", board_name(o->board)); 106 snprintf(buf, sizeof buf, "%d %s · %s %d %s · %s%s%s", 107 o->np, en ? "seats" : "asientos", en ? "to" : "a", o->vp_target, en ? "points" : "puntos", kind, 108 o->variant2p ? (en ? " · 2-player variant" : " · variante para 2") : "", 109 o->freeplay ? (en ? " · free play" : " · juego libre") : ""); 110 putf(put, ctx, "[%s \"%s\"]", en ? "Options" : "Opciones", buf); 111 if (g->winner >= 0) 112 snprintf(buf, sizeof buf, "%s %s %s %d %s, %s %d", en ? "over:" : "terminada:", names[g->winner], 113 en ? "won with" : "ganó con", game_vp(g, g->winner), tr(S_VP), en ? "turn" : "turno", g->turn + 1); 114 else 115 snprintf(buf, sizeof buf, "%s %d", en ? "in play, turn" : "en juego, turno", g->turn + 1); 116 putf(put, ctx, "[%s \"%s\"]", en ? "State" : "Estado", buf); 117 118 /* tablero por filas */ 119 const Topo *t = game_topo(g); 120 putf(put, ctx, "%s", ""); 121 putf(put, ctx, "## %s", en ? "Board" : "Tablero"); 122 int row = -100; 123 k = 0; 124 buf[0] = 0; 125 for (int h = 0; h <= t->nhex; h++) { 126 if (h == t->nhex || t->r[h] != row) { 127 if (buf[0]) putf(put, ctx, "%s", buf); 128 if (h == t->nhex) break; 129 row = t->r[h]; 130 k = snprintf(buf, sizeof buf, "%s %d:", en ? "Row" : "Fila", row - t->r[0] + 1); 131 } 132 char hn[40]; 133 hex_name(g, h, hn, sizeof hn); 134 if (k < (int)sizeof buf) 135 k += snprintf(buf + k, sizeof buf - (size_t)k, " %s%s", hn, h == g->robber ? "*" : h == g->pirate ? "P" : ""); 136 } 137 k = snprintf(buf, sizeof buf, "%s:", en ? "Ports" : "Puertos"); 138 for (int i = 0; i < t->nport && k < (int)sizeof buf; i++) { 139 char pn[24], hn[40]; 140 port_name(g, i, pn, sizeof pn); 141 int e = t->port_edge[i], h = t->edge_hex[e][0]; 142 if (h >= 0 && !ter_land(g->terrain[h]) && t->edge_hex[e][1] >= 0) h = t->edge_hex[e][1]; /* el de tierra */ 143 hex_name(g, h, hn, sizeof hn); 144 k += snprintf(buf + k, sizeof buf - (size_t)k, "%s %s (%s %s)", i ? "," : "", pn, en ? "by" : "junto a", hn); 145 } 146 putf(put, ctx, "%s", buf); 147 putf(put, ctx, "(* = %s%s)", en ? "robber" : "ladrón", g->pirate < t->nhex ? (en ? ", P = pirate" : ", P = pirata") : ""); 148 } 149 150 void export_game(const char *hdr, const char *const *moves, int nmoves, const char *code, 151 const char *const names[MAXP], const char *kinds, unsigned viewers, ExportPut put, void *ctx) 152 { 153 static Game g, end; 154 static Undo u; 155 bool en = g_lang; 156 if (!hdr_parse(hdr, &end)) return; 157 u.n = 0; 158 for (int i = 0; i < nmoves; i++) { /* el estado final, para la cabecera */ 159 Move m; 160 if (move_parse(moves[i], &m)) game_apply_u(&end, &u, &m); 161 } 162 export_header(&end, code, names, kinds, put, ctx); /* estado y ladron de ahora */ 163 hdr_parse(hdr, &g); 164 165 putf(put, ctx, "%s", ""); 166 putf(put, ctx, "## %s", en ? "Game" : "Partida"); 167 putf(put, ctx, "### %s", en ? "Initial placement" : "Colocación inicial"); 168 u.n = 0; 169 int turn = -1; 170 for (int i = 0; i < nmoves; i++) { 171 Move m; 172 if (!move_parse(moves[i], &m)) continue; 173 if (g.phase != PH_SETUP && g.turn != turn && m.type == M_ROLL) { 174 turn = g.turn; 175 putf(put, ctx, "### %s %d — %s", en ? "Turn" : "Turno", g.turn + 1, names[g.cur] ? names[g.cur] : "?"); 176 } 177 uint8_t before[MAXP][NRES]; 178 for (int p = 0; p < MAXP; p++) memcpy(before[p], g.p[p].res, NRES); 179 game_apply_u(&g, &u, &m); 180 char line[400], where[160] = ""; 181 move_describe_for(&g, &m, names, viewers, line, sizeof line); 182 if (m.type == M_SETTLE || m.type == M_CITY) where_vert(&g, m.a, where, sizeof where); 183 else if (m.type == M_ROAD || m.type == M_SHIP) where_edge(&g, m.a, where, sizeof where); 184 else if (m.type == M_NEUTRAL) { 185 if (m.b == 1) where_vert(&g, m.c, where, sizeof where); 186 else where_edge(&g, m.c, where, sizeof where); 187 } 188 putf(put, ctx, " %s%s", line, where); 189 if (m.type == M_END && g.phase == PH_PAIRED) 190 putf(put, ctx, " %s %s", names[g.cur] ? names[g.cur] : "?", tr(S_PLAYS_PAIRED)); 191 if (m.type == M_ROLL) { /* que produjo la tirada */ 192 char got[400]; 193 int k = 0; 194 got[0] = 0; 195 for (int p = 0; p < g.o.np && k < (int)sizeof got; p++) 196 for (int r = 0; r < NRES; r++) { 197 int d = g.p[p].res[r] - before[p][r]; 198 if (d > 0 && k < (int)sizeof got) 199 k += snprintf(got + k, sizeof got - (size_t)k, "%s%s +%d %s", k ? ", " : "", names[p], d, res_name(r)); 200 } 201 if (got[0]) putf(put, ctx, " → %s", got); 202 } 203 } 204 205 if (end.winner >= 0) { 206 putf(put, ctx, "%s", ""); 207 putf(put, ctx, "## %s", en ? "Final score" : "Puntos al final"); 208 for (int p = 0; p < end.o.np; p++) { 209 const Player *pl = &end.p[p]; 210 if (pl->neutral) continue; 211 putf(put, ctx, " %s: %d %s (%d %s, %d %s%s%s, %d %s)", names[p], game_vp(&end, p), tr(S_VP), 212 MAX_SETTLES - pl->settles, en ? "settlements" : "pueblos", MAX_CITIES - pl->cities, 213 en ? "cities" : "ciudades", end.longest == p ? (en ? ", longest road" : ", camino más largo") : "", 214 end.army == p ? (en ? ", largest army" : ", ejército más grande") : "", 215 pl->dev[D_VP] + pl->dev_new[D_VP], en ? "VP cards" : "cartas de PV"); 216 } 217 } 218 219 putf(put, ctx, "%s", ""); 220 putf(put, ctx, "## %s", en ? "Moves (game format)" : "Movidas (formato del juego)"); 221 putf(put, ctx, "%s", hdr); 222 for (int i = 0; i < nmoves; i++) putf(put, ctx, "%s", moves[i]); 223 }