| 360 pgn_insert_newlines(moveblk); |
360 pgn_insert_newlines(moveblk); |
| 361 fputs(moveblk, stream); |
361 fputs(moveblk, stream); |
| 362 |
362 |
| 363 free(moveblk); |
363 free(moveblk); |
| 364 } |
364 } |
| 365 |
|
| 366 static size_t fen_pieces(char *str, GameState *gamestate) { |
|
| 367 size_t i = 0; |
|
| 368 for (int row = 7 ; row >= 0 ; row--) { |
|
| 369 unsigned int skip = 0; |
|
| 370 for (int file = 0 ; file < 8 ; file++) { |
|
| 371 if (gamestate->board[row][file]) { |
|
| 372 if (skip > 0) { |
|
| 373 str[i++] = '0'+skip; |
|
| 374 skip = 0; |
|
| 375 } |
|
| 376 switch (gamestate->board[row][file] & ~ENPASSANT_THREAT) { |
|
| 377 case WHITE|KING: str[i++] = 'K'; break; |
|
| 378 case WHITE|QUEEN: str[i++] = 'Q'; break; |
|
| 379 case WHITE|BISHOP: str[i++] = 'B'; break; |
|
| 380 case WHITE|KNIGHT: str[i++] = 'N'; break; |
|
| 381 case WHITE|ROOK: str[i++] = 'R'; break; |
|
| 382 case WHITE|PAWN: str[i++] = 'P'; break; |
|
| 383 case BLACK|KING: str[i++] = 'k'; break; |
|
| 384 case BLACK|QUEEN: str[i++] = 'q'; break; |
|
| 385 case BLACK|BISHOP: str[i++] = 'b'; break; |
|
| 386 case BLACK|KNIGHT: str[i++] = 'n'; break; |
|
| 387 case BLACK|ROOK: str[i++] = 'r'; break; |
|
| 388 case BLACK|PAWN: str[i++] = 'p'; break; |
|
| 389 } |
|
| 390 } else { |
|
| 391 skip++; |
|
| 392 } |
|
| 393 } |
|
| 394 if (skip > 0) { |
|
| 395 str[i++] = '0'+skip; |
|
| 396 } |
|
| 397 if (row > 0) { |
|
| 398 str[i++] = '/'; |
|
| 399 } |
|
| 400 } |
|
| 401 |
|
| 402 return i; |
|
| 403 } |
|
| 404 |
|
| 405 static size_t fen_color(char *str, GameState *gamestate) { |
|
| 406 uint8_t color = opponent_color(gamestate->movecount > 0 ? |
|
| 407 (last_move(gamestate).piece & COLOR_MASK) : BLACK); |
|
| 408 |
|
| 409 str[0] = color == WHITE ? 'w' : 'b'; |
|
| 410 |
|
| 411 return 1; |
|
| 412 } |
|
| 413 |
|
| 414 static bool fen_castling_chkmoved(GameState *gamestate, |
|
| 415 uint8_t row, uint8_t file) { |
|
| 416 |
|
| 417 for (unsigned i = 0 ; i < gamestate->movecount ; i++) { |
|
| 418 if (gamestate->moves[i].fromfile == file |
|
| 419 && gamestate->moves[i].fromrow == row) { |
|
| 420 return true; |
|
| 421 } |
|
| 422 } |
|
| 423 |
|
| 424 return false; |
|
| 425 } |
|
| 426 |
|
| 427 static size_t fen_castling(char *str, GameState *gamestate) { |
|
| 428 bool K, Q, k, q; |
|
| 429 |
|
| 430 if (fen_castling_chkmoved(gamestate, rowidx('1'), fileidx('e'))) { |
|
| 431 K = Q = false; |
|
| 432 } else { |
|
| 433 K = !fen_castling_chkmoved(gamestate, rowidx('1'), fileidx('h')); |
|
| 434 Q = !fen_castling_chkmoved(gamestate, rowidx('1'), fileidx('a')); |
|
| 435 } |
|
| 436 if (fen_castling_chkmoved(gamestate, rowidx('8'), fileidx('e'))) { |
|
| 437 k = q = false; |
|
| 438 } else { |
|
| 439 k = !fen_castling_chkmoved(gamestate, rowidx('8'), fileidx('h')); |
|
| 440 q = !fen_castling_chkmoved(gamestate, rowidx('8'), fileidx('a')); |
|
| 441 } |
|
| 442 |
|
| 443 size_t i = 0; |
|
| 444 if (K) str[i++] = 'K'; |
|
| 445 if (Q) str[i++] = 'Q'; |
|
| 446 if (k) str[i++] = 'k'; |
|
| 447 if (q) str[i++] = 'q'; |
|
| 448 if (!i) str[i++] = '-'; |
|
| 449 |
|
| 450 return i; |
|
| 451 } |
|
| 452 |
|
| 453 static size_t fen_enpassant(char *str, GameState *gamestate) { |
|
| 454 |
|
| 455 str[0] = '-'; str[1] = '\0'; |
|
| 456 |
|
| 457 for (int file = 0 ; file < 8 ; file++) { |
|
| 458 if (gamestate->board[3][file] & ENPASSANT_THREAT) { |
|
| 459 str[0] = filechr(file); |
|
| 460 str[1] = rowchr(2); |
|
| 461 } |
|
| 462 if (gamestate->board[4][file] & ENPASSANT_THREAT) { |
|
| 463 str[0] = filechr(file); |
|
| 464 str[1] = rowchr(5); |
|
| 465 } |
|
| 466 } |
|
| 467 |
|
| 468 return str[0] == '-' ? 1 : 2; |
|
| 469 } |
|
| 470 |
|
| 471 static size_t fen_halfmove(char *str, GameState *gamestate) { |
|
| 472 unsigned int hm = 0; |
|
| 473 for (unsigned int i = 0; i < gamestate->movecount; i++) { |
|
| 474 if (gamestate->moves[i].capture |
|
| 475 || (gamestate->moves[i].piece & PIECE_MASK) == PAWN) { |
|
| 476 hm = 0; |
|
| 477 } else { |
|
| 478 hm++; |
|
| 479 } |
|
| 480 } |
|
| 481 |
|
| 482 return sprintf(str, "%u", hm); |
|
| 483 } |
|
| 484 |
|
| 485 static size_t fen_movenr(char *str, GameState *gamestate) { |
|
| 486 return sprintf(str, "%u", gamestate->movecount); |
|
| 487 } |
|
| 488 |
|
| 489 void compute_fen(char *str, GameState *gamestate) { |
|
| 490 str += fen_pieces(str, gamestate); |
|
| 491 *str = ' '; str++; |
|
| 492 str += fen_color(str, gamestate); |
|
| 493 *str = ' '; str++; |
|
| 494 str += fen_castling(str, gamestate); |
|
| 495 *str = ' '; str++; |
|
| 496 str += fen_enpassant(str, gamestate); |
|
| 497 *str = ' '; str++; |
|
| 498 str += fen_halfmove(str, gamestate); |
|
| 499 *str = ' '; str++; |
|
| 500 str += fen_movenr(str, gamestate); |
|
| 501 str[0] = '\0'; |
|
| 502 } |
|