src/chess/pgn.c

changeset 228
13b934b7d03e
parent 219
24b866230dd4
equal deleted inserted replaced
227:447d1e192807 228:13b934b7d03e
273 pgn_result result = parse_pgn(data, gamestate); 273 pgn_result result = parse_pgn(data, gamestate);
274 free(data); 274 free(data);
275 return result; 275 return result;
276 } 276 }
277 277
278 static void pgn_insert_newlines(char *block) { 278 static void pgn_insert_newlines(char *block, unsigned max_width) {
279 size_t pos = 0; 279 size_t pos = 0;
280 size_t last_space_pos = 0; 280 size_t last_space_pos = 0;
281 size_t line_len = 0; 281 size_t line_len = 0;
282 while (block[pos] != '\0') { 282 while (block[pos] != '\0') {
283 if (block[pos] == 0x1f) { 283 if (block[pos] == 0x1f) {
286 } 286 }
287 if (block[pos] == ' ') { 287 if (block[pos] == ' ') {
288 last_space_pos = pos; 288 last_space_pos = pos;
289 } 289 }
290 line_len++; 290 line_len++;
291 if (line_len > 80) { 291 if (line_len > max_width) {
292 block[last_space_pos] = '\n'; 292 block[last_space_pos] = '\n';
293 line_len = pos - last_space_pos; 293 line_len = pos - last_space_pos;
294 } 294 }
295 pos++; 295 pos++;
296 } 296 }
339 fputs(pgn, stream); 339 fputs(pgn, stream);
340 free(pgn); 340 free(pgn);
341 return 0; 341 return 0;
342 } 342 }
343 343
344 char *create_pgn(const GameState *gamestate, bool export_comments) { 344 static size_t pgn_move_text_add(
345 size_t resultcap = 4096; 345 char *buffer,
346 char *result = malloc(resultcap); 346 const GameState *gamestate,
347 if (result == NULL) return NULL; 347 unsigned max_width, bool export_comments) {
348 char *resp = result; 348 char *resp = buffer;
349
350 /* STR tag pairs */
351 resp += sprintf(resp, "[Event \"%s\"]\n", "terminal-chess game");
352 resp += sprintf(resp, "[Site \"%s\"]\n", "Somewhere on Earth");
353 resp += sprintf(resp, "[Date \"%s\"]\n", pgn_date(gamestate));
354 resp += sprintf(resp, "[Round \"%s\"]\n", "-");
355 resp += sprintf(resp, "[White \"%s\"]\n", pgn_player_name(gamestate, WHITE));
356 resp += sprintf(resp, "[Black \"%s\"]\n", pgn_player_name(gamestate, BLACK));
357 const char *gameresult = pgn_game_result(gamestate);
358 resp += sprintf(resp, "[Result \"%s\"]\n\n", gameresult);
359
360 // TODO: add optional clock info
361
362 /* moves */
363 char *moveblk = resp; /* remember where the move block starts */
364
365 for (unsigned i = 0 ; i < gamestate->movecount ; i++) { 349 for (unsigned i = 0 ; i < gamestate->movecount ; i++) {
366 /* reallocate buffer if needed */
367 {
368 size_t rsize = resp - result;
369 if (rsize + 128 > resultcap) {
370 resultcap *= 2;
371 char *newresult = realloc(result, resultcap);
372 if (newresult == NULL) {
373 free(result);
374 return NULL;
375 }
376 result = newresult;
377 resp = result + rsize;
378 }
379 }
380
381 if (i % 2 == 0) { 350 if (i % 2 == 0) {
382 // TODO: add move_start offset 351 // TODO: add move_start offset
383 resp += snprintf(resp, 16, "%d.\x1f%s", 352 resp += snprintf(resp, 16, "%d.\x1f%s",
384 1+i/2, gamestate->moves[i].string); 353 1+i/2, gamestate->moves[i].string);
385 } else { 354 } else {
399 unsigned clkmv = i + 2; /* time for the next move! */ 368 unsigned clkmv = i + 2; /* time for the next move! */
400 uint16_t clk = remaining_movetime2(gamestate, clkmv); 369 uint16_t clk = remaining_movetime2(gamestate, clkmv);
401 resp += print_clk(clk, resp, true); 370 resp += print_clk(clk, resp, true);
402 *(resp++) = ']'; 371 *(resp++) = ']';
403 *(resp++) = '}'; 372 *(resp++) = '}';
404 373
405 /* elapsed move time */ 374 /* elapsed move time */
406 memcpy(resp, " {[%emt ", 8); 375 memcpy(resp, " {[%emt ", 8);
407 resp += 8; 376 resp += 8;
408 uint16_t emt = gamestate->moves[i].movetime / 1000000ull; 377 uint16_t emt = gamestate->moves[i].movetime / 1000000ull;
409 resp += print_clk(emt, resp, true); 378 resp += print_clk(emt, resp, true);
410 *(resp++) = ']'; 379 *(resp++) = ']';
411 *(resp++) = '}'; 380 *(resp++) = '}';
412 } 381 }
413 382
414 *(resp++) = ' '; 383 *(resp++) = ' ';
415 } 384 }
416 385
386 const char *gameresult = pgn_game_result(gamestate);
417 if (gameresult[0] != '*') { 387 if (gameresult[0] != '*') {
418 size_t rlen = strlen(gameresult); 388 size_t rlen = strlen(gameresult);
419 memcpy(resp, gameresult, rlen); 389 memcpy(resp, gameresult, rlen);
420 resp += rlen; 390 resp += rlen;
421 } 391 }
422 *(resp++) = '\n'; 392 *(resp++) = '\n';
423 *resp = 0; 393 *(resp++) = '\0';
424 394
425 /* post-process move block and format it nicely */ 395 /* post-process move block and format it nicely */
426 pgn_insert_newlines(moveblk); 396 pgn_insert_newlines(buffer, max_width);
427 397
428 return result; 398 return resp-buffer;
429 } 399 }
400
401 char *pgn_move_text(const GameState *gamestate,
402 unsigned max_width, bool export_comments) {
403 size_t bufcap = 16;
404 bufcap += gamestate->movecount * 16;
405 if (export_comments) {
406 bufcap += gamestate->movecount * 40;
407 }
408 char *result = malloc(bufcap);
409 if (result == NULL) return NULL;
410 size_t n = pgn_move_text_add(result, gamestate, max_width, export_comments);
411 return realloc(result, n);
412 }
413
414 char *create_pgn(const GameState *gamestate, bool export_comments) {
415 /* calculate generous upper bound for memory usage */
416 size_t bufcap = 256;
417 bufcap += gamestate->movecount * 16;
418 if (export_comments) {
419 bufcap += gamestate->movecount * 40;
420 }
421 char *result = malloc(bufcap);
422 if (result == NULL) return NULL;
423 char *resp = result;
424
425 /* STR tag pairs */
426 resp += sprintf(resp, "[Event \"%s\"]\n", "terminal-chess game");
427 resp += sprintf(resp, "[Site \"%s\"]\n", "Somewhere on Earth");
428 resp += sprintf(resp, "[Date \"%s\"]\n", pgn_date(gamestate));
429 resp += sprintf(resp, "[Round \"%s\"]\n", "-");
430 resp += sprintf(resp, "[White \"%s\"]\n", pgn_player_name(gamestate, WHITE));
431 resp += sprintf(resp, "[Black \"%s\"]\n", pgn_player_name(gamestate, BLACK));
432 resp += sprintf(resp, "[Result \"%s\"]\n", pgn_game_result(gamestate));
433
434 // TODO: add optional clock info
435
436 *(resp++) = '\n';
437 resp += pgn_move_text_add(resp, gamestate, 80, export_comments);
438
439 /* shrink memory to fit */
440 return realloc(result, resp-result);
441 }

mercurial