Skip to content

Instantly share code, notes, and snippets.

@arthurmco
Last active September 18, 2017 01:47
Show Gist options
  • Save arthurmco/a1b854bece22a7a21dae7cc040734292 to your computer and use it in GitHub Desktop.
Save arthurmco/a1b854bece22a7a21dae7cc040734292 to your computer and use it in GitHub Desktop.
tic tac toe game in go
package main
import (
"fmt"
)
type Board [3][3]rune
type Player struct {
name string
choosenchar rune
score uint
}
func main() {
var b Board
board_clear(&b)
fmt.Println("TicTacToe game")
fmt.Print("Your name: ")
var name string
fmt.Scanln(&name)
human := Player{name: name, choosenchar: 'O'}
ai := Player{name: "AI", choosenchar: 'X'}
for {
winner := board_get_winner(&b, &human, &ai)
if winner != nil {
fmt.Printf("Game Over. %s won!\n", winner.name)
break
}
if board_is_full(&b) {
fmt.Println("Game Over. Tie")
break
}
fmt.Printf("%s turn\n", human.name)
human_process(&b, &human)
board_show(&b)
fmt.Println("Computer turn")
ai_process(&b, &ai)
board_show(&b)
}
}
func board_show(b *Board) {
for y := 0; y < 3; y++ {
for x := 0; x < 3; x++ {
fmt.Print(string(b[y][x]))
if x < 2 {
fmt.Print(" | ")
} else {
fmt.Println("")
}
}
}
}
/* Gets two players. Returns the winner */
func board_get_winner(b *Board, p1, p2 *Player) *Player {
p1_diagonal, p2_diagonal := true, true
p1_diagonal_rev, p2_diagonal_rev := true, true
for y := 0; y < 3; y++ {
/* Check for horizontal wins */
if b[y][0] == p1.choosenchar && b[y][1] == p1.choosenchar && b[y][2] == p1.choosenchar {
return p1
}
if b[y][0] == p2.choosenchar && b[y][1] == p2.choosenchar && b[y][2] == p2.choosenchar {
return p2
}
for x := 0; x < 3; x++ {
/* Check for vertical wins */
if b[0][x] == p1.choosenchar && b[1][x] == p1.choosenchar && b[2][x] == p1.choosenchar {
return p1
}
if b[0][x] == p2.choosenchar && b[1][x] == p2.choosenchar && b[2][x] == p2.choosenchar {
return p2
}
/* Check for diagonal wins... differently */
if x == y {
p1_diagonal = p1_diagonal && (b[y][x] == p1.choosenchar)
p2_diagonal = p2_diagonal && (b[y][x] == p2.choosenchar)
p1_diagonal_rev = p1_diagonal_rev && (b[y][2-x] == p1.choosenchar)
p2_diagonal_rev = p2_diagonal_rev && (b[y][2-x] == p2.choosenchar)
}
}
}
if p1_diagonal || p1_diagonal_rev {
return p1
}
if p2_diagonal || p2_diagonal_rev {
return p2
}
return nil
}
func board_clear(b *Board) {
for y := 0; y < 3; y++ {
for x := 0; x < 3; x++ {
b[y][x] = ' '
}
}
}
func board_is_full(b *Board) bool {
for y := 0; y < 3; y++ {
for x := 0; x < 3; x++ {
if b[y][x] == ' ' {
return false
}
}
}
return true
}
func human_process(b *Board, human *Player) {
var x, y uint
for {
fmt.Print("Type wished position (y x): ")
num, err := fmt.Scanf("%v %v", &y, &x)
if num < 2 {
fmt.Println("Error: Invalid format: ", err.Error())
continue
}
if x >= 3 {
num = 0
fmt.Println("xpos too high! Remember that positions start at zero")
continue
}
if y >= 3 {
num = 0
fmt.Println("ypos too high! Remember that positions start at zero")
continue
}
if b[y][x] != ' ' {
num = 0
fmt.Println("Place already occupied")
continue
}
break
}
b[y][x] = human.choosenchar
}
type scoreDest struct {
score, desty, destx int
}
func ai_process(b *Board, ai *Player) {
scores := make(map[string]scoreDest)
/* Process the 'likeliness' of different possible positions
Shows its likeliness, ie, the most likely ones are the ones that have the
most number of 'human' choosenchars, because the human might trying to draw
a line
*/
for y := 0; y < 3; y++ {
if b[y][0] != ' ' && b[y][2] != ' ' && b[y][1] == ' ' {
fmt.Println(">> Path x1")
b[y][1] = ai.choosenchar
score := 0
for _, v := range b[y] {
if v != ai.choosenchar && v != ' ' {
score += 5
} else if v == ai.choosenchar {
score += 2
} else {
score += 1
}
}
scores["x1" + string(y)] = scoreDest{score, y, 1}
}
if b[y][0] != ' ' && b[y][1] != ' ' && b[y][2] == ' ' {
fmt.Println(">> Path x2")
score := 0
for _, v := range b[y] {
if v != ai.choosenchar && v != ' ' {
score += 5
} else if v == ai.choosenchar {
score += 2
} else {
score += 1
}
}
scores["x2" + string(y)] = scoreDest{score, y, 2}
}
for x := 0; x < 3; x++ {
if b[0][x] != ' ' && b[1][x] != ' ' && b[2][x] == ' ' {
fmt.Println(">> Path y1")
score := 0
for _, v := range [3]rune{b[0][x], b[1][x], b[2][x]} {
if v != ai.choosenchar && v != ' ' {
score += 5
} else if v == ai.choosenchar {
score += 2
} else {
score += 1
}
}
scores["y1" + string(x)] = scoreDest{score, 2, x}
}
if b[0][x] != ' ' && b[1][x] == ' ' {
fmt.Println(">> Path y2")
score := 0
for _, v := range [3]rune{b[0][x], b[1][x], b[2][x]} {
if v != ai.choosenchar && v != ' ' {
score += 5
} else if v == ai.choosenchar {
score += 2
} else {
score += 1
}
}
scores["y2" + string(x)] = scoreDest{score, 1, x}
}
if b[y][x] == ' ' {
fmt.Println(">> Path generic")
scores["g" + string(y) + string(x)] = scoreDest{1, y, x}
}
}
}
/* Iterate over every score, get the bigger and return them */
var maxscore scoreDest = scoreDest{0, 0, 0}
for _, sitem := range scores {
if sitem.score > maxscore.score {
maxscore = sitem
}
}
if b[maxscore.desty][maxscore.destx] == ' ' {
b[maxscore.desty][maxscore.destx] = ai.choosenchar
} else {
fmt.Println("No more places!")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment