@@ -300,20 +300,22 @@ const PIECE_NAMES = {
300
300
}
301
301
302
302
/** Generate algebraic notation for a legal move. */
303
- export const toAlgebraic = ( move : Move , board : Board ) : Result < AlgebraicMove , string > => {
304
- const { from, to } = move
303
+ export const toAlgebraic = (
304
+ { from, to, promotion, check, mate } : Move ,
305
+ board : Board ,
306
+ ) : Result < AlgebraicMove , string > => {
305
307
const colorPiece = board [ from ]
306
308
if ( ! colorPiece ) {
307
309
return err ( `There is no piece on ${ from } .` )
308
310
}
309
311
const color = colorPiece [ 0 ] as Color
310
312
const piece = ( colorPiece [ 1 ] ?? "" ) as Piece
311
- const check = move . mate ? "#" : move . check ? "+" : ""
313
+ const checkSign = mate ? "#" : check ? "+" : ""
312
314
313
315
// castling
314
316
const fileDelta = to . charCodeAt ( 0 ) - from . charCodeAt ( 0 )
315
317
if ( piece === "K" && Math . abs ( fileDelta ) === 2 ) {
316
- return ok ( `${ fileDelta > 0 ? "O-O" : "O-O-O" } ${ check } ` satisfies AlgebraicMove )
318
+ return ok ( `${ fileDelta > 0 ? "O-O" : "O-O-O" } ${ checkSign } ` satisfies AlgebraicMove )
317
319
}
318
320
319
321
const opponentPiece = board [ to ]
@@ -338,7 +340,7 @@ export const toAlgebraic = (move: Move, board: Board): Result<AlgebraicMove, str
338
340
return err ( `You have no ${ PIECE_NAMES [ piece ] } on ${ from } .` )
339
341
}
340
342
341
- const suffix = `${ opponentPiece ? "x" : "" } ${ to } ${ move . promotion ? `=${ move . promotion } ` : "" } ${ check } `
343
+ const suffix = `${ opponentPiece ? "x" : "" } ${ to } ${ promotion ? `=${ promotion } ` : "" } ${ checkSign } `
342
344
if ( candidates . length === 1 && ! ( piece === "" && opponentPiece ) ) {
343
345
return ok ( `${ piece } ${ suffix } ` as AlgebraicMove )
344
346
}
@@ -546,23 +548,27 @@ export const revertToMove = (idx: number, game: Game): Game =>
546
548
applyHistory ( START_GAME , game . history . slice ( 0 , idx ) ) . unwrap ( )
547
549
548
550
549
- export const toPGN = ( game : Game ) : string => {
551
+ /**
552
+ * Turn game state into Portable Game Notation (PGN)
553
+ * @see https://en.wikipedia.org/wiki/Portable_Game_Notation
554
+ */
555
+ export const toPGN = ( { history, outcome, termination } : Game ) : string => {
550
556
const date = new Date ( ) . toISOString ( ) . slice ( 0 , 10 ) . replaceAll ( "-" , "." )
551
- const result = match ( game . outcome )
557
+ const result = match ( outcome )
552
558
. with ( "w" , ( ) => "1-0" )
553
559
. with ( "b" , ( ) => "0-1" )
554
560
. with ( "draw" , ( ) => "1/2-1/2" )
555
561
. with ( undefined , ( ) => "*" )
556
562
. exhaustive ( )
557
- const termination = game . outcome
563
+ const term = outcome
558
564
? (
559
- match ( game . outcome )
565
+ match ( outcome )
560
566
. with ( "w" , ( ) => "White wins" )
561
567
. with ( "b" , ( ) => "Black wins" )
562
568
. with ( "draw" , ( ) => "Draw" )
563
569
. exhaustive ( )
564
570
+ " "
565
- + match ( game . termination )
571
+ + match ( termination )
566
572
. with ( "checkmate" , ( ) => "by checkmate." )
567
573
. with ( "time" , ( ) => "on time." )
568
574
. with ( "stalemate" , ( ) => " by stalemate" )
@@ -574,10 +580,10 @@ export const toPGN = (game: Game): string => {
574
580
)
575
581
: "unterminated"
576
582
const moves = Array . from (
577
- pairs ( game . history . map ( move => move . algebraic ) ) ,
583
+ pairs ( history . map ( move => move . algebraic ) ) ,
578
584
( movePair , idx ) => `${ idx + 1 } . ${ movePair . join ( " " ) } ` ,
579
585
)
580
- if ( game . outcome ) {
586
+ if ( outcome ) {
581
587
moves . push ( result )
582
588
}
583
589
return [
@@ -588,7 +594,7 @@ export const toPGN = (game: Game): string => {
588
594
`[White "?"]` ,
589
595
`[Black "?"]` ,
590
596
`[Result "${ result } "]` ,
591
- `[Termination "${ termination } "]` ,
597
+ `[Termination "${ term } "]` ,
592
598
// `[TimeControl "120+1"]`,
593
599
// `[Time HH:MM:SS]`,
594
600
// `[EndTime "3:38:57 PST"]`,
0 commit comments