Compare commits
No commits in common. "d931d9027c22b5782082e8512dd6d44567667e0c" and "06849170e46ea3afcc63d69d6cad8b7e44aeb0e1" have entirely different histories.
d931d9027c
...
06849170e4
7 changed files with 48 additions and 227 deletions
BIN
assets/cross.png
BIN
assets/cross.png
Binary file not shown.
Before Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.9 KiB |
174
game.go
174
game.go
|
@ -1,183 +1,23 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GameStateInProgress int = 0
|
GameStateInProgress int = 0
|
||||||
GameStateWaitingUser = 1
|
GameStateEnded = 1
|
||||||
GameStateEnded = 2
|
GameStateDraw = 2
|
||||||
GameStateDraw = 3
|
GameStateAIWon = 3
|
||||||
GameStateAIWon = 4
|
GameStateAILost = 4
|
||||||
GameStateAILost = 5
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
ImageCross string = "cross"
|
|
||||||
ImageNought = "nought"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
WinnerNone int = 0
|
|
||||||
WinnerAI = 1
|
|
||||||
WinnerUser = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
var field2point = map[int][2]int{
|
|
||||||
1: {0, 0},
|
|
||||||
2: {0, 1},
|
|
||||||
3: {0, 2},
|
|
||||||
4: {1, 0},
|
|
||||||
5: {1, 1},
|
|
||||||
6: {1, 2},
|
|
||||||
7: {2, 0},
|
|
||||||
8: {2, 1},
|
|
||||||
9: {2, 2},
|
|
||||||
}
|
|
||||||
|
|
||||||
var FieldClass = map[int]string{
|
|
||||||
1: "tt-item br bb",
|
|
||||||
2: "tt-item bb",
|
|
||||||
3: "tt-item bl bb",
|
|
||||||
4: "tt-item br",
|
|
||||||
5: "tt-item",
|
|
||||||
6: "tt-item bl",
|
|
||||||
7: "tt-item bt br",
|
|
||||||
8: "tt-item bt",
|
|
||||||
9: "tt-item bl bt",
|
|
||||||
}
|
|
||||||
|
|
||||||
var winner = [][3]int{
|
|
||||||
{1, 2, 3},
|
|
||||||
{4, 5, 6},
|
|
||||||
{7, 8, 9},
|
|
||||||
{1, 4, 7},
|
|
||||||
{2, 5, 8},
|
|
||||||
{3, 6, 9},
|
|
||||||
{1, 5, 9},
|
|
||||||
{7, 5, 3},
|
|
||||||
}
|
|
||||||
|
|
||||||
type Field struct {
|
|
||||||
disabled bool
|
|
||||||
image string
|
|
||||||
line bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
gameFlow string
|
gameFlow string
|
||||||
aiSecond bool
|
aiSecond bool
|
||||||
aiFields []int
|
|
||||||
userFields []int
|
|
||||||
fields map[int]Field
|
|
||||||
state int
|
|
||||||
winnerFields [3]int
|
|
||||||
winner int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGame(gameFlow string, aiSecond bool) *Game {
|
func newGame(gameFlow string, aiSecond bool) *Game {
|
||||||
fields := make(map[int]Field)
|
|
||||||
for i := 1; i < 10; i++ {
|
|
||||||
field := &Field{
|
|
||||||
disabled: false,
|
|
||||||
}
|
|
||||||
fields[i] = *field
|
|
||||||
}
|
|
||||||
|
|
||||||
game := &Game{
|
game := &Game{
|
||||||
gameFlow: gameFlow,
|
gameFlow: gameFlow,
|
||||||
aiSecond: aiSecond,
|
aiSecond: aiSecond,
|
||||||
fields: fields,
|
|
||||||
winner: WinnerNone,
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, f := range str2array(game.gameFlow) {
|
|
||||||
fieldNum, _ := strconv.ParseInt(f, 0, 16)
|
|
||||||
|
|
||||||
if field, ok := game.fields[int(fieldNum)]; ok {
|
|
||||||
field.disabled = true
|
|
||||||
|
|
||||||
if i%2 == 0 {
|
|
||||||
field.image = ImageCross
|
|
||||||
} else {
|
|
||||||
field.image = ImageNought
|
|
||||||
}
|
|
||||||
|
|
||||||
game.fields[int(fieldNum)] = field
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i%2 == 0) == game.aiSecond {
|
|
||||||
game.userFields = append(game.userFields, int(fieldNum))
|
|
||||||
} else {
|
|
||||||
game.aiFields = append(game.aiFields, int(fieldNum))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
game.calculateState()
|
|
||||||
|
|
||||||
if game.isFinished() && game.winner != WinnerNone {
|
|
||||||
for _, f := range game.winnerFields {
|
|
||||||
if field, ok := game.fields[f]; ok {
|
|
||||||
field.line = true
|
|
||||||
game.fields[f] = field
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 1; i < 10; i++ {
|
|
||||||
if field, ok := game.fields[i]; ok {
|
|
||||||
field.disabled = true
|
|
||||||
game.fields[i] = field
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return game
|
return game
|
||||||
}
|
}
|
||||||
|
|
||||||
func (game *Game) isFinished() bool {
|
|
||||||
return game.state > GameStateWaitingUser
|
|
||||||
}
|
|
||||||
|
|
||||||
func (game *Game) calculateState() {
|
|
||||||
game.state = GameStateInProgress
|
|
||||||
|
|
||||||
if "0" == game.gameFlow {
|
|
||||||
game.state = GameStateWaitingUser
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, w := range winner {
|
|
||||||
if len(game.aiFields) >= 3 && isSubArray(w[:], game.aiFields) {
|
|
||||||
game.state = GameStateAIWon
|
|
||||||
game.winnerFields = w
|
|
||||||
game.winner = WinnerAI
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(game.userFields) >= 3 && isSubArray(w[:], game.userFields) {
|
|
||||||
game.state = GameStateAILost
|
|
||||||
game.winnerFields = w
|
|
||||||
game.winner = WinnerUser
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(game.gameFlow) == 9 {
|
|
||||||
game.state = GameStateDraw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isSubArray(a, b []int) bool {
|
|
||||||
mb := make(map[int]struct{}, len(b))
|
|
||||||
for _, x := range b {
|
|
||||||
mb[x] = struct{}{}
|
|
||||||
}
|
|
||||||
var diff []int
|
|
||||||
for _, x := range a {
|
|
||||||
if _, found := mb[x]; !found {
|
|
||||||
diff = append(diff, x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(diff) == 0
|
|
||||||
}
|
|
||||||
|
|
68
main.go
68
main.go
|
@ -22,17 +22,34 @@ func main() {
|
||||||
r.StaticFile("/favicon.ico", "./assets/favicon.ico")
|
r.StaticFile("/favicon.ico", "./assets/favicon.ico")
|
||||||
r.StaticFile("/sgs.png", "./assets/sgs.png")
|
r.StaticFile("/sgs.png", "./assets/sgs.png")
|
||||||
r.StaticFile("/en.png", "./assets/en.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) {
|
r.GET("/", func(c *gin.Context) {
|
||||||
lang := getLang(c)
|
lang := getLang(c)
|
||||||
c.Redirect(http.StatusMovedPermanently, fmt.Sprintf("/%s/0/x", lang))
|
fmt.Println(lang)
|
||||||
|
c.Redirect(http.StatusMovedPermanently, fmt.Sprintf("/%s/0/%s", lang, initGame(false)))
|
||||||
})
|
})
|
||||||
|
|
||||||
r.GET("/:lang", func(c *gin.Context) {
|
r.GET("/:lang", func(c *gin.Context) {
|
||||||
lang := getLang(c)
|
lang := getLang(c)
|
||||||
c.Redirect(http.StatusMovedPermanently, fmt.Sprintf("/%s/0/x", lang))
|
c.Redirect(http.StatusMovedPermanently, fmt.Sprintf("/%s/0/%s", lang, initGame(false)))
|
||||||
|
})
|
||||||
|
|
||||||
|
r.GET("/:lang/:ai_second", func(c *gin.Context) {
|
||||||
|
lang := getLang(c)
|
||||||
|
aiSecond, err := strconv.ParseBool(c.Param("ai_second"))
|
||||||
|
if err != nil {
|
||||||
|
aiSecond = false
|
||||||
|
}
|
||||||
|
|
||||||
|
aiSecondVar := "0"
|
||||||
|
if aiSecond {
|
||||||
|
aiSecondVar = "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Redirect(
|
||||||
|
http.StatusMovedPermanently,
|
||||||
|
fmt.Sprintf("/%s/%s/%s", lang, aiSecondVar, initGame(aiSecond)),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
r.GET("/:lang/:ai_second/:game", func(c *gin.Context) {
|
r.GET("/:lang/:ai_second/:game", func(c *gin.Context) {
|
||||||
|
@ -46,45 +63,28 @@ func main() {
|
||||||
|
|
||||||
game := newGame(gameFlow, aiSecond)
|
game := newGame(gameFlow, aiSecond)
|
||||||
|
|
||||||
if gameState == GameStateInProgress && !game.isFinished() {
|
gameFlow = gameFlow + getNewClick(gameFlow)
|
||||||
gameFlow = gameFlow + getNewClick(gameFlow)
|
|
||||||
game = newGame(gameFlow, aiSecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if game ended - save results
|
|
||||||
|
|
||||||
c.HTML(http.StatusOK, "index", gin.H{
|
c.HTML(http.StatusOK, "index", gin.H{
|
||||||
"title": "Index title!",
|
"title": "Index title!",
|
||||||
"lang": lang,
|
"lang": lang,
|
||||||
"gameFlow": gameFlow,
|
"gameFlow": gameFlow,
|
||||||
"game": game,
|
"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
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Run()
|
r.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initGame(aiSecond bool) string {
|
||||||
|
if aiSecond {
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// get from DB
|
||||||
|
return "1"
|
||||||
|
}
|
||||||
|
|
||||||
func getLang(c *gin.Context) string {
|
func getLang(c *gin.Context) string {
|
||||||
session := sessions.Default(c)
|
session := sessions.Default(c)
|
||||||
|
|
||||||
|
@ -108,11 +108,7 @@ func getLang(c *gin.Context) string {
|
||||||
func getGameFlow(c *gin.Context) (string, int) {
|
func getGameFlow(c *gin.Context) (string, int) {
|
||||||
gameFlow := c.Param("game")
|
gameFlow := c.Param("game")
|
||||||
if "0" == gameFlow {
|
if "0" == gameFlow {
|
||||||
return gameFlow, GameStateWaitingUser
|
return gameFlow, GameStateInProgress
|
||||||
}
|
|
||||||
|
|
||||||
if "x" == gameFlow {
|
|
||||||
return "", GameStateInProgress
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if "0" == string(gameFlow[0]) {
|
if "0" == string(gameFlow[0]) {
|
||||||
|
|
|
@ -22,34 +22,21 @@
|
||||||
.bl { border-left: 5px solid #333; }
|
.bl { border-left: 5px solid #333; }
|
||||||
.br { border-right: 5px solid #333; }
|
.br { border-right: 5px solid #333; }
|
||||||
|
|
||||||
.cross { background-image: url('/cross.png'); }
|
|
||||||
.nought { background-image: url('/nought.png'); }
|
|
||||||
|
|
||||||
.line.cross {
|
|
||||||
background-color: #f88;
|
|
||||||
border: 2px solid #f33;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line.nought {
|
|
||||||
background-color: #aaf;
|
|
||||||
border: 2px solid #33f;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
{{ $Game := .game }}
|
<a class="tt-item br bb" href="{{ .gameFlow -}}1"> </a>
|
||||||
{{ $GameFlow := .gameFlow }}
|
<a class="tt-item bb" href="{{ .gameFlow -}}2"> </a>
|
||||||
{{ range .fields }}
|
<a class="tt-item bl bb" href="{{ .gameFlow -}}3"> </a>
|
||||||
{{ if call $.disabled $Game . }}
|
<a class="tt-item br" href="{{ .gameFlow -}}4"> </a>
|
||||||
<span class="{{ call $.fieldClass $Game . }}"></span>
|
<a class="tt-item" href="{{ .gameFlow -}}5"> </a>
|
||||||
{{ else }}
|
<a class="tt-item bl" href="{{ .gameFlow -}}6"> </a>
|
||||||
<a class="{{ call $.fieldClass $Game . }}" href="{{ $GameFlow -}}{{- . }}"></a>
|
<a class="tt-item bt br" href="{{ .gameFlow -}}7"> </a>
|
||||||
{{ end }}
|
<a class="tt-item bt" href="{{ .gameFlow -}}8"> </a>
|
||||||
{{ end }}
|
<a class="tt-item bl bt" href="{{ .gameFlow -}}9"> </a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<b>Game:</b> {{.gameFlow}}
|
<b>Game:</b> {{.gameFlow}}
|
||||||
|
|
|
@ -10,6 +10,5 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a class="btn btn-secondary" href="/{{.lang}}/1">Me first!</a>
|
<a class="btn btn-secondary" href="/{{.lang}}/1">Me first!</a>
|
||||||
<a class="btn btn-secondary" href="/{{.lang}}/0/x">AI first</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -10,5 +10,4 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a class="btn btn-secondary" href="/{{.lang}}/1">Aš pėrms!</a>
|
<a class="btn btn-secondary" href="/{{.lang}}/1">Aš pėrms!</a>
|
||||||
<a class="btn btn-secondary" href="/{{.lang}}/0/x">DI pėrms</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Reference in a new issue