pgn.c (5530B)
1 /* pgn.c - exportar e importar partidas en PGN (sin stdio). */ 2 #include <string.h> 3 #include "chess.h" 4 5 static const char START_FEN[] = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; 6 7 typedef struct { char *b; int n, pos, col; bool full; } Out; 8 9 static void out_s(Out *o, const char *s) 10 { 11 int p = str_put(o->b, o->n, o->pos, s); 12 if (p < 0) { o->full = true; return; } 13 o->col += p - o->pos; 14 o->pos = p; 15 } 16 17 static void tag(Out *o, const char *name, const char *val) 18 { 19 out_s(o, "["); 20 out_s(o, name); 21 out_s(o, " \""); 22 out_s(o, val && *val ? val : "?"); 23 out_s(o, "\"]\n"); 24 } 25 26 /* token de la lista de jugadas, cortando lineas en ~79 columnas */ 27 static void word(Out *o, const char *w) 28 { 29 int l = (int)strlen(w); 30 if (o->col > 0 && o->col + 1 + l > 79) { out_s(o, "\n"); o->col = 0; } 31 else if (o->col > 0) out_s(o, " "); 32 out_s(o, w); 33 } 34 35 int game_pgn(const Game *g, const PgnTags *t, char *buf, int n) 36 { 37 Out o = { buf, n, 0, 0, false }; 38 buf[0] = 0; 39 PgnTags def; 40 memset(&def, 0, sizeof def); 41 if (!t) t = &def; 42 tag(&o, "Event", t->event[0] ? t->event : "Ajedrez"); 43 tag(&o, "Site", t->site); 44 tag(&o, "Date", t->date[0] ? t->date : "????.??.??"); 45 tag(&o, "Round", t->round[0] ? t->round : "-"); 46 tag(&o, "White", t->white); 47 tag(&o, "Black", t->black); 48 tag(&o, "Result", result_text(g->result)); 49 char fen[100]; 50 pos_fen(&g->start, fen, sizeof fen); 51 if (strcmp(fen, START_FEN)) { tag(&o, "SetUp", "1"); tag(&o, "FEN", fen); } 52 out_s(&o, "\n"); 53 o.col = 0; 54 Pos p = g->start; 55 for (int i = 0; i < g->nply; i++) { 56 char w[24]; 57 if (p.side == WHITE || i == 0) { 58 int k = str_int(w, sizeof w, 0, p.fullmove); 59 str_put(w, sizeof w, k, p.side == WHITE ? "." : "..."); 60 word(&o, w); 61 } 62 move_san(&p, g->mv[i], w, sizeof w); 63 word(&o, w); 64 Undo u; 65 pos_make(&p, g->mv[i], &u); 66 } 67 word(&o, result_text(g->result)); 68 out_s(&o, "\n"); 69 return o.full ? -1 : o.pos; 70 } 71 72 /* ------------------------------------------------------------------ importar */ 73 74 static void copy_val(char *dst, int n, const char *s, int len) 75 { 76 if (len > n - 1) len = n - 1; 77 memcpy(dst, s, (size_t)len); 78 dst[len] = 0; 79 } 80 81 static bool is_result(const char *w) 82 { 83 return !strcmp(w, "1-0") || !strcmp(w, "0-1") || !strcmp(w, "1/2-1/2") || !strcmp(w, "*"); 84 } 85 86 bool game_from_pgn(Game *g, const char *s, PgnTags *tags, char *err, int errn) 87 { 88 Pos start; 89 pos_start(&start); 90 if (tags) memset(tags, 0, sizeof *tags); 91 if (err && errn) err[0] = 0; 92 /* primera pasada: etiquetas */ 93 for (const char *q = s; *q; q++) { 94 if (*q != '[') continue; 95 const char *name = q + 1, *sp = name; 96 while (*sp && *sp != ' ' && *sp != ']') sp++; 97 const char *v0 = strchr(sp, '"'); 98 const char *nl = strchr(sp, '\n'); 99 if (!v0 || (nl && v0 > nl)) continue; 100 const char *v1 = strchr(v0 + 1, '"'); 101 if (!v1) break; 102 int nl_ = (int)(sp - name), vl = (int)(v1 - v0 - 1); 103 char key[16]; 104 copy_val(key, sizeof key, name, nl_); 105 if (!strcmp(key, "FEN")) { 106 char fen[100]; 107 copy_val(fen, sizeof fen, v0 + 1, vl); 108 if (!pos_from_fen(&start, fen)) { if (err) str_put(err, errn, 0, "FEN invalido"); return false; } 109 } else if (tags) { 110 if (!strcmp(key, "White")) copy_val(tags->white, sizeof tags->white, v0 + 1, vl); 111 else if (!strcmp(key, "Black")) copy_val(tags->black, sizeof tags->black, v0 + 1, vl); 112 else if (!strcmp(key, "Event")) copy_val(tags->event, sizeof tags->event, v0 + 1, vl); 113 else if (!strcmp(key, "Site")) copy_val(tags->site, sizeof tags->site, v0 + 1, vl); 114 else if (!strcmp(key, "Date")) copy_val(tags->date, sizeof tags->date, v0 + 1, vl); 115 else if (!strcmp(key, "Round")) copy_val(tags->round, sizeof tags->round, v0 + 1, vl); 116 } 117 q = v1; 118 } 119 TimeCtl tc = { 0, 0, 0 }; 120 game_init(g, &start, &tc, true); 121 /* segunda pasada: jugadas */ 122 int depth = 0; 123 const char *q = s; 124 while (*q) { 125 char c = *q; 126 if (c == '[' && depth == 0) { while (*q && *q != ']') q++; if (*q) q++; continue; } 127 if (c == '{') { while (*q && *q != '}') q++; if (*q) q++; continue; } 128 if (c == ';') { while (*q && *q != '\n') q++; continue; } 129 if (c == '(') { depth++; q++; continue; } 130 if (c == ')') { if (depth) depth--; q++; continue; } 131 if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { q++; continue; } 132 const char *w0 = q; 133 while (*q && !strchr(" \t\r\n{}();[", *q)) q++; 134 if (depth) continue; 135 char w[32]; 136 copy_val(w, sizeof w, w0, (int)(q - w0)); 137 if (w[0] == '$' || is_result(w)) continue; 138 /* numeros de jugada: "12." "12..." o pegados "12.e4" */ 139 char *m = w; 140 while (*m >= '0' && *m <= '9') m++; 141 if (m != w && *m == '.') { while (*m == '.') m++; } 142 else m = w; 143 if (!*m) continue; 144 if (g->result != R_NONE) break; 145 Move mv = move_from_san(&g->pos, m); 146 if (!mv) { 147 if (err) { 148 int k = str_put(err, errn, 0, "jugada invalida: "); 149 if (k >= 0) str_put(err, errn, k, m); 150 } 151 return false; 152 } 153 Act a = { ACT_MOVE, (int8_t)g->pos.side, mv, 0 }; 154 game_apply(g, &a); 155 } 156 return true; 157 }