Last active
March 2, 2023 17:00
-
-
Save Trey2k/da45b86e4b5c915004eb5d39b9951bb9 to your computer and use it in GitHub Desktop.
Tic Tac Toe Golang
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"strings" | |
) | |
// set a board type for get and set functions | |
type board uint16 | |
// position struct | |
type position struct { | |
turn uint8 | |
board [2]board | |
draw bool | |
} | |
// Masks used for checking if there is a winner | |
const ( | |
vertMask = 0b111 | |
horzMask = 0b100100100 | |
diagMask1 = 0b100010001 | |
diagMask2 = 0b001010100 | |
) | |
func (b board) get(index uint16) bool { | |
return (1<<index)&b != 0 | |
} | |
func (b *board) set(index uint16) { | |
// Dereferance the board and set the bit at the current index | |
*b |= 1 << index | |
} | |
func (b board) countBits() int { | |
var r int | |
for r = 0; b > 0; r++ { | |
b &= b - 1 | |
} | |
return r | |
} | |
func main() { | |
playAgain := true | |
// Reset the board after every game until the user quits | |
for playAgain { | |
pos := &position{} | |
pos.gameLoop() | |
var answer string | |
fmt.Print("Play again? (y/N): ") | |
fmt.Scanln(&answer) | |
answer = strings.ToLower(answer) | |
if playAgain = answer == "y"; playAgain { | |
fmt.Print("\n") | |
} | |
} | |
} | |
func (pos *position) gameLoop() { | |
ended := false | |
for !ended { | |
pos.print() | |
var toMove string | |
if pos.turn == 0 { | |
toMove = "X" | |
} else { | |
toMove = "O" | |
} | |
fmt.Printf("It is %s's turn\n", toMove) | |
validMove := false | |
// keep trying to get a move while the move provided is invalid | |
for !validMove { | |
fmt.Print("Your Move: ") | |
var move string | |
fmt.Scanln(&move) | |
validMove = pos.makeMove(move) | |
} | |
ended = pos.gameEnded() | |
if !ended { | |
pos.turn ^= 1 // 0 == 1 && 1 == 0 | |
} | |
} | |
if !pos.draw { | |
pos.print() | |
var winner string | |
if pos.turn == 0 { | |
winner = "X" | |
} else { | |
winner = "O" | |
} | |
fmt.Printf("%s wins! Conratz!\n", winner) | |
} else { | |
fmt.Println("The game is a draw. No one wins!") | |
} | |
} | |
func (pos *position) makeMove(move string) bool { | |
// Return false if the move is more than 2 chars | |
if len(move) != 2 { | |
return false | |
} | |
// Convert all letters to lowercase so we can calculate from a known state | |
move = strings.ToLower(move) | |
// get the value of the first char and subtract it by the ascii value of 'a' so a wil == 0 b will == 1 and so on | |
row := uint16(move[0] - 'a') | |
// do the same thing except for the number, 1 == 0, 2 == 1 and so on | |
col := uint16(move[1] - '1') | |
// Convert our 2D coords into a 1D index for the bitboard | |
index := (row * 3) + col | |
// Check that no square is already taken | |
if pos.board[0].get(index) || pos.board[1].get(index) { | |
return false | |
} | |
// Set the bit for the active player | |
pos.board[pos.turn].set(index) | |
return true | |
} | |
func (pos *position) gameEnded() bool { | |
// Check board for player who jsut moved since they are the only possible winner. | |
// Check diagnal masks first since they require no loop | |
if pos.board[pos.turn]&diagMask1 == diagMask1 || pos.board[pos.turn]&diagMask2 == diagMask2 { | |
return true | |
} | |
// Loop 3 times for each horizantal and vertical mask | |
for i := uint16(0); i < 3; i++ { | |
// Bit shift the board by i*3 to the right to allign the board with our vertical mask | |
if vertMask&(pos.board[pos.turn]>>(i*3)) == vertMask { | |
return true | |
} | |
// Bit shift the board by i to the left to allign the board with our horizantal mask | |
if horzMask&(pos.board[pos.turn]<<i) == horzMask { | |
return true | |
} | |
} | |
// check for a draw | |
if pos.board[0].countBits()+pos.board[1].countBits() >= 9 { | |
pos.draw = true | |
return true | |
} | |
return false | |
} | |
func (pos *position) print() { | |
fmt.Println(" 1 2 3\n -------------") | |
letter := 'A' | |
for i := uint16(0); i < 9; i++ { | |
if i%3 == 0 { | |
if i > 0 { | |
fmt.Println(" |") | |
} | |
fmt.Printf("%s| ", string(letter)) | |
letter++ | |
} | |
if pos.board[0].get(i) { | |
fmt.Print(" X ") | |
} else if pos.board[1].get(i) { | |
fmt.Print(" O ") | |
} else { | |
fmt.Print(" - ") | |
} | |
} | |
fmt.Println(" |\n -------------") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment