added uciconnect
This commit is contained in:
parent
c975372b17
commit
eb8f0cbd6b
12
README
12
README
|
@ -12,6 +12,9 @@
|
|||
|
||||
chessterm [-font path] [-bg col] [-fg col] player directory
|
||||
|
||||
uciconnect [-think time] [-wait time] [-player p] -dir game
|
||||
command
|
||||
|
||||
rc/play [-r] player directory
|
||||
|
||||
rc/spectate [-r] directory
|
||||
|
@ -75,6 +78,15 @@
|
|||
and foreground colors can also be set, based on which the
|
||||
board is drawn.
|
||||
|
||||
Uciconnect can be used to connect a game directory, with a
|
||||
process serving the UCI protocol via standard input and out-
|
||||
put. There are two mandatory arguments: the game directory
|
||||
and the command to execute. The -think arguments sets the
|
||||
time the engine is allowed to find a move. Between commands
|
||||
and reads, the program sleeps a duration, which can be set
|
||||
via the -wait option. By default, the engine plays the white
|
||||
player, which can be set with the -player flag.
|
||||
|
||||
Play is an interactive script which prints the board and
|
||||
game status, and waits for your or your opponent's move. The
|
||||
-r flag transcribes the UTF-8 piece symbols into other sym-
|
||||
|
|
13
chessfs.4
13
chessfs.4
|
@ -6,6 +6,8 @@ chessfs [-verbose] [-addr addr] [-srv srv] [-group gid]
|
|||
|
||||
chessterm [-font path] [-bg col] [-fg col] player directory
|
||||
|
||||
uciconnect [-think time] [-wait time] [-player p] -dir game command
|
||||
|
||||
rc/play [-r] player directory
|
||||
|
||||
rc/spectate [-r] directory
|
||||
|
@ -90,6 +92,17 @@ from the
|
|||
environment variable, or from the argument. Additionally, the background
|
||||
and foreground colors can also be set, based on which the board is drawn.
|
||||
.PP
|
||||
Uciconnect can be used to connect a game directory, with a process serving
|
||||
the UCI protocol via standard input and output. There are two mandatory
|
||||
arguments: the game directory and the command to execute. The
|
||||
.BI -think
|
||||
arguments sets the time the engine is allowed to find a move. Between
|
||||
commands and reads, the program sleeps a duration, which can be set via the
|
||||
.BI -wait
|
||||
option. By default, the engine plays the white player, which can be set with the
|
||||
.BI -player
|
||||
flag.
|
||||
.PP
|
||||
Play is an interactive script which prints the board and game status,
|
||||
and waits for your or your opponent's move. The -r flag transcribes
|
||||
the UTF-8 piece symbols into other symbols, in case the used font
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue