dkforest

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

commit 001cb7bcdf4ca6f74f99fd27a88906e91a8b3f54
parent 882d243fd610fe5fcbd8deadd03f68d0394e6d76
Author: n0tr1v <n0tr1v@protonmail.com>
Date:   Wed, 14 Jun 2023 01:42:46 -0700

save chess in database

Diffstat:
Acmd/dkf/migrations/142.sql | 24++++++++++++++++++++++++
Apkg/database/tableChessGames.go | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mpkg/web/handlers/chess.go | 25++++++++++++++++++++-----
Mpkg/web/handlers/interceptors/chess.go | 60++++++++++++++++++++++++++++++++++++++++++++----------------
4 files changed, 136 insertions(+), 21 deletions(-)

diff --git a/cmd/dkf/migrations/142.sql b/cmd/dkf/migrations/142.sql @@ -0,0 +1,24 @@ +-- +migrate Up +CREATE TABLE IF NOT EXISTS chess_games ( + id INTEGER NOT NULL PRIMARY KEY, + uuid VARCHAR(100) UNIQUE NOT NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP, + white_user_id INTEGER NOT NULL, + black_user_id INTEGER NOT NULL, + pgn TEXT NOT NULL, + outcome VARCHAR(20) NOT NULL DEFAULT '*', + accuracy_white REAL NOT NULL DEFAULT 0, + accuracy_black REAL NOT NULL DEFAULT 0, + CONSTRAINT chess_games_white_user_id_fk + FOREIGN KEY (white_user_id) + REFERENCES users (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT chess_games_black_user_id_fk + FOREIGN KEY (black_user_id) + REFERENCES users (id) + ON DELETE CASCADE + ON UPDATE CASCADE); + +-- +migrate Down diff --git a/pkg/database/tableChessGames.go b/pkg/database/tableChessGames.go @@ -0,0 +1,48 @@ +package database + +import ( + "github.com/sirupsen/logrus" + "time" +) + +type ChessGame struct { + ID int64 + UUID string + WhiteUserID UserID + BlackUserID UserID + PGN string + Outcome string + AccuracyWhite float64 + AccuracyBlack float64 + CreatedAt time.Time + UpdatedAt time.Time +} + +func (d *DkfDB) CreateChessGame(uuid string, whiteUserID, blackUserID UserID) (*ChessGame, error) { + chessGame := ChessGame{ + UUID: uuid, + WhiteUserID: whiteUserID, + BlackUserID: blackUserID, + Outcome: "*", + } + err := d.db.Create(&chessGame).Error + return &chessGame, err +} + +func (d *DkfDB) GetChessGame(uuid string) (*ChessGame, error) { + out := ChessGame{} + err := d.db.First(&out, "uuid = ?", uuid).Error + return &out, err +} + +// Save chessGame in the database +func (g *ChessGame) Save(db *DkfDB) error { + return db.db.Save(g).Error +} + +// DoSave chessGame in the database, ignore error +func (g *ChessGame) DoSave(db *DkfDB) { + if err := g.Save(db); err != nil { + logrus.Error(err) + } +} diff --git a/pkg/web/handlers/chess.go b/pkg/web/handlers/chess.go @@ -115,8 +115,12 @@ func ChessHandler(c echo.Context) error { func ChessGameFormHandler(c echo.Context) error { key := c.Param("key") csrf, _ := c.Get("csrf").(string) + db := c.Get("database").(*database.DkfDB) authUser := c.Get("authUser").(*database.User) - g := interceptors.ChessInstance.GetGame(key) + g, err := interceptors.ChessInstance.GetGame(key) + if err != nil { + return c.NoContent(http.StatusOK) + } game := g.Game isFlipped := g.IsBlack(authUser.ID) @@ -158,6 +162,9 @@ func ChessGameFormHandler(c echo.Context) error { } else if btnSubmit == "resign-confirm" { resignColor := utils.Ternary(isFlipped, chess.Black, chess.White) game.Resign(resignColor) + g.DbChessGame.PGN = game.String() + g.DbChessGame.Outcome = game.Outcome().String() + g.DbChessGame.DoSave(db) interceptors.ChessPubSub.Pub(key, interceptors.ChessMove{}) } else { @@ -224,16 +231,24 @@ func ChessGameHandler(c echo.Context) error { authUser := c.Get("authUser").(*database.User) key := c.Param("key") - g := interceptors.ChessInstance.GetGame(key) + g, _ := interceptors.ChessInstance.GetGame(key) if g == nil { if debugChess && config.Development.IsTrue() { // Chess debug db := c.Get("database").(*database.DkfDB) user1, _ := db.GetUserByID(1) user2, _ := db.GetUserByID(30814) - interceptors.ChessInstance.NewGame(key, user1, user2) - g = interceptors.ChessInstance.GetGame(key) - g.MakeMoves(queenSideCastleGame) + if _, err := interceptors.ChessInstance.NewGame(key, user1, user2); err != nil { + logrus.Error(err) + return c.Redirect(http.StatusFound, "/") + } + var err error + g, err = interceptors.ChessInstance.GetGame(key) + if err != nil { + logrus.Error(err) + return c.Redirect(http.StatusFound, "/") + } + g.MakeMoves(queenSideCastleGame, db) } else { return c.Redirect(http.StatusFound, "/") } diff --git a/pkg/web/handlers/interceptors/chess.go b/pkg/web/handlers/interceptors/chess.go @@ -46,6 +46,7 @@ type ChessPlayer struct { } type ChessGame struct { + DbChessGame *database.ChessGame Key string Game *chess.Game lastUpdated time.Time @@ -64,12 +65,17 @@ func newChessPlayer(player database.User) *ChessPlayer { return p } -func newChessGame(gameKey string, player1, player2 database.User) *ChessGame { +func newChessGame(gameKey string, player1, player2 database.User, dbChessGame *database.ChessGame) *ChessGame { g := new(ChessGame) + g.DbChessGame = dbChessGame g.piecesCache = make(map[chess.Square]string) g.CreatedAt = time.Now() g.Key = gameKey g.Game = chess.NewGame() + if dbChessGame.PGN != "" { + pgnOpt, _ := chess.PGN(strings.NewReader(dbChessGame.PGN)) + g.Game = chess.NewGame(pgnOpt) + } g.lastUpdated = time.Now() g.Player1 = newChessPlayer(player1) g.Player2 = newChessPlayer(player2) @@ -431,13 +437,21 @@ func (g *ChessGame) DrawSpectatorCard(isFlipped, soundsEnabled bool) string { return g.drawPlayerCard("", isFlipped, true, false, soundsEnabled) } -func (b *Chess) GetGame(key string) *ChessGame { +func (b *Chess) GetGame(key string) (*ChessGame, error) { b.Lock() defer b.Unlock() + dbChessGame, err := b.db.GetChessGame(key) + if err != nil { + return nil, err + } if g, ok := b.games[key]; ok { - return g + return g, nil } - return nil + player1, _ := b.db.GetUserByID(dbChessGame.WhiteUserID) + player2, _ := b.db.GetUserByID(dbChessGame.BlackUserID) + g := newChessGame(key, player1, player2, dbChessGame) + b.games[key] = g + return g, nil } func (b *Chess) GetGames() (out []ChessGame) { @@ -458,25 +472,26 @@ func (b *Chess) NewGame1(roomKey string, roomID database.RoomID, player1, player } key := uuid.New().String() - g := b.NewGame(key, player1, player2) + g, err := b.NewGame(key, player1, player2) + if err != nil { + return nil, err + } zeroUser := dutils.GetZeroUser(b.db) dutils.SendNewChessGameMessages(b.db, key, roomKey, roomID, zeroUser, player1, player2) return g, nil } -func (b *Chess) NewGame(gameKey string, user1, user2 database.User) *ChessGame { - g := newChessGame(gameKey, user1, user2) +func (b *Chess) NewGame(gameKey string, user1, user2 database.User) (*ChessGame, error) { + dbChessGame, err := b.db.CreateChessGame(gameKey, user1.ID, user2.ID) + if err != nil { + return nil, err + } + g := newChessGame(gameKey, user1, user2, dbChessGame) b.Lock() b.games[gameKey] = g b.Unlock() - return g -} - -func (b *Chess) newGame(gameKey string, user1, user2 database.User) *ChessGame { - g := newChessGame(gameKey, user1, user2) - b.games[gameKey] = g - return g + return g, nil } func (b *Chess) SendMove(gameKey string, userID database.UserID, g *ChessGame, c echo.Context) error { @@ -543,6 +558,9 @@ func (b *Chess) SendMove(gameKey string, userID database.UserID, g *ChessGame, c _ = game.MoveStr(moveStr) g.lastUpdated = time.Now() + g.DbChessGame.PGN = game.String() + g.DbChessGame.Outcome = game.Outcome().String() + g.DbChessGame.DoSave(b.db) idStr1 := piecesCache[mov.S1()] idStr2 := piecesCache[mov.S2()] idStr3 := "" @@ -591,11 +609,14 @@ func (g *ChessGame) IsPlayer(userID database.UserID) bool { return g.Player1.ID == userID || g.Player2.ID == userID } -func (g *ChessGame) MakeMoves(movesStr string) { +func (g *ChessGame) MakeMoves(movesStr string, db *database.DkfDB) { moves := strings.Split(movesStr, " ") for _, move := range moves { g.MoveStr(move) } + g.DbChessGame.PGN = g.Game.String() + g.DbChessGame.Outcome = g.Game.Outcome().String() + g.DbChessGame.DoSave(db) } func (g *ChessGame) MoveStr(m string) { @@ -753,6 +774,10 @@ func Test() { cps := make([]int, 0) for idx, position := range positions { + // First position is the board without any move played + if idx == 0 { + continue + } cmdPos := uci.CmdPosition{Position: position} cmdGo := uci.CmdGo{MoveTime: time.Second / 20} if err := eng.Run(cmdPos, cmdGo); err != nil { @@ -760,8 +785,11 @@ func Test() { } res := eng.SearchResults() cp := res.Info.Score.CP + if idx%2 != 0 { + cp *= -1 + } cps = append(cps, cp) - fmt.Printf("%d/%d %d %d\n", idx, len(positions), idx%2, cp) + fmt.Printf("%d: %d/%d %d %d\n", idx/2, idx, len(positions), idx%2, cp) } s := make([]string, 0)