dkforest

A forum and chat platform (onion)
git clone https://git.dasho.dev/n0tr1v/dkforest.git
Log | Files | Refs | LICENSE

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:
Mpkg/web/handlers/chess.go | 5+++--
Mpkg/web/handlers/data.go | 1+
Mpkg/web/handlers/interceptors/chess.go | 43++++++++++++++++++++++++++++++++++++-------
Mpkg/web/public/views/pages/chess.gohtml | 4+++-
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>