v0.14.0 web replay UI, My games, CLI replay
This commit is contained in:
1 parent
87dcb3e550
commit
a999177f0c
37 files changed
+4058
-1803
No files matched your search
@@ -674,6 +674,7 @@ func (p *Processor) buildGameResponse(gameID string, g game.View) core.GameRespo
|
||||
State: g.State.String(),
|
||||
Termination: string(g.Termination),
|
||||
Moves: g.Moves,
|
||||
SAN: notateLine(g.FENs, g.Moves),
|
||||
Players: core.PlayersResponse{
|
||||
White: g.WhitePlayer,
|
||||
Black: g.BlackPlayer,
|
||||
@@ -721,6 +722,27 @@ func (p *Processor) Close() error {
|
||||
return errors.Join(queueErr, engineErr)
|
||||
}
|
||||
|
||||
// notateLine derives SAN for each live move from the position before it,
|
||||
// fens[i] being the position before moves[i]. Each ply stands alone, so a
|
||||
// position the core cannot parse leaves only that entry empty (and is logged
|
||||
// once, by the shadow check, when the move was made).
|
||||
func notateLine(fens, moves []string) []string {
|
||||
san := make([]string, len(moves))
|
||||
for i, uci := range moves {
|
||||
if i >= len(fens) {
|
||||
break
|
||||
}
|
||||
pos, err := chess.ParseFEN(fens[i])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if m, err := pos.ParseUCI(uci); err == nil {
|
||||
san[i] = pos.SAN(m)
|
||||
}
|
||||
}
|
||||
return san
|
||||
}
|
||||
|
||||
// rulesResult is the rules core's verdict on a move, computed alongside the
|
||||
// engine's. Stockfish stays authoritative; a disagreement is logged so the
|
||||
// core can be trusted, or fixed, before it validates anything.
|
||||
|
||||
@@ -2,6 +2,8 @@ package processor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/lixenwraith/chess/internal/server/chess"
|
||||
@@ -25,6 +27,33 @@ func TestRulesCheck(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotateLine(t *testing.T) {
|
||||
line, err := chess.Replay(chess.StartFEN, []string{"e2e4", "e7e5", "g1f3", "b8c6", "f1b5"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fens := make([]string, 6)
|
||||
for i := range fens {
|
||||
fens[i] = line.Position(i).FEN()
|
||||
}
|
||||
moves := []string{"e2e4", "e7e5", "g1f3", "b8c6", "f1b5"}
|
||||
if got := strings.Join(notateLine(fens, moves), " "); got != "e4 e5 Nf3 Nc6 Bb5" {
|
||||
t.Errorf("SAN = %q", got)
|
||||
}
|
||||
// A ply the core cannot notate leaves only its own entry empty.
|
||||
broken := append([]string(nil), fens...)
|
||||
broken[2] = "not a fen"
|
||||
if got := notateLine(broken, moves); !reflect.DeepEqual(got, []string{"e4", "e5", "", "Nc6", "Bb5"}) {
|
||||
t.Errorf("SAN with a broken position = %q", got)
|
||||
}
|
||||
if got := notateLine(nil, nil); len(got) != 0 {
|
||||
t.Errorf("SAN of no moves = %q", got)
|
||||
}
|
||||
if got := notateLine(fens[:1], moves[:2]); !reflect.DeepEqual(got, []string{"e4", ""}) {
|
||||
t.Errorf("SAN short of positions = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdjudicateDraw(t *testing.T) {
|
||||
cycle := []string{"g1f3", "g8f6", "f3g1", "f6g8"}
|
||||
line, err := chess.Replay(chess.StartFEN, append(cycle, cycle...))
|
||||
|
||||
Reference in new issue
Block a user