tic-tac-toe/db.go

144 lines
3.3 KiB
Go

package main
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
var migrations = []string{
"ALTER TABLE Games ADD count INT DEFAULT 0;",
}
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);
INSERT OR IGNORE INTO Stats (name, count) VALUES ('last_migration', 0);
`
_, err := db.Exec(sqlStmt)
if err != nil {
return err
}
err = runMigrations(db)
if err != nil {
return err
}
return nil
}
func runMigrations(db *sql.DB) error {
lastMigration, err := getStatsCounter(db, "last_migration")
if err != nil {
return err
}
for i := lastMigration; i < len(migrations); i++ {
sqlStmt := migrations[i]
_, err := db.Exec(sqlStmt)
if err != nil {
return err
}
db.Exec("UPDATE Stats SET count=count+1 WHERE name='last_migration';")
}
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(),
)
db.Exec(
"UPDATE Games SET count=count+1 WHERE game_flow=? AND first_won=?;",
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 = ? ORDER BY count DESC",
)
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]
}