added AI on db
This commit is contained in:
parent
04851913df
commit
2a18c27c02
8 changed files with 205 additions and 14 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -21,3 +21,4 @@
|
|||
# Go workspace file
|
||||
go.work
|
||||
|
||||
game.db
|
||||
|
|
108
db.go
Normal file
108
db.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func connectDB() (*sql.DB, error) {
|
||||
db, err := sql.Open("sqlite3", "./game.db")
|
||||
if err != nil {
|
||||
return db, err
|
||||
}
|
||||
|
||||
err = checkPreinstall(db)
|
||||
if err != nil {
|
||||
return db, err
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func checkPreinstall(db *sql.DB) error {
|
||||
sqlStmt := `
|
||||
CREATE TABLE IF NOT EXISTS Games (id INTEGER NOT NULL PRIMARY KEY, game_flow TEXT NOT NULL, first_won BOOL);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS game_flow_idx ON Games (game_flow);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS Stats (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, count INT, comment TEXT);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS stats_idx ON Stats (name);
|
||||
INSERT OR IGNORE INTO Stats (name, count) VALUES ('total', 0);
|
||||
INSERT OR IGNORE INTO Stats (name, count) VALUES ('ai_won', 0);
|
||||
INSERT OR IGNORE INTO Stats (name, count) VALUES ('draw', 0);
|
||||
INSERT OR IGNORE INTO Stats (name, comment) VALUES ('last_ai_lost', NULL);
|
||||
`
|
||||
_, err := db.Exec(sqlStmt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getStatsCounter(db *sql.DB, name string) (int, error) {
|
||||
stmt, err := db.Prepare("SELECT count FROM Stats WHERE name = ?")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
var count int
|
||||
err = stmt.QueryRow(name).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func getStatsComment(db *sql.DB, name string) (string, error) {
|
||||
stmt, err := db.Prepare("SELECT comment FROM Stats WHERE name = ?")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
var comment string
|
||||
err = stmt.QueryRow(name).Scan(&comment)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return comment, nil
|
||||
}
|
||||
|
||||
func saveGameLog(db *sql.DB, game *Game) {
|
||||
db.Exec("UPDATE Stats SET count=count+1 WHERE name='total';")
|
||||
if game.hasWinner() {
|
||||
db.Exec(
|
||||
"INSERT OR IGNORE INTO Games (game_flow, first_won) VALUES (?, ?);",
|
||||
game.gameFlow,
|
||||
game.isFirstWon(),
|
||||
)
|
||||
|
||||
if game.isAiWon() {
|
||||
db.Exec("UPDATE Stats SET count=count+1 WHERE name='ai_won';")
|
||||
} else {
|
||||
db.Exec("UPDATE Stats SET comment=datetime() WHERE name='last_ai_lost';")
|
||||
}
|
||||
} else {
|
||||
db.Exec("UPDATE Stats SET count=count+1 WHERE name='draw';")
|
||||
}
|
||||
}
|
||||
|
||||
func getWinnerNextStep(db *sql.DB, game *Game) string {
|
||||
stmt, err := db.Prepare("SELECT game_flow FROM Games WHERE game_flow LIKE ? AND first_won = ?")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
var gameFlow string
|
||||
err = stmt.QueryRow(game.gameFlow+"%", !game.aiSecond).Scan(&gameFlow)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return gameFlow[len(game.gameFlow) : len(game.gameFlow)+1]
|
||||
}
|
20
game.go
20
game.go
|
@ -167,6 +167,26 @@ func (game *Game) calculateState() {
|
|||
}
|
||||
}
|
||||
|
||||
func (game *Game) hasWinner() bool {
|
||||
return game.winner != WinnerNone
|
||||
}
|
||||
|
||||
func (game *Game) isAiWon() bool {
|
||||
return game.winner == WinnerAI
|
||||
}
|
||||
|
||||
func (game *Game) isFirstWon() bool {
|
||||
if game.winner == WinnerNone {
|
||||
return false
|
||||
}
|
||||
|
||||
if game.aiSecond && game.winner == WinnerUser {
|
||||
return true
|
||||
}
|
||||
|
||||
return !game.aiSecond && game.winner == WinnerAI
|
||||
}
|
||||
|
||||
func isSubArray(a, b []int) bool {
|
||||
mb := make(map[int]struct{}, len(b))
|
||||
for _, x := range b {
|
||||
|
|
3
go.mod
3
go.mod
|
@ -4,8 +4,9 @@ go 1.21.5
|
|||
|
||||
require (
|
||||
github.com/foolin/goview v0.3.0
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/gin-contrib/sessions v0.0.5
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
10
go.sum
10
go.sum
|
@ -9,6 +9,7 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhD
|
|||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/foolin/goview v0.3.0 h1:q5wKwXKEFb20dMRfYd59uj5qGCo7q4L9eVHHUjmMWrg=
|
||||
|
@ -23,6 +24,8 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
|
|||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
|
@ -33,6 +36,7 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
|||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
|
@ -58,6 +62,8 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
|||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
@ -67,6 +73,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
|||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
|
@ -78,6 +85,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
|
@ -115,10 +123,12 @@ golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
|||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||
|
|
67
main.go
67
main.go
|
@ -1,7 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
|
@ -12,6 +14,12 @@ import (
|
|||
)
|
||||
|
||||
func main() {
|
||||
db, err := connectDB()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
r := gin.Default()
|
||||
|
||||
r.HTMLRender = ginview.Default()
|
||||
|
@ -47,11 +55,16 @@ func main() {
|
|||
game := newGame(gameFlow, aiSecond)
|
||||
|
||||
if gameState == GameStateInProgress && !game.isFinished() {
|
||||
gameFlow = gameFlow + getNewClick(gameFlow)
|
||||
gameFlow = gameFlow + getNewStep(db, game)
|
||||
game = newGame(gameFlow, aiSecond)
|
||||
}
|
||||
|
||||
// if game ended - save results
|
||||
if game.isFinished() {
|
||||
saveGameLog(db, game)
|
||||
}
|
||||
|
||||
stats := getStats(db)
|
||||
fmt.Println(game)
|
||||
|
||||
c.HTML(http.StatusOK, "index", gin.H{
|
||||
"title": "Index title!",
|
||||
|
@ -79,11 +92,12 @@ func main() {
|
|||
|
||||
return false
|
||||
},
|
||||
"total": 8787,
|
||||
"aiWon": 232,
|
||||
"aiWonPercent": "3.5%",
|
||||
"draw": 6777,
|
||||
"drawPercent": "94.4%",
|
||||
"total": stats["total"],
|
||||
"aiWon": stats["aiWon"],
|
||||
"aiWonPercent": stats["aiWonPercent"],
|
||||
"draw": stats["draw"],
|
||||
"drawPercent": stats["drawPercent"],
|
||||
"lastAiLost": stats["lastAiLost"],
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -131,8 +145,13 @@ func getGameFlow(c *gin.Context) (string, int) {
|
|||
return gameFlow, GameStateInProgress
|
||||
}
|
||||
|
||||
func getNewClick(gameFlow string) string {
|
||||
return difference("123456789", gameFlow)[0]
|
||||
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 {
|
||||
|
@ -158,3 +177,33 @@ func str2array(str string) []string {
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<p>
|
||||
<b>Game:</b> {{.gameFlow}}<br>
|
||||
<b>Total plaied:</b> {{.total}}<br>
|
||||
<b>AI Won:</b> {{.aiWon}} {{.aiWonPercent}}<br>
|
||||
<b>Draw:</b> {{.draw}} {{.drawPercent}}<br>
|
||||
<b>AI Won:</b> {{.aiWon}} <sup class="text-danger">{{.aiWonPercent}}</sup><br>
|
||||
<b>Draw:</b> {{.draw}} <sup class="text-danger">{{.drawPercent}}</sup><br>
|
||||
<b>Last AI lost:</b> {{.lastAiLost}}<br>
|
||||
</p>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<p>
|
||||
<b>Žaidėms:</b> {{.gameFlow}}<br>
|
||||
<b>Ėš vėsa žaidėmu:</b> {{.total}}<br>
|
||||
<b>Laimiejė DI:</b> {{.aiWon}} {{.aiWonPercent}}<br>
|
||||
<b>Līgiuoms:</b> {{.draw}} {{.drawPercent}}<br>
|
||||
<b>Laimiejė DI:</b> {{.aiWon}} <sup class="text-danger">{{.aiWonPercent}}</sup><br>
|
||||
<b>Līgiuoms:</b> {{.draw}} <sup class="text-danger">{{.drawPercent}}</sup><br>
|
||||
<b>Paskutinis DI pralaimiejėms:</b> {{.lastAiLost}}<br>
|
||||
</p>
|
||||
|
|
Loading…
Reference in a new issue