tic-tac-toe/main.go
2023-12-30 00:15:11 +02:00

209 lines
4.4 KiB
Go

package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"strconv"
"github.com/foolin/goview/supports/ginview"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
func main() {
db, err := connectDB()
if err != nil {
log.Fatal(err)
}
defer db.Close()
r := gin.Default()
r.HTMLRender = ginview.Default()
store := cookie.NewStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store))
r.StaticFile("/favicon.ico", "./assets/favicon.ico")
r.StaticFile("/sgs.png", "./assets/sgs.png")
r.StaticFile("/en.png", "./assets/en.png")
r.StaticFile("/cross.png", "./assets/cross.png")
r.StaticFile("/nought.png", "./assets/nought.png")
r.GET("/", func(c *gin.Context) {
lang := getLang(c)
c.Redirect(http.StatusMovedPermanently, fmt.Sprintf("/%s/0/x", lang))
})
r.GET("/:lang", func(c *gin.Context) {
lang := getLang(c)
c.Redirect(http.StatusMovedPermanently, fmt.Sprintf("/%s/0/x", lang))
})
r.GET("/:lang/:ai_second/:game", func(c *gin.Context) {
lang := getLang(c)
gameFlow, gameState := getGameFlow(c)
aiSecond, err := strconv.ParseBool(c.Param("ai_second"))
if err != nil {
aiSecond = false
}
game := newGame(gameFlow, aiSecond)
if gameState == GameStateInProgress && !game.isFinished() {
gameFlow = gameFlow + getNewStep(db, game)
game = newGame(gameFlow, aiSecond)
}
if game.isFinished() {
saveGameLog(db, game)
}
stats := getStats(db)
fmt.Println(game)
c.HTML(http.StatusOK, "index", gin.H{
"title": "Index title!",
"lang": lang,
"gameFlow": gameFlow,
"game": game,
"fields": [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9},
"fieldClass": func(game *Game, fieldNum int) string {
fieldClass := FieldClass[fieldNum]
if field, ok := game.fields[fieldNum]; ok {
if len(field.image) > 0 {
fieldClass = fieldClass + " " + field.image
}
if field.line {
fieldClass = fieldClass + " line"
}
}
return fieldClass
},
"disabled": func(game *Game, fieldNum int) bool {
if field, ok := game.fields[fieldNum]; ok {
return field.disabled
}
return false
},
"total": stats["total"],
"aiWon": stats["aiWon"],
"aiWonPercent": stats["aiWonPercent"],
"draw": stats["draw"],
"drawPercent": stats["drawPercent"],
"lastAiLost": stats["lastAiLost"],
})
})
r.Run()
}
func getLang(c *gin.Context) string {
session := sessions.Default(c)
lang := session.Get("lang").(string)
pLang, hasParam := c.Params.Get("lang")
if hasParam && pLang != lang {
lang = pLang
session.Set("lang", lang)
session.Save()
}
if lang != "sgs" && lang != "en" {
lang = "sgs"
session.Set("lang", lang)
session.Save()
}
return lang
}
func getGameFlow(c *gin.Context) (string, int) {
gameFlow := c.Param("game")
if "0" == gameFlow {
return gameFlow, GameStateWaitingUser
}
if "x" == gameFlow {
return "", GameStateInProgress
}
if "0" == string(gameFlow[0]) {
gameFlow = string(gameFlow[1:])
}
if len(gameFlow) >= 9 {
return gameFlow, GameStateEnded
}
return gameFlow, GameStateInProgress
}
func getNewStep(db *sql.DB, game *Game) string {
nextStep := getWinnerNextStep(db, game)
if "" != nextStep {
return nextStep
}
return difference("123456789", game.gameFlow)[0]
}
func difference(a, b string) []string {
mb := make(map[string]struct{}, len(b))
for _, x := range str2array(b) {
mb[x] = struct{}{}
}
var diff []string
for _, x := range str2array(a) {
if _, found := mb[x]; !found {
diff = append(diff, x)
}
}
return diff
}
func str2array(str string) []string {
chars := []rune(str)
var splited []string
for i := 0; i < len(chars); i++ {
splited = append(splited, string(chars[i]))
}
return splited
}
func getStats(db *sql.DB) map[string]string {
stats := make(map[string]string)
total, _ := getStatsCounter(db, "total")
stats["total"] = strconv.Itoa(total)
aiWon, _ := getStatsCounter(db, "ai_won")
stats["aiWon"] = strconv.Itoa(aiWon)
if total > 0 {
stats["aiWonPercent"] = fmt.Sprintf("%.2f%%", float64(aiWon)/float64(total)*100)
} else {
stats["aiWonPercent"] = "0%"
}
draw, _ := getStatsCounter(db, "draw")
stats["draw"] = strconv.Itoa(draw)
if total > 0 {
stats["drawPercent"] = fmt.Sprintf("%.2f%%", float64(draw)/float64(total)*100)
} else {
stats["drawPercent"] = "0%"
}
lastAiLost, _ := getStatsComment(db, "last_ai_lost")
stats["lastAiLost"] = lastAiLost
return stats
}