shithub: chessfs

Download patch

ref: c975372b175fdf14e5c1158c705303fc73c116c7
parent: 03a1f26a9e4fdd5c7ccaeae9c82e0af4b074db11
author: kitzman <kitzman@disroot.org>
date: Thu Jan 25 13:09:49 EST 2024

added rw fen file; chessterm now uses fen; fixed menu order

--- a/README
+++ b/README
@@ -28,14 +28,14 @@
           The filesystem contains three files - ctl, clone and games.
           Opening the clone file creates a new game, and the id can be
           read from the fid. The game can be found under the games
-          directory, as a directory with the game id. Ctl is for
-          reading and setting server options. It displays the maximum
+          directory, as a directory with the game id. Ctl is for read-
+          ing and setting server options. It displays the maximum
           amount of games, and the current count of games. Setting
           roomsize changes the maximum number of games which can be
           played.
 
-          In each game directory, there are the ctl, white, black and
-          moves file. Ctl supports the following commands:
+          In each game directory, there are the ctl, white, black,
+          moves and fen files. Ctl supports the following commands:
 
 
           time seconds  Set the amount of time each player has. The
@@ -55,9 +55,13 @@
           move    Make the following move, following the algebraic
                   notation.
 
-          For reading the game in the PGN notation, the moves files
-          can be read.
+          For reading the game in the PGN notation, the moves file can
+          be read.
 
+          The fen file can be used to read the FEN notation of the
+          current game. Additionally, writing to the file, before the
+          game is started, sets the pieces' position.
+
           Chessterm is the user interface for the filesystem. It
           assumes the game is already cloned, and takes as arguments
           the player (either "black" or "white"), and the game direc-
@@ -96,10 +100,14 @@
 
      SOURCE
           https://git.disroot.org/kitzman/chessfs
+
           https://shithub.us/kitzman/chess/HEAD/info.html
 
 
      BUGS AND TODO
+          Menus don't work with plan9port's devdraw. I have only
+          tested this, once, on a Linux machine.
+
           Encrypting connections should be an option.
 
           Currently players cannot change the owners of the files.
--- a/chessfs.4
+++ b/chessfs.4
@@ -18,7 +18,11 @@
 is the one running the program, and the group can be set using
 the -group option.
 
-The filesystem contains three files - ctl, clone and games.
+The filesystem contains three files -
+.BI "ctl",
+.BI "clone"
+and
+.BI "games".
 Opening the clone file creates a new game, and the id can be
 read from the fid. The game can be found under the games directory,
 as a directory with the game id. Ctl is for reading and setting
@@ -26,8 +30,14 @@
 current count of games. Setting roomsize changes the maximum number
 of games which can be played.
 
-In each game directory, there are the ctl, white, black and moves
-file. Ctl supports the following commands:
+In each game directory, there are the
+.BI "ctl",
+.BI "white",
+.BI "black",
+.BI "moves"
+and
+.BI "fen"
+files. Ctl supports the following commands:
 
 .TF "\fLtime seconds\fR"
 .TP
@@ -59,8 +69,15 @@
 .PD
 .RE
 .PP
-For reading the game in the PGN notation, the moves files can be read.
+For reading the game in the PGN notation, the
+.BI moves
+file can be read.
 .PP
+The
+.BI fen
+file can be used to read the FEN notation of the current game. Additionally,
+writing to the file, before the game is started, sets the pieces' position.
+.PP
 Chessterm is the user interface for the filesystem. It assumes the game
 is already cloned, and takes as arguments the player (either "black" or "white"),
 and the game directory. Every half of second the directory is checked
@@ -101,9 +118,13 @@
 .SH SOURCE
 
 https://git.disroot.org/kitzman/chessfs
+
 https://shithub.us/kitzman/chess/HEAD/info.html
 
 .SH BUGS AND TODO
+
+Menus don't work with plan9port's devdraw. I have only
+tested this, once, on a Linux machine.
 
 Encrypting connections should be an option.
 
--- a/chessfs.go
+++ b/chessfs.go
@@ -119,12 +119,12 @@
 
 func (f *CloneFile) Read(fid uint64, offset uint64, count uint64) ([]byte, error) {
 	f.fidl.RLock()
-	id := f.fidm[fid]
+	id, ok := f.fidm[fid]
 	f.fidl.RUnlock()
 
-	//	if id == 0 {
-	//		return nil, fmt.Errorf("fid not found")
-	//	}
+	if !ok {
+		return nil, fmt.Errorf("fid not found")
+	}
 
 	data := []byte(fmt.Sprintf("%d\n", id+1))
 
@@ -140,7 +140,8 @@
 
 func (f *CloneFile) CreateGameDirectory(game *Game, uname, gid string) {
 	statdir := f.fs.NewStat(fmt.Sprint(game.id+1), uname, gid, 0555)
-	statctl := f.fs.NewStat("ctl", uname, gid, 0664)
+	statctl := f.fs.NewStat("ctl", uname, gid, 0644)
+	statfen := f.fs.NewStat("fen", uname, gid, 0644)
 	statmoves := f.fs.NewStat("moves", uname, gid, 0444)
 	statwhite := f.fs.NewStat("white", uname, gid, 0664)
 	statblack := f.fs.NewStat("black", uname, gid, 0664)
@@ -147,6 +148,7 @@
 
 	d := fs.NewStaticDir(statdir)
 	d.AddChild(NewGameCtlFile(statctl, f, game))
+	d.AddChild(NewFENFile(statfen, game))
 	d.AddChild(NewMoveFile(statmoves, game))
 	d.AddChild(NewBoardFile(statwhite, game, White))
 	d.AddChild(NewBoardFile(statblack, game, Black))
@@ -353,4 +355,45 @@
 		count = flen - offset
 	}
 	return data[offset : offset+count], nil
+}
+
+// FEN file for gave saving/loading
+type FENFile struct {
+	fs.WrappedFile
+	game	*Game
+}
+
+func NewFENFile(s *proto.Stat, game *Game) *FENFile {
+	return &FENFile {
+		WrappedFile:	fs.WrappedFile{
+			File:		fs.NewBaseFile(s),
+		},
+		game: game,
+	}
+}
+
+func (f *FENFile) Read(_ uint64, offset uint64, count uint64) ([]byte, error) {
+	data := []byte(f.game.GetFEN())
+	flen := uint64(len(data))
+	if offset >= flen {
+		return []byte{}, nil
+	}
+	if offset+count > flen {
+		count = flen - offset
+	}
+	return data[offset : offset+count], nil
+}
+
+func (f *FENFile) Write(_ uint64, offset uint64, data []byte) (uint32, error) {
+	if offset != 0 {
+		return 0, fmt.Errorf("cannot append to board file")
+	}
+
+	n := uint32(len(data))
+	err := f.game.LoadFEN(string(data))
+	if err != nil {
+		return 0, err
+	}
+
+	return n, nil
 }
--- a/cmd/chessterm/chessterm.go
+++ b/cmd/chessterm/chessterm.go
@@ -317,7 +317,7 @@
 	}
 	timer := time.NewTicker(timerDuration)
 	ctlFile := gameDir + "/ctl"
-	moveFile := gameDir + "/moves"
+	fenFile := gameDir + "/fen"
 	for {
 		<-timer.C
 		ctl, err := os.ReadFile(ctlFile)
@@ -325,7 +325,7 @@
 			log.Fatalf("watcher: %v\n", err)
 			return
 		}
-		moves, err := os.ReadFile(moveFile)
+		moves, err := os.ReadFile(fenFile)
 		if err != nil {
 			log.Fatalf("watcher: %v\n", err)
 			return
@@ -349,31 +349,20 @@
 			return
 		}
 
-		games, err := chess.GamesFromPGN(strings.NewReader(string(moves)))
+		fen, err := chess.FEN(string(moves))
 		if err != nil {
-			log.Fatalf("watcher: unable to read PGN: %v\n", err)
+			log.Fatalf("watcher: unable to read fen: %v\n", err)
 			return
 		}
-		if len(games) != 1 {
-			log.Fatalf("watcher: malformed moves file\n")
-			return
-		}
 
-		var board map[chess.Square]chess.Piece
-		board = games[0].Position().Board().SquareMap()
-//		if player == "black" {
-//			board = games[0].Position().Board().Flip(chess.LeftRight).SquareMap()
-//			board = games[0].Position().Board().Flip(chess.LeftRight).Flip(chess.UpDown).SquareMap()
-//		} else {
-//			board = games[0].Position().Board().Flip(chess.UpDown).SquareMap()
-//			board = games[0].Position().Board().SquareMap()
-//		}
+		game := chess.NewGame(fen)
+		board := game.Position().Board().SquareMap()
 		gameStatus := GameStatus {
 			Status: status,
 			Msg: msg,
 			TimeWhite: timeWhite,
 			TimeBlack: timeBlack,
-			Game: games[0],
+			Game: game,
 			Board: board,
 			LastStatus: nil,
 		}
@@ -460,16 +449,16 @@
 }
 
 const (
+	MenuStart	=	"start"
 	MenuDraw	=	"offer draw"
 	MenuResign	=	"resign"
-	MenuStart	=	"start"
 //	MenuTime	=	"set game time"
 )
 
 const (
-	MenuDrawHit	=	iota
+	MenuStartHit	=	iota
 	MenuResignHit
-	MenuStartHit
+	MenuDrawHit
 //	MenuTimeHit
 )
 
@@ -538,9 +527,9 @@
 
 	menu := &draw.Menu {
 		Item:	[]string {
+			MenuStart,
 			MenuDraw,
 			MenuResign,
-			MenuStart,
 //			MenuTime,
 		},
 	}
--- a/game.go
+++ b/game.go
@@ -293,6 +293,33 @@
 	return g.board.String() + "\n"
 }
 
+func (g *Game) GetFEN() string {
+	g.RLock()
+	defer g.RUnlock()
+
+	return g.board.FEN() + "\n"
+}
+
+func (g *Game) LoadFEN(fens string) error {
+	g.RLock()
+	if g.state != NewState {
+		return fmt.Errorf("game already started")
+	}
+	g.RUnlock()
+
+	g.Lock()
+	defer g.Unlock()
+
+	fen, err := chess.FEN(fens)
+	if err != nil {
+		return err
+	}
+	g.board = chess.NewGame(fen)
+
+	return nil
+}
+
+
 // The GameRoom is responsible for games, and the communication
 // between the actual filesystem and them.
 type GameRoom struct {