src/chess/pgn.c

changeset 178
724e8acff6f8
parent 175
e0e061cf4829
equal deleted inserted replaced
177:18dbb0dc9cd6 178:724e8acff6f8
56 56
57 size_t pgn_error_position(void) { 57 size_t pgn_error_position(void) {
58 return pgn_error_pos; 58 return pgn_error_pos;
59 } 59 }
60 60
61 static const char *pgn_result(GameState *gamestate) { 61 static const char *pgn_result(const GameState *gamestate) {
62 if (is_game_drawn(gamestate)) { 62 if (is_game_drawn(gamestate)) {
63 return "1/2-1/2"; 63 return "1/2-1/2";
64 } else if (gamestate->wresign) { 64 } else if (gamestate->wresign) {
65 return "0-1"; 65 return "0-1";
66 } else if (gamestate->bresign) { 66 } else if (gamestate->bresign) {
267 } 267 }
268 pos++; 268 pos++;
269 } 269 }
270 } 270 }
271 271
272 static const char *pgn_date(GameState *gamestate) { 272 static const char *pgn_date(const GameState *gamestate) {
273 static char date[16]; 273 static char date[16];
274 time_t dateseconds; 274 time_t dateseconds;
275 if (gamestate->movecount == 0) { 275 if (gamestate->movecount == 0) {
276 struct timeval curtimestamp; 276 struct timeval curtimestamp;
277 gettimeofday(&curtimestamp, NULL); 277 gettimeofday(&curtimestamp, NULL);
299 year, month, day); 299 year, month, day);
300 } 300 }
301 return date; 301 return date;
302 } 302 }
303 303
304 const char *pgn_player_name(GameState *gamestate, uint8_t color) { 304 const char *pgn_player_name(const GameState *gamestate, uint8_t color) {
305 const char *name = color == WHITE ? gamestate->wname : gamestate->bname; 305 const char *name = color == WHITE ? gamestate->wname : gamestate->bname;
306 return name[0] != '\0' ? name : "Anonymous"; 306 return name[0] != '\0' ? name : "Anonymous";
307 } 307 }
308 308
309 void write_pgn(FILE* stream, GameState *gamestate, bool export_comments) { 309 int write_pgn(FILE* stream, const GameState *gamestate, bool export_comments) {
310 char *pgn = create_pgn(gamestate, export_comments);
311 if (pgn == NULL) return 1;
312 fputs(pgn, stream);
313 free(pgn);
314 return 0;
315 }
316
317 char *create_pgn(const GameState *gamestate, bool export_comments) {
318 size_t resultcap = 4096;
319 char *result = malloc(resultcap);
320 if (result == NULL) return NULL;
321 char *resp = result;
322
310 /* STR tag pairs */ 323 /* STR tag pairs */
311 fprintf(stream, "[Event \"%s\"]\n", "terminal-chess game"); 324 resp += sprintf(resp, "[Event \"%s\"]\n", "terminal-chess game");
312 fprintf(stream, "[Site \"%s\"]\n", "Somewhere on Earth"); 325 resp += sprintf(resp, "[Site \"%s\"]\n", "Somewhere on Earth");
313 fprintf(stream, "[Date \"%s\"]\n", pgn_date(gamestate)); 326 resp += sprintf(resp, "[Date \"%s\"]\n", pgn_date(gamestate));
314 fprintf(stream, "[Round \"%s\"]\n", "-"); 327 resp += sprintf(resp, "[Round \"%s\"]\n", "-");
315 fprintf(stream, "[White \"%s\"]\n", pgn_player_name(gamestate, WHITE)); 328 resp += sprintf(resp, "[White \"%s\"]\n", pgn_player_name(gamestate, WHITE));
316 fprintf(stream, "[Black \"%s\"]\n", pgn_player_name(gamestate, BLACK)); 329 resp += sprintf(resp, "[Black \"%s\"]\n", pgn_player_name(gamestate, BLACK));
317 const char *result = pgn_result(gamestate); 330 const char *gameresult = pgn_result(gamestate);
318 fprintf(stream, "[Result \"%s\"]\n\n", result); 331 resp += sprintf(resp, "[Result \"%s\"]\n\n", gameresult);
319 332
320 // TODO: add optional clock info 333 // TODO: add optional clock info
321 334
322 /* moves */ 335 /* moves */
323 size_t moveblkcap = 4096; 336 char *moveblk = resp; /* remember where the move block starts */
324 char *moveblk = malloc(moveblkcap); 337
325 char *moveblkptr = moveblk;
326 if (moveblk == NULL) {
327 // TODO: error handling (for the entire function actually)
328 abort();
329 }
330 for (unsigned i = 0 ; i < gamestate->movecount ; i++) { 338 for (unsigned i = 0 ; i < gamestate->movecount ; i++) {
331 /* reallocate move block buffer if needed */ 339 /* reallocate buffer if needed */
332 { 340 {
333 size_t moveblksize = moveblkptr - moveblk; 341 size_t rsize = resp - result;
334 if (moveblksize + 128 > moveblkcap) { 342 if (rsize + 128 > resultcap) {
335 moveblkcap *= 2; 343 resultcap *= 2;
336 char *newmoveblk = realloc(moveblk, moveblkcap); 344 char *newresult = realloc(result, resultcap);
337 if (newmoveblk == NULL) { 345 if (newresult == NULL) {
338 free(moveblk); 346 free(result);
339 abort(); 347 return NULL;
340 } 348 }
341 moveblk = newmoveblk; 349 result = newresult;
342 moveblkptr = moveblk + moveblksize; 350 resp = result + rsize;
343 } 351 }
344 } 352 }
345
346 int snpr; /* return value of printf calls */
347 353
348 if (i % 2 == 0) { 354 if (i % 2 == 0) {
349 snpr = snprintf(moveblkptr, 16, "%d.\x1f%s", 355 resp += snprintf(resp, 16, "%d.\x1f%s",
350 1+i/2, gamestate->moves[i].string); 356 1+i/2, gamestate->moves[i].string);
351 } else { 357 } else {
352 snpr = snprintf(moveblkptr, 16, "%s", 358 resp += snprintf(resp, 16, "%s",
353 gamestate->moves[i].string); 359 gamestate->moves[i].string);
354 } 360 }
355 moveblkptr += snpr;
356 361
357 if (!export_comments) { 362 if (!export_comments) {
358 *(moveblkptr++) = ' '; 363 *(resp++) = ' ';
359 continue; 364 continue;
360 } 365 }
361 366
362 /* add clock times when the game was under time control */ 367 /* add clock times when the game was under time control */
363 if (gamestate->info.timecontrol) { 368 if (gamestate->info.timecontrol) {
364 memcpy(moveblkptr, " {[%clk ", 8); 369 memcpy(resp, " {[%clk ", 8);
365 moveblkptr += 8; 370 resp += 8;
366 unsigned clkmv = i + 2; /* time for the next move! */ 371 unsigned clkmv = i + 2; /* time for the next move! */
367 uint16_t clk = remaining_movetime2(gamestate, clkmv); 372 uint16_t clk = remaining_movetime2(gamestate, clkmv);
368 snpr = print_clk(clk, moveblkptr, true); 373 resp += print_clk(clk, resp, true);
369 moveblkptr += snpr; 374 *(resp++) = ']';
370 *(moveblkptr++) = ']'; 375 *(resp++) = '}';
371 *(moveblkptr++) = '}';
372 376
373 /* elapsed move time */ 377 /* elapsed move time */
374 memcpy(moveblkptr, " {[%emt ", 8); 378 memcpy(resp, " {[%emt ", 8);
375 moveblkptr += 8; 379 resp += 8;
376 uint16_t emt = gamestate->moves[i].movetime.tv_sec; 380 uint16_t emt = gamestate->moves[i].movetime.tv_sec;
377 snpr = print_clk(emt, moveblkptr, true); 381 resp += print_clk(emt, resp, true);
378 moveblkptr += snpr; 382 *(resp++) = ']';
379 *(moveblkptr++) = ']'; 383 *(resp++) = '}';
380 *(moveblkptr++) = '}'; 384 }
381 } 385
382 386 *(resp++) = ' ';
383 *(moveblkptr++) = ' '; 387 }
384 } 388
385 389 if (gameresult[0] != '*') {
386 if (result[0] != '*') { 390 size_t rlen = strlen(gameresult);
387 size_t rlen = strlen(result); 391 memcpy(resp, gameresult, rlen);
388 memcpy(moveblkptr, result, rlen); 392 resp += rlen;
389 moveblkptr += rlen; 393 }
390 } 394 *(resp++) = '\n';
391 *(moveblkptr++) = '\n'; 395 *resp = 0;
392 *moveblkptr = 0; 396
393 397 /* post-process move block and format it nicely */
394 pgn_insert_newlines(moveblk); 398 pgn_insert_newlines(moveblk);
395 fputs(moveblk, stream); 399
396 400 return result;
397 free(moveblk); 401 }
398 }

mercurial