tictactoe_bot_dynamic_size
· 5.0 KiB · Text
Eredeti
package main
import (
"fmt"
"strconv"
"strings"
)
const (
cellEmpty = " "
cellUser = "U"
cellBot = "B"
)
func boardToString(board [][]string) string {
maxRowLength := 0
rowStrings := make([]string, len(board)+1)
for i, row := range board {
if len(row) > maxRowLength {
maxRowLength = len(row)
}
rowIndex := strconv.Itoa(i)
if i < 10 {
rowIndex += " "
}
rowStrings[i+1] = rowIndex + strings.Join(row, "|")
}
columnIndices := make([]string, maxRowLength)
for i := 0; i < maxRowLength; i++ {
rowIndex := strconv.Itoa(i)
if i < 10 {
rowIndex = " " + rowIndex
}
columnIndices[i] = rowIndex
}
rowStrings[0] = "/" + strings.Join(columnIndices, "")
return strings.Join(rowStrings, "\n")
}
func createBoard(size int) [][]string {
board := make([][]string, size)
for r := 0; r < size; r++ {
rowSlice := make([]string, size)
for i := range rowSlice {
rowSlice[i] = cellEmpty
}
board[r] = rowSlice
}
return board
}
func changeCellUserInput(board *[][]string) {
var row *[]string
for {
var rowIndex int
fmt.Print("row: ")
fmt.Scanln(&rowIndex)
if 0 <= rowIndex && rowIndex < len(*board) {
row = &(*board)[rowIndex]
break
} else {
fmt.Println("The value needs to be in the range of the board rows.")
continue
}
}
for {
var colIndex int
fmt.Print("col: ")
fmt.Scanln(&colIndex)
if 0 <= colIndex && colIndex < len(*row) {
if (*row)[colIndex] != " " {
fmt.Println("This cell is already occupied.")
defer changeCellUserInput(board)
break
}
(*row)[colIndex] = cellUser
break
} else {
fmt.Println("The value needs to be in the range of the board columns.")
continue
}
}
}
func getWinningCombinations(board *[][]string) [][]*string {
// there are 8 winning combinations in total
winningCombinations := make([][]*string, len(*board)*2+2)
// board_size row wins | board_size col wins
for i, _ := range *board {
row := make([]*string, len(*board))
col := make([]*string, len(*board))
for j, _ := range *board {
row[j] = &(*board)[i][j]
col[j] = &(*board)[j][i]
}
winningCombinations[i*2] = row
winningCombinations[i*2+1] = col
}
// 2 vertical wins
a := make([]*string, len(*board))
b := make([]*string, len(*board))
for i, _ := range *board {
a[i] = &(*board)[i][i]
b[i] = &(*board)[i][len(*board)-i-1]
}
winningCombinations[len(*board)*2] = a
winningCombinations[len(*board)*2+1] = b
return winningCombinations
}
type combinationStat struct {
totalCells int
emptyCells int
userCells int
botCells int
isWinnable bool
emptyCellRefs []*string
}
func calculateCombinationStats(combination []*string) combinationStat {
c := combinationStat{}
for _, cell := range combination {
c.totalCells++
switch *cell {
case cellEmpty:
c.emptyCells++
c.emptyCellRefs = append(c.emptyCellRefs, cell)
case cellBot:
c.botCells++
case cellUser:
c.userCells++
}
}
c.isWinnable = c.userCells == 0 || c.botCells == 0
return c
}
func changeCellBotInput(winningCombinations [][]*string) {
combinationStats := make([]combinationStat, len(winningCombinations))
for i, combination := range winningCombinations {
combinationStats[i] = calculateCombinationStats(combination)
}
// now count the still possible wins each pointer has
cellScores := make(map[*string]int)
for _, c := range combinationStats {
score := 0
if c.userCells == 0 {
score = 1 + (c.totalCells - c.emptyCells)
} else if c.botCells == 0 {
score = c.totalCells - c.emptyCells
}
for _, emptyCell := range c.emptyCellRefs {
cellScores[emptyCell] += score * score
}
}
bestCellScore := 0
var bestCell *string
for cell, cellScore := range cellScores {
if cellScore > bestCellScore {
bestCellScore = cellScore
bestCell = cell
}
}
*bestCell = cellBot
}
type boardData struct {
isWin bool
isPat bool
winningUser string
}
func analyzeWin(winningCombinations [][]*string) boardData {
b := boardData{}
winnable := false
for _, combination := range winningCombinations {
c := calculateCombinationStats(combination)
if c.botCells == c.totalCells {
b.isWin = true
b.winningUser = cellBot
return b
}
if c.userCells == c.totalCells {
b.isWin = true
b.winningUser = cellUser
return b
}
if c.botCells == 0 || c.userCells == 0 {
winnable = true
}
}
if !winnable {
b.isPat = true
}
return b
}
func main() {
board := createBoard(4)
winningCombinations := getWinningCombinations(&board)
currentUser := 0
for {
fmt.Println()
fmt.Println(boardToString(board))
bd := analyzeWin(winningCombinations)
if bd.isWin {
fmt.Println()
switch bd.winningUser {
case cellBot:
fmt.Println("THE BOT WINS!!")
case cellUser:
fmt.Println("CONGRATULATION, YOU WIN!!")
}
break
}
if bd.isPat {
fmt.Println()
fmt.Println("GAME OVER. NEITHER ONE WON")
break
}
if currentUser == 0 {
fmt.Println()
fmt.Println("it's your turn human")
changeCellUserInput(&board)
} else {
changeCellBotInput(winningCombinations)
}
currentUser = (currentUser + 1) % 2
}
}
1 | package main |
2 | |
3 | import ( |
4 | "fmt" |
5 | "strconv" |
6 | "strings" |
7 | ) |
8 | |
9 | const ( |
10 | cellEmpty = " " |
11 | cellUser = "U" |
12 | cellBot = "B" |
13 | ) |
14 | |
15 | func boardToString(board [][]string) string { |
16 | maxRowLength := 0 |
17 | rowStrings := make([]string, len(board)+1) |
18 | for i, row := range board { |
19 | if len(row) > maxRowLength { |
20 | maxRowLength = len(row) |
21 | } |
22 | |
23 | rowIndex := strconv.Itoa(i) |
24 | if i < 10 { |
25 | rowIndex += " " |
26 | } |
27 | rowStrings[i+1] = rowIndex + strings.Join(row, "|") |
28 | } |
29 | |
30 | columnIndices := make([]string, maxRowLength) |
31 | for i := 0; i < maxRowLength; i++ { |
32 | rowIndex := strconv.Itoa(i) |
33 | if i < 10 { |
34 | rowIndex = " " + rowIndex |
35 | } |
36 | columnIndices[i] = rowIndex |
37 | } |
38 | rowStrings[0] = "/" + strings.Join(columnIndices, "") |
39 | |
40 | return strings.Join(rowStrings, "\n") |
41 | } |
42 | |
43 | func createBoard(size int) [][]string { |
44 | board := make([][]string, size) |
45 | |
46 | for r := 0; r < size; r++ { |
47 | rowSlice := make([]string, size) |
48 | for i := range rowSlice { |
49 | rowSlice[i] = cellEmpty |
50 | } |
51 | |
52 | board[r] = rowSlice |
53 | } |
54 | |
55 | return board |
56 | } |
57 | |
58 | func changeCellUserInput(board *[][]string) { |
59 | var row *[]string |
60 | |
61 | for { |
62 | var rowIndex int |
63 | fmt.Print("row: ") |
64 | fmt.Scanln(&rowIndex) |
65 | |
66 | if 0 <= rowIndex && rowIndex < len(*board) { |
67 | row = &(*board)[rowIndex] |
68 | break |
69 | } else { |
70 | fmt.Println("The value needs to be in the range of the board rows.") |
71 | continue |
72 | } |
73 | } |
74 | |
75 | for { |
76 | var colIndex int |
77 | fmt.Print("col: ") |
78 | fmt.Scanln(&colIndex) |
79 | |
80 | if 0 <= colIndex && colIndex < len(*row) { |
81 | if (*row)[colIndex] != " " { |
82 | fmt.Println("This cell is already occupied.") |
83 | defer changeCellUserInput(board) |
84 | break |
85 | } |
86 | |
87 | (*row)[colIndex] = cellUser |
88 | break |
89 | } else { |
90 | fmt.Println("The value needs to be in the range of the board columns.") |
91 | continue |
92 | } |
93 | } |
94 | } |
95 | |
96 | func getWinningCombinations(board *[][]string) [][]*string { |
97 | // there are 8 winning combinations in total |
98 | winningCombinations := make([][]*string, len(*board)*2+2) |
99 | |
100 | // board_size row wins | board_size col wins |
101 | for i, _ := range *board { |
102 | row := make([]*string, len(*board)) |
103 | col := make([]*string, len(*board)) |
104 | |
105 | for j, _ := range *board { |
106 | row[j] = &(*board)[i][j] |
107 | col[j] = &(*board)[j][i] |
108 | } |
109 | |
110 | winningCombinations[i*2] = row |
111 | winningCombinations[i*2+1] = col |
112 | } |
113 | |
114 | // 2 vertical wins |
115 | a := make([]*string, len(*board)) |
116 | b := make([]*string, len(*board)) |
117 | for i, _ := range *board { |
118 | a[i] = &(*board)[i][i] |
119 | b[i] = &(*board)[i][len(*board)-i-1] |
120 | } |
121 | winningCombinations[len(*board)*2] = a |
122 | winningCombinations[len(*board)*2+1] = b |
123 | |
124 | return winningCombinations |
125 | } |
126 | |
127 | type combinationStat struct { |
128 | totalCells int |
129 | emptyCells int |
130 | userCells int |
131 | botCells int |
132 | |
133 | isWinnable bool |
134 | emptyCellRefs []*string |
135 | } |
136 | |
137 | func calculateCombinationStats(combination []*string) combinationStat { |
138 | c := combinationStat{} |
139 | |
140 | for _, cell := range combination { |
141 | c.totalCells++ |
142 | switch *cell { |
143 | case cellEmpty: |
144 | c.emptyCells++ |
145 | c.emptyCellRefs = append(c.emptyCellRefs, cell) |
146 | case cellBot: |
147 | c.botCells++ |
148 | case cellUser: |
149 | c.userCells++ |
150 | } |
151 | } |
152 | |
153 | c.isWinnable = c.userCells == 0 || c.botCells == 0 |
154 | |
155 | return c |
156 | } |
157 | |
158 | func changeCellBotInput(winningCombinations [][]*string) { |
159 | combinationStats := make([]combinationStat, len(winningCombinations)) |
160 | for i, combination := range winningCombinations { |
161 | combinationStats[i] = calculateCombinationStats(combination) |
162 | } |
163 | |
164 | // now count the still possible wins each pointer has |
165 | cellScores := make(map[*string]int) |
166 | for _, c := range combinationStats { |
167 | score := 0 |
168 | if c.userCells == 0 { |
169 | score = 1 + (c.totalCells - c.emptyCells) |
170 | } else if c.botCells == 0 { |
171 | score = c.totalCells - c.emptyCells |
172 | } |
173 | |
174 | for _, emptyCell := range c.emptyCellRefs { |
175 | cellScores[emptyCell] += score * score |
176 | } |
177 | } |
178 | |
179 | bestCellScore := 0 |
180 | var bestCell *string |
181 | for cell, cellScore := range cellScores { |
182 | if cellScore > bestCellScore { |
183 | bestCellScore = cellScore |
184 | bestCell = cell |
185 | } |
186 | } |
187 | |
188 | *bestCell = cellBot |
189 | } |
190 | |
191 | type boardData struct { |
192 | isWin bool |
193 | isPat bool |
194 | |
195 | winningUser string |
196 | } |
197 | |
198 | func analyzeWin(winningCombinations [][]*string) boardData { |
199 | b := boardData{} |
200 | |
201 | winnable := false |
202 | for _, combination := range winningCombinations { |
203 | c := calculateCombinationStats(combination) |
204 | |
205 | if c.botCells == c.totalCells { |
206 | b.isWin = true |
207 | b.winningUser = cellBot |
208 | return b |
209 | } |
210 | if c.userCells == c.totalCells { |
211 | b.isWin = true |
212 | b.winningUser = cellUser |
213 | return b |
214 | } |
215 | |
216 | if c.botCells == 0 || c.userCells == 0 { |
217 | winnable = true |
218 | } |
219 | } |
220 | |
221 | if !winnable { |
222 | b.isPat = true |
223 | } |
224 | |
225 | return b |
226 | } |
227 | |
228 | func main() { |
229 | board := createBoard(4) |
230 | winningCombinations := getWinningCombinations(&board) |
231 | |
232 | currentUser := 0 |
233 | for { |
234 | fmt.Println() |
235 | fmt.Println(boardToString(board)) |
236 | |
237 | bd := analyzeWin(winningCombinations) |
238 | if bd.isWin { |
239 | fmt.Println() |
240 | switch bd.winningUser { |
241 | case cellBot: |
242 | fmt.Println("THE BOT WINS!!") |
243 | case cellUser: |
244 | fmt.Println("CONGRATULATION, YOU WIN!!") |
245 | } |
246 | break |
247 | } |
248 | if bd.isPat { |
249 | fmt.Println() |
250 | fmt.Println("GAME OVER. NEITHER ONE WON") |
251 | break |
252 | } |
253 | |
254 | if currentUser == 0 { |
255 | fmt.Println() |
256 | fmt.Println("it's your turn human") |
257 | changeCellUserInput(&board) |
258 | } else { |
259 | changeCellBotInput(winningCombinations) |
260 | } |
261 | |
262 | currentUser = (currentUser + 1) % 2 |
263 | } |
264 | } |
265 |