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

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 }