ref: eb8f0cbd6b03c83d0ec53c73ce721dae347217de
dir: /cmd/uciconnect/uciconnect.go/
package main import ( "fmt" "flag" "log" "os" "strings" "time" "github.com/notnil/chess" "github.com/notnil/chess/uci" ) const ( DefaultThink = "0.25s" DefaultWait = "0.5s" ) type GameDir struct { dir string player string } func NewGameDir(dir, player string) (*GameDir, error) { var err error _, err = os.Stat(dir) if err != nil { return nil, fmt.Errorf("game dir stat: %v", err) } _, err = os.Stat(dir + "/ctl") if err != nil { return nil, fmt.Errorf("game dir stat ctl: %v", err) } _, err = os.Stat(dir + "/fen") if err != nil { return nil, fmt.Errorf("game dir stat fen: %v", err) } _, err = os.Stat(dir + "/" + player) if err != nil { return nil, fmt.Errorf("game dir stat %s: %v", player, err) } return &GameDir { dir, player }, nil } func (g *GameDir) GetNew() (bool, error) { ctl, err := os.ReadFile(g.dir + "/ctl") if err != nil { return false, err } ctllines := strings.Split(string(ctl), "\n") if ctllines[0] == "new" { return true, nil } return false, nil } func (g *GameDir) GetOngoing() (bool, error) { ctl, err := os.ReadFile(g.dir + "/ctl") if err != nil { return false, err } ctllines := strings.Split(string(ctl), "\n") if ctllines[0] == "ongoing" { return true, nil } return false, nil } func (g *GameDir) GetTurn() (string, error) { ctl, err := os.ReadFile(g.dir + "/ctl") if err != nil { return "", err } ctllines := strings.Split(string(ctl), "\n") turn := strings.Split(ctllines[1], "'") if len(turn) != 2 { return "", fmt.Errorf("malformed player turn line") } return turn[0], nil } func (g *GameDir) GetBoard() (*chess.Game, error) { fenf, err := os.ReadFile(g.dir + "/fen") if err != nil { return nil, err } fens := strings.Split(string(fenf), "\n") if len(fens) < 1 { return nil, fmt.Errorf("fen file empty") } fen, err := chess.FEN(fens[0]) if err != nil { return nil, err } return chess.NewGame(fen), nil } func (g *GameDir) MakeMove(move string) error { err := os.WriteFile(g.dir + "/" + g.player, []byte(move), os.FileMode(os.O_WRONLY)) if err != nil { return err } return nil } func main() { player := flag.String("player", "white", "which player to play") gameDir := flag.String("dir", "", "game directory") thinks := flag.String("think", DefaultThink, "thinking time") waits := flag.String("wait", DefaultWait, "waiting time") flag.Parse() if flag.NArg() == 0 { log.Fatalf("no command supplied") flag.Usage() os.Exit(1) } command := strings.Join(flag.Args(), " ") if *player != "white" && *player != "black" { log.Fatalf("player can either be black or white\n") os.Exit(1) } think, err := time.ParseDuration(*thinks) if err != nil { log.Fatalf("%v\n", err) os.Exit(1) } wait, err := time.ParseDuration(*waits) if err != nil { log.Fatalf("%v\n", err) os.Exit(1) } log.Printf("opening game dir %s\n", *gameDir) game, err := NewGameDir(*gameDir, *player) if err != nil { log.Fatalf("%v\n", err) os.Exit(1) } log.Printf("starting engine\n") engine, err := uci.New(command) if err != nil { log.Fatalf("error starting engine: %v\n", err) os.Exit(1) } notation := chess.LongAlgebraicNotation{} var playable = true var isNew bool var isOngoing bool isNew, err = game.GetNew() if err != nil { log.Fatalf("error: %v\n", err) os.Exit(1) } isOngoing, err = game.GetOngoing() if err != nil { log.Fatalf("error: %v\n", err) os.Exit(1) } playable = isNew || isOngoing for playable { isOngoing, err = game.GetOngoing() if err != nil { log.Fatalf("error getting status: %v\n", err) os.Exit(1) } if !isOngoing { time.Sleep(wait) continue } turn, err := game.GetTurn() if err != nil { log.Fatalf("error getting turn: %v\n", err) os.Exit(1) } if turn != *player { time.Sleep(wait) continue } board, err := game.GetBoard() if err != nil { log.Fatalf("error getting game: %v\n", err) os.Exit(1) } cmdPos := uci.CmdPosition{Position: board.Position()} cmdGo := uci.CmdGo{MoveTime: think} if err := engine.Run(cmdPos, cmdGo); err != nil { log.Fatalf("error thinking: %v\n", err) os.Exit(1) } move := engine.SearchResults().BestMove err = board.Move(move) if err != nil { log.Fatalf("error attempting client move: %v\n", err) os.Exit(1) } moves := board.Moves() positions := board.Positions() err = game.MakeMove(notation.Encode(positions[len(moves) - 1], moves[len(moves) - 1])) if err != nil { log.Fatalf("error moving: %v\n", err) os.Exit(1) } time.Sleep(wait) playable, err = game.GetOngoing() if err != nil { log.Fatalf("error getting status: %v\n", err) os.Exit(1) } playable = isOngoing } }