commit 79629fa300022a89071c99e7bebf2e926827dc76
parent fa56e693ce1d1d666be5e05f267a90f20320249d
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Sun, 11 Jun 2023 11:38:49 -0700
insanity
Diffstat:
2 files changed, 215 insertions(+), 79 deletions(-)
diff --git a/pkg/web/handlers/handlers.go b/pkg/web/handlers/handlers.go
@@ -4871,6 +4871,42 @@ html, body {
}
</style>`
+func chessGamefoolMate(g *interceptors.ChessGame) {
+ g.MoveStr("f3")
+ g.MoveStr("e5")
+ g.MoveStr("g4")
+}
+
+func chessGameCheck(g *interceptors.ChessGame) {
+ g.MoveStr("Nc3")
+ g.MoveStr("h6")
+ g.MoveStr("Nb5")
+ g.MoveStr("h5")
+}
+
+func chessGamePromoW(g *interceptors.ChessGame) {
+ g.MoveStr("h4")
+ g.MoveStr("g5")
+ g.MoveStr("hxg5")
+ g.MoveStr("h5")
+ g.MoveStr("g6")
+ g.MoveStr("h4")
+ g.MoveStr("g7")
+ g.MoveStr("h3")
+}
+
+func chessGamePromoB(g *interceptors.ChessGame) {
+ g.MoveStr("a3")
+ g.MoveStr("c5")
+ g.MoveStr("a4")
+ g.MoveStr("c4")
+ g.MoveStr("a5")
+ g.MoveStr("c3")
+ g.MoveStr("a6")
+ g.MoveStr("cxb2")
+ g.MoveStr("axb7")
+}
+
func ChessGameHandler(c echo.Context) error {
authUser := c.Get("authUser").(*database.User)
db := c.Get("database").(*database.DkfDB)
@@ -4883,6 +4919,7 @@ func ChessGameHandler(c echo.Context) error {
user2, _ := db.GetUserByID(30814)
interceptors.ChessInstance.NewGame(key, user1, user2)
g = interceptors.ChessInstance.GetGame(key)
+ chessGamePromoB(g)
//return c.Redirect(http.StatusFound, "/")
}
@@ -4893,7 +4930,7 @@ func ChessGameHandler(c echo.Context) error {
if msg == "resign" {
resignColor := utils.Ternary(isFlipped, chess.Black, chess.White)
g.Game.Resign(resignColor)
- interceptors.ChessPubSub.Pub(key, true)
+ interceptors.ChessPubSub.Pub(key, interceptors.ChessMove{})
} else {
if err := interceptors.ChessInstance.SendMove(key, authUser.ID, g, c); err != nil {
logrus.Error(err)
@@ -4914,7 +4951,7 @@ func ChessGameHandler(c echo.Context) error {
isYourTurn := isYourTurnFn()
// If you are not a spectator, and it's your turn to play, we just render the form directly.
- if isYourTurn {
+ if isYourTurn || g.Game.Outcome() != chess.NoOutcome {
c.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8)
c.Response().WriteHeader(http.StatusOK)
_, _ = c.Response().Write([]byte(cssReset))
@@ -4961,10 +4998,11 @@ Loop:
}
if g.Game.Outcome() != chess.NoOutcome {
+ _, _ = c.Response().Write([]byte(`<meta http-equiv="refresh" content="0" />`))
break
}
- _, _, err := sub.ReceiveTimeout2(1*time.Second, quit)
+ _, payload, err := sub.ReceiveTimeout2(1*time.Second, quit)
if err != nil {
if err == pubsub.ErrCancelled {
break Loop
@@ -4974,14 +5012,68 @@ Loop:
i++
- var card1 string
- if isSpectator {
- card1 = g.DrawSpectatorCard(isFlipped)
+ //id1 := g.PiecesCache[payload.Move.S1()]
+ x1 := int(payload.Move.S1().File())
+ y1 := int(payload.Move.S1().Rank())
+ x := int(payload.Move.S2().File())
+ y := int(payload.Move.S2().Rank())
+ if isFlipped {
+ x1 = 7 - x1
+ x = 7 - x
} else {
- card1 = g.DrawPlayerCard(isFlipped, isYourTurnFn())
+ y1 = 7 - y1
+ y = 7 - y
+ }
+
+ _, _ = c.Response().Write([]byte(fmt.Sprintf(`<style>@keyframes move_anim_%d { from { left: calc(%d*12.5%%); top: calc(%d*12.5%%); } to { left: calc(%d*12.5%%); top: calc(%d*12.5%%); } }</style>`, i, x1, y1, x, y)))
+ _, _ = c.Response().Write([]byte(fmt.Sprintf(`<style>#%s { animation-name: move_anim_%d; animation-duration: 400ms; animation-fill-mode: forwards; }</style>`, payload.IDStr1, i)))
+ if payload.IDStr2 != "" {
+ _, _ = c.Response().Write([]byte(`<style>#` + payload.IDStr2 + ` { display: none; }</style>`))
+ }
+
+ if payload.Move.Promo() != chess.NoPieceType {
+ go func(payload interceptors.ChessMove, c echo.Context) {
+ select {
+ case <-time.After(400 * time.Millisecond):
+ case <-quit:
+ return
+ }
+ cTurn := utils.Ternary(payload.Turn == chess.White, "w", "b")
+ promoImg := "/public/img/chess/" + cTurn + "Q.png"
+ switch payload.Move.Promo() {
+ case chess.Queen:
+ promoImg = "/public/img/chess/" + cTurn + "Q.png"
+ case chess.Rook:
+ promoImg = "/public/img/chess/" + cTurn + "R.png"
+ case chess.Bishop:
+ promoImg = "/public/img/chess/" + cTurn + "B.png"
+ case chess.Knight:
+ promoImg = "/public/img/chess/" + cTurn + "N.png"
+ }
+ _, _ = c.Response().Write([]byte(fmt.Sprintf(`<style>#%s { background-image: url("%s") !important; }</style>`, payload.IDStr1, promoImg)))
+ c.Response().Flush()
+ }(payload, c)
}
- _, _ = c.Response().Write([]byte(fmt.Sprintf(`<style>#div_%d { display: none; }</style>`, i-1)))
- _, _ = c.Response().Write([]byte(fmt.Sprintf(`<div id="div_%d">%s</div>`, i, card1)))
+
+ _, _ = c.Response().Write([]byte(`<style>.square { background-color: transparent !important; }</style>`))
+ _, _ = c.Response().Write([]byte(`<style>.square_` + strconv.Itoa(int(payload.Move.S1())) + `, .square_` + strconv.Itoa(int(payload.Move.S2())) + ` { background-color: rgba(0, 255, 0, 0.2) !important; }</style>`))
+
+ _, _ = c.Response().Write([]byte(`<style>#img_4 { background-color: transparent !important; }</style>`))
+ _, _ = c.Response().Write([]byte(`<style>#img_60 { background-color: transparent !important; }</style>`))
+ if payload.CheckW {
+ _, _ = c.Response().Write([]byte(`<style>#img_4 { background-color: rgba(255, 0, 0, 0.4) !important; }</style>`))
+ } else if payload.CheckB {
+ _, _ = c.Response().Write([]byte(`<style>#img_60 { background-color: rgba(255, 0, 0, 0.4) !important; }</style>`))
+ }
+
+ //var card1 string
+ //if isSpectator {
+ // card1 = g.DrawSpectatorCard(isFlipped)
+ //} else {
+ // card1 = g.DrawPlayerCard(isFlipped, isYourTurnFn())
+ //}
+ //_, _ = c.Response().Write([]byte(fmt.Sprintf(`<style>#div_%d { display: none; }</style>`, i-1)))
+ //_, _ = c.Response().Write([]byte(fmt.Sprintf(`<div id="div_%d">%s</div>`, i, card1)))
c.Response().Flush()
}
return nil
diff --git a/pkg/web/handlers/interceptors/chess.go b/pkg/web/handlers/interceptors/chess.go
@@ -41,6 +41,7 @@ type ChessGame struct {
Player1 *ChessPlayer
Player2 *ChessPlayer
CreatedAt time.Time
+ PiecesCache map[chess.Square]string
}
func newChessPlayer(player database.User) *ChessPlayer {
@@ -54,12 +55,19 @@ func newChessPlayer(player database.User) *ChessPlayer {
func newChessGame(gameKey string, player1, player2 database.User) *ChessGame {
g := new(ChessGame)
+ g.PiecesCache = make(map[chess.Square]string)
g.CreatedAt = time.Now()
g.Key = gameKey
g.Game = chess.NewGame()
g.lastUpdated = time.Now()
g.Player1 = newChessPlayer(player1)
g.Player2 = newChessPlayer(player2)
+ for i := 0; i < 64; i++ {
+ sq := chess.Square(i)
+ if g.Game.Position().Board().Piece(sq) != chess.NoPiece {
+ g.PiecesCache[sq] = "img_" + strconv.Itoa(int(sq))
+ }
+ }
return g
}
@@ -99,7 +107,12 @@ const (
boardSize = 8 * sqSize
)
-func renderBoardHTML(position *chess.Position, isFlipped bool, imgB64 string, spectator bool) string {
+func (g *ChessGame) renderBoardHTML1(position *chess.Position, isFlipped bool, imgB64 string, spectator bool) string {
+ moves := g.Game.Moves()
+ var last *chess.Move
+ if len(moves) > 0 {
+ last = moves[len(moves)-1]
+ }
boardMap := position.Board().SquareMap()
out := `<style>
.newBoard {
@@ -113,10 +126,11 @@ func renderBoardHTML(position *chess.Position, isFlipped bool, imgB64 string, sp
}
.newBoard td {
}
-.newBoard img {
+.newBoard .img {
position: absolute;
width: 12.5%;
height: 12.5%;
+ background-size: 100%;
}
.idk {
width: 100%;
@@ -177,17 +191,29 @@ input[type=checkbox]:checked + label {
}
out += "<tr>"
}
- out += "<td>"
sq := chess.Square(id)
+ idStr := strconv.Itoa(id)
+ out += `<td class="square square_` + idStr + `"`
+ if last != nil && (last.S1() == sq || last.S2() == sq) {
+ out += ` style="background-color: rgba(0, 255, 0, 0.2);"`
+ }
+ out += ">"
sqPiece := boardMap[sq]
p := sqPiece
- out += `<div class="idk">`
if p != chess.NoPiece {
- out += `<img src="/public/img/chess/` + p.Color().String() + pieceTypeMap[p.Type()] + `.png" alt="" />`
+ x := i % 8
+ y := i / 8
+ pidStr := g.PiecesCache[sq]
+ //out += `<img id="` + pidStr + `" src="/public/img/chess/` + p.Color().String() + pieceTypeMap[p.Type()] + `.png" alt="" style="left: calc(` + strconv.Itoa(x) + `*12.5%); top: calc(` + strconv.Itoa(y) + `*12.5%);`
+ out += `<div id="` + pidStr + `" class="img" style="left: calc(` + strconv.Itoa(x) + `*12.5%); top: calc(` + strconv.Itoa(y) + `*12.5%); background-image: url(/public/img/chess/` + p.Color().String() + pieceTypeMap[p.Type()] + `.png);`
+ if last != nil && p.Color() == g.Game.Position().Turn() && p.Type() == chess.King && last.HasTag(chess.Check) {
+ out += ` background-color: rgba(255, 0, 0, 0.4);`
+ }
+ out += `"></div>`
}
+ out += `<div class="idk">`
if !spectator {
- idStr := strconv.Itoa(id)
- out += `<input name="sq_` + idStr + `" ID="sq_` + idStr + `" type="checkbox" value="1" /><label for="sq_` + idStr + `"></label>`
+ out += `<input name="sq_` + idStr + `" id="sq_` + idStr + `" type="checkbox" value="1" /><label for="sq_` + idStr + `"></label>`
}
out += "</div>"
out += "</td>"
@@ -236,26 +262,15 @@ func renderSquare(ctx *gg.Context, sq chess.Square, last *chess.Move, turn chess
ctx.Fill()
ctx.Pop()
// Draw previous move
- if last != nil {
- if last.S1() == sq || last.S2() == sq {
- ctx.Push()
- ctx.SetRGBA(0, 1, 0, 0.1)
- ctx.DrawRectangle(float64(x), float64(y), sqSize, sqSize)
- ctx.Fill()
- ctx.Pop()
- }
- // Draw check
- p := sqPiece
- if p != chess.NoPiece {
- if p.Type() == chess.King && p.Color() == turn && last.HasTag(chess.Check) {
- ctx.Push()
- ctx.SetRGBA(1, 0, 0, 0.4)
- ctx.DrawRectangle(float64(x), float64(y), sqSize, sqSize)
- ctx.Fill()
- ctx.Pop()
- }
- }
- }
+ //if last != nil {
+ // if last.S1() == sq || last.S2() == sq {
+ // ctx.Push()
+ // ctx.SetRGBA(0, 1, 0, 0.1)
+ // ctx.DrawRectangle(float64(x), float64(y), sqSize, sqSize)
+ // ctx.Fill()
+ // ctx.Pop()
+ // }
+ //}
ctx.Push()
ctx.SetColor(color.RGBA{R: 0, G: 0, B: 0, A: 180})
@@ -266,15 +281,6 @@ func renderSquare(ctx *gg.Context, sq chess.Square, last *chess.Move, turn chess
ctx.DrawString(sq.Rank().String(), float64(x+1), float64(y+11))
}
ctx.Pop()
-
- //// draw piece
- //p := sqPiece
- //if p != chess.NoPiece {
- // img := getFile("img/chess/" + p.Color().String() + pieceTypeMap[p.Type()] + ".png")
- // ctx.Push()
- // ctx.DrawImage(img, x, y)
- // ctx.Pop()
- //}
}
var pieceTypeMap = map[chess.PieceType]string{
@@ -359,7 +365,7 @@ input[type=checkbox]:checked + label {
func (g *ChessGame) renderBoardHTML(isFlipped bool, imgB64 string, spectator bool) string {
position := g.Game.Position()
- out := renderBoardHTML(position, isFlipped, imgB64, spectator)
+ out := g.renderBoardHTML1(position, isFlipped, imgB64, spectator)
return out
}
@@ -414,34 +420,30 @@ func (g *ChessGame) drawPlayerCard(roomName string, isBlack, isYourTurn bool) st
<button type="submit" style="background-color: #aaa; margin: 5px 0;">Resign</button>
</form>
<div>
- {{ if .IsYourTurn }}
- <form method="post"{{ if .InChat }} action="/api/v1/chess"{{ end }} style="aspect-ratio: 1/1; height: 70%;">
- {{ .Table }}
- {{ if .InChat }}
- <input type="hidden" name="room" value="{{ .RoomName }}" />
- <input type="hidden" name="enemyUsername" value="{{ .Username }}" />
- <input type="hidden" name="move" value="move" />
- {{ else }}
- <input type="hidden" name="message" value="/pm {{ .Username }} /c move" />
- {{ end }}
- <div style="width: 100%; display: flex; margin: 5px 0;">
- <div>
- <button type="submit" style="background-color: #aaa;">Move</button>
- </div>
- <div style="margin-left: auto;">
- <span style="color: #aaa; margin-left: 20px;">Promo:</span>
- <select name="promotion" style="background-color: #aaa;">
- <option value="queen">Queen</option>
- <option value="rook">Rook</option>
- <option value="knight">Knight</option>
- <option value="bishop">Bishop</option>
- </select>
- </div>
- </div>
- </form>
- {{ else }}
+ <form method="post"{{ if .InChat }} action="/api/v1/chess"{{ end }} style="aspect-ratio: 1/1; height: 70%;">
{{ .Table }}
- {{ end }}
+ {{ if .InChat }}
+ <input type="hidden" name="room" value="{{ .RoomName }}" />
+ <input type="hidden" name="enemyUsername" value="{{ .Username }}" />
+ <input type="hidden" name="move" value="move" />
+ {{ else }}
+ <input type="hidden" name="message" value="/pm {{ .Username }} /c move" />
+ {{ end }}
+ <div style="width: 100%; display: flex; margin: 5px 0;">
+ <div>
+ <button type="submit" style="background-color: #aaa;">Move</button>
+ </div>
+ <div style="margin-left: auto;">
+ <span style="color: #aaa; margin-left: 20px;">Promo:</span>
+ <select name="promotion" style="background-color: #aaa;">
+ <option value="queen">Queen</option>
+ <option value="rook">Rook</option>
+ <option value="knight">Knight</option>
+ <option value="bishop">Bishop</option>
+ </select>
+ </div>
+ </div>
+ </form>
</div>
{{ end }}
</td>
@@ -470,7 +472,7 @@ func (g *ChessGame) drawPlayerCard(roomName string, isBlack, isYourTurn bool) st
"White": g.Player1,
"Black": g.Player2,
"Username": enemy.Username,
- "Table": template.HTML(g.renderBoardHTML(isBlack, imgB64, !isYourTurn)),
+ "Table": template.HTML(g.renderBoardHTML(isBlack, imgB64, false)),
"ImgB64": imgB64,
"Outcome": g.Game.Outcome().String(),
"GameOver": g.Game.Outcome() != chess.NoOutcome,
@@ -636,11 +638,13 @@ func (b *Chess) SendMove(gameKey string, userID database.UserID, g *ChessGame, c
var moveStr string
validMoves := g.Game.Position().ValidMoves()
var found bool
+ var mov chess.Move
for _, move := range validMoves {
if (move.S1() == selectedSquares[0] && move.S2() == selectedSquares[1] && (move.Promo() == chess.NoPieceType || move.Promo() == promo)) ||
(move.S1() == selectedSquares[1] && move.S2() == selectedSquares[0] && (move.Promo() == chess.NoPieceType || move.Promo() == promo)) {
moveStr = chess.AlgebraicNotation{}.Encode(g.Game.Position(), move)
found = true
+ mov = *move
break
}
}
@@ -649,13 +653,26 @@ func (b *Chess) SendMove(gameKey string, userID database.UserID, g *ChessGame, c
return fmt.Errorf("invalid move %s %s", selectedSquares[0], selectedSquares[1])
}
+ //fmt.Println(moveStr)
+
+ turn := g.Game.Position().Turn()
_ = g.Game.MoveStr(moveStr)
g.lastUpdated = time.Now()
- if g.Game.Outcome() != chess.NoOutcome {
- //delete(b.games, gameKey)
- }
- ChessPubSub.Pub(gameKey, true)
+ idStr1 := g.PiecesCache[mov.S1()]
+ idStr2 := g.PiecesCache[mov.S2()]
+ chessMov := ChessMove{
+ IDStr1: idStr1,
+ IDStr2: idStr2,
+ CheckW: g.Game.Position().Turn() == chess.White && mov.HasTag(chess.Check),
+ CheckB: g.Game.Position().Turn() == chess.Black && mov.HasTag(chess.Check),
+ Move: mov,
+ Turn: turn,
+ }
+ delete(g.PiecesCache, mov.S1())
+ delete(g.PiecesCache, mov.S2())
+ g.PiecesCache[mov.S2()] = idStr1
+ ChessPubSub.Pub(gameKey, chessMov)
// Notify (pm) the opponent that you made a move
if opponent.NotifyChessMove {
@@ -671,7 +688,34 @@ func (b *Chess) SendMove(gameKey string, userID database.UserID, g *ChessGame, c
return nil
}
-var ChessPubSub = pubsub.NewPubSub[bool]()
+func (g *ChessGame) MoveStr(m string) {
+ validMoves := g.Game.Position().ValidMoves()
+ var mov chess.Move
+ for _, move := range validMoves {
+ moveStr := chess.AlgebraicNotation{}.Encode(g.Game.Position(), move)
+ if moveStr == m {
+ mov = *move
+ break
+ }
+ }
+ idStr1 := g.PiecesCache[mov.S1()]
+ //idStr2 := g.PiecesCache[mov.S2()]
+ delete(g.PiecesCache, mov.S1())
+ delete(g.PiecesCache, mov.S2())
+ g.PiecesCache[mov.S2()] = idStr1
+ _ = g.Game.MoveStr(m)
+}
+
+type ChessMove struct {
+ IDStr1 string
+ IDStr2 string
+ CheckW bool
+ CheckB bool
+ Move chess.Move
+ Turn chess.Color
+}
+
+var ChessPubSub = pubsub.NewPubSub[ChessMove]()
func (b *Chess) InterceptMsg(cmd *command.Command) {
m := cRgx.FindStringSubmatch(cmd.Message)