commit ae9d814911481fe442b863633908c1ee64e64071
parent 17a65e6254f33ebf912ea1311913d04d63eed14b
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Fri, 31 Jan 2025 18:10:18 -0800
allow user to start a chess game from a given PGN
Diffstat:
4 files changed, 43 insertions(+), 10 deletions(-)
diff --git a/pkg/web/handlers/chess.go b/pkg/web/handlers/chess.go
@@ -59,13 +59,14 @@ func ChessHandler(c echo.Context) error {
if c.Request().Method == http.MethodPost {
data.Username = database.Username(c.Request().PostFormValue("username"))
data.Color = c.Request().PostFormValue("color")
+ data.Pgn = c.Request().PostFormValue("pgn")
player1 := *authUser
player2, err := db.GetUserByUsername(data.Username)
if err != nil {
data.Error = "invalid username"
return c.Render(http.StatusOK, "chess", data)
}
- if _, err := interceptors.ChessInstance.NewGame1("", config.GeneralRoomID, player1, player2, data.Color); err != nil {
+ if _, err := interceptors.ChessInstance.NewGameWithPgn("", config.GeneralRoomID, player1, player2, data.Color, data.Pgn); err != nil {
data.Error = err.Error()
return c.Render(http.StatusOK, "chess", data)
}
@@ -558,7 +559,7 @@ func ChessGameHandler(c echo.Context) error {
db := c.Get("database").(*database.DkfDB)
user1, _ := db.GetUserByID(1)
user2, _ := db.GetUserByID(30814)
- if _, err := interceptors.ChessInstance.NewGame(key, user1, user2); err != nil {
+ if _, err := interceptors.ChessInstance.NewGame(key, user1, user2, ""); err != nil {
logrus.Error(err)
return c.Redirect(http.StatusFound, "/")
}
diff --git a/pkg/web/handlers/data.go b/pkg/web/handlers/data.go
@@ -961,6 +961,7 @@ type chessData struct {
Error string
Username database.Username
Color string
+ Pgn string
}
type TmpTable struct {
diff --git a/pkg/web/handlers/interceptors/chess.go b/pkg/web/handlers/interceptors/chess.go
@@ -89,12 +89,31 @@ func newChessPlayer(player database.User) *ChessPlayer {
return p
}
-func newChessGame(gameKey string, player1, player2 database.User, dbChessGame *database.ChessGame) *ChessGame {
+func newChessGame(gameKey string, player1, player2 database.User, dbChessGame *database.ChessGame, pgn string) (*ChessGame, error) {
g := new(ChessGame)
g.DbChessGame = dbChessGame
g.CreatedAt = time.Now()
g.Key = gameKey
- g.Game = chess.NewGame()
+ options := make([]func(*chess.Game), 0)
+ if pgn != "" {
+ if strings.HasSuffix(pgn, " 1-0") ||
+ strings.HasSuffix(pgn, " 0-1") ||
+ strings.HasSuffix(pgn, " 1/2-1/2") {
+ return nil, errors.New("pgn should have no outcome")
+ }
+ if !strings.HasSuffix(pgn, " *") {
+ pgn += " *"
+ }
+ p, err := chess.PGN(strings.NewReader(pgn))
+ if err != nil {
+ return nil, err
+ }
+ options = append(options, p)
+ }
+ g.Game = chess.NewGame(options...)
+ if g.Game.Outcome() != chess.NoOutcome {
+ return nil, errors.New("invalid pgn, outcome should be 'NoOutcome'")
+ }
if dbChessGame.PGN != "" {
pgnOpt, _ := chess.PGN(strings.NewReader(dbChessGame.PGN))
g.Game = chess.NewGame(pgnOpt)
@@ -103,7 +122,7 @@ func newChessGame(gameKey string, player1, player2 database.User, dbChessGame *d
g.Player1 = newChessPlayer(player1)
g.Player2 = newChessPlayer(player2)
g.piecesCache = InitPiecesCache(g.Game.Moves())
- return g
+ return g, nil
}
type Chess struct {
@@ -662,7 +681,10 @@ func (b *Chess) GetGame(key string) (*ChessGame, error) {
}
player1, _ := b.db.GetUserByID(dbChessGame.WhiteUserID)
player2, _ := b.db.GetUserByID(dbChessGame.BlackUserID)
- g := newChessGame(key, player1, player2, dbChessGame)
+ g, err := newChessGame(key, player1, player2, dbChessGame, "")
+ if err != nil {
+ return nil, err
+ }
b.games[key] = g
return g, nil
}
@@ -680,6 +702,10 @@ func (b *Chess) GetGames() (out []ChessGame) {
}
func (b *Chess) NewGame1(roomKey string, roomID database.RoomID, player1, player2 database.User, color string) (*ChessGame, error) {
+ return b.NewGameWithPgn(roomKey, roomID, player1, player2, color, "")
+}
+
+func (b *Chess) NewGameWithPgn(roomKey string, roomID database.RoomID, player1, player2 database.User, color, pgn string) (*ChessGame, error) {
if player1.ID == player2.ID {
return nil, errors.New("can't play yourself")
}
@@ -691,7 +717,7 @@ func (b *Chess) NewGame1(roomKey string, roomID database.RoomID, player1, player
}
key := uuid.New().String()
- g, err := b.NewGame(key, player1, player2)
+ g, err := b.NewGame(key, player1, player2, pgn)
if err != nil {
return nil, err
}
@@ -701,12 +727,15 @@ func (b *Chess) NewGame1(roomKey string, roomID database.RoomID, player1, player
return g, nil
}
-func (b *Chess) NewGame(gameKey string, user1, user2 database.User) (*ChessGame, error) {
+func (b *Chess) NewGame(gameKey string, user1, user2 database.User, pgn string) (*ChessGame, error) {
dbChessGame, err := b.db.CreateChessGame(gameKey, user1.ID, user2.ID)
if err != nil {
return nil, err
}
- g := newChessGame(gameKey, user1, user2, dbChessGame)
+ g, err := newChessGame(gameKey, user1, user2, dbChessGame, pgn)
+ if err != nil {
+ return nil, err
+ }
b.Lock()
b.games[gameKey] = g
b.Unlock()
diff --git a/pkg/web/public/views/pages/chess.gohtml b/pkg/web/public/views/pages/chess.gohtml
@@ -4,7 +4,7 @@
<div class="container">
<h3>Games</h3>
<p>You can create a chess game with someone by using the <code>/chess username</code> command.</p>
- <form method="post" style="width: 500px;">
+ <form method="post" style="width: 700px;">
<input type="hidden" name="csrf" value="{{ .CSRF }}" />
<div class="input-group">
<input style="width: 200px;" type="text" name="username" value="{{ .Data.Username }}" placeholder="opponent username" class="form-control form-control-sm{{ if .Data.Error }} is-invalid{{ end }}" />
@@ -15,6 +15,8 @@
<option value="r"{{ if eq .Data.Color "r" }}} selected{{ end }}>Random</option>
</select>
+ <input type="text" name="pgn" value="{{ .Data.Pgn }}" placeholder="PGN" aria-label="pgn" class="form-control form-control-sm" />
+
<div class="input-group-append">
<button class="btn btn-sm btn-primary">Create chess game</button>
</div>