Skip to content

Instantly share code, notes, and snippets.

@n3k0lai
Created October 30, 2014 12:59
Show Gist options
  • Save n3k0lai/90d6a9900df9ee534f86 to your computer and use it in GitHub Desktop.
Save n3k0lai/90d6a9900df9ee534f86 to your computer and use it in GitHub Desktop.
class Board
constructor: (temp) ->
@board = temp.board ? [0,0,0,0,0,0,0,0,0]
@turnNumber = temp.turn ? 0
@over = temp.over ? false
@winner = temp.winner ? "In progress"
win: (playerGuess) ->
result = false
if @board[0] is @board[1] and @board[0] is @board[2]
if @board[0] is playerGuess then result = true else result = false
else if @board[0] is @board[4] and @board[0] is @board[8]
if @board[0] is playerGuess then result = true else result = false
else if @board[0] is @board[3] and @board[0] is @board[6]
if @board[0] is playerGuess then result = true else result = false
else if @board[0] is @board[1] and @board[0] is @board[2]
if @board[0] is playerGuess then result = true else result = false
else if @board[1] is @board[4] and @board[1] is @board[7]
if @board[1] is playerGuess then result = true else result = false
else if @board[2] is @board[5] and @board[2] is @board[8]
if @board[2] is playerGuess then result = true else result = false
else if @board[2] is @board[4] and @board[2] is @board[6]
if @board[2] is playerGuess then result = true else result = false
else if @board[3] is @board[4] and @board[3] is @board[5]
if @board[3] is playerGuess then result = true else result = false
else if @board[6] is @board[7] and @board[6] is @board[8]
if @board[6] is playerGuess then result = true else result = false
else result = false
result
over: -> @over
winner: -> @winner
board: -> @board
turn: -> @turn
getMoves: -> (index for move, index in @board when move is 0)
validateMove: (move) -> if @board[move] is 0 then true else false
templateBoard: (move) ->
result = new Board(@board)
result.makeMove(move)
result
makeMove: (move) ->
if validateMove(move) and @turnNumber % 2 is 0 then @board[move] = "X" else @board[move] = "O"
@turnNumber++
if this.win("X")
@over = true
@winner = "X"
else if this.win("O")
@over = true
@winner = "O"
else if @turnNumber is 8
@over = true
@winner = "draw"
class Game
constructor: (@player) ->
@game = new Board()
@turn = "X"
@choice = -1 # what aI chooses move as
if @player is "X" then @opponent = "O" else @opponent = "X"
upTurn: () ->
if @turn is "X" then @turn = "O" else @turn = "X"
score: (game, depth) ->
if game.win(@player) then return 10 - depth else if game.win(@opponent) then return depth - 10 else return 0
minimax: (game, depth) ->
if @game.over then return score(@game)
if depth? then depth += 1 else depth = 0
scores = [] # an array of scores
moves = [] # an array of moves
# Populate the scores array, recursing as needed
for move in @game.getMoves
possible_game = @game.templateBoard(move)
scores.push(minimax(possible_game, depth))
moves.push(move)
# Do the min or the max calculation
if @turn is @player
# This is the max calculation
max = 0
index = 0
for move, i in scores if move > max
max = move
index = i
@choice = moves[index]
scores[index]
else
# This is the min calculation
min = 999999999999
index = 0
for score, i in scores if score < min
min = score
index = i
@choice = moves[index]
scores[index]
###
Win: If the player has two in a row, they can place a third to get three in a row.
Block: If the opponent has two in a row, the player must play the third themselves to block the opponent.
Fork: Create an opportunity where the player has two threats to win (two non-blocked lines of 2).
Blocking an opponent's fork:
Option 1: The player should create two in a row to force the opponent into defending, as long as it doesn't result in them creating a fork. For example, if "X" has a corner, "O" has the center, and "X" has the opposite corner as well, "O" must not play a corner in order to win. (Playing a corner in this scenario creates a fork for "X" to win.)
Option 2: If there is a configuration where the opponent can fork, the player should block that fork.
Center: A player marks the center. (If it is the first move of the game, playing on a corner gives "O" more opportunities to make a mistake and may therefore be the better choice; however, it makes no difference between perfect players.)
Opposite corner: If the opponent is in the corner, the player plays the opposite corner.
Empty corner: The player plays in a corner square.
Empty side: The player plays in a middle square on any of the 4 sides.
###
@n3k0lai
Copy link
Author

n3k0lai commented Oct 30, 2014

Coffeescript implementation still broken. Using ruby currently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment