commit 001cb7bcdf4ca6f74f99fd27a88906e91a8b3f54
parent 882d243fd610fe5fcbd8deadd03f68d0394e6d76
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Wed, 14 Jun 2023 01:42:46 -0700
save chess in database
Diffstat:
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)