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 } }