Created
October 30, 2014 12:59
-
-
Save n3k0lai/90d6a9900df9ee534f86 to your computer and use it in GitHub Desktop.
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
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. | |
### |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Coffeescript implementation still broken. Using ruby currently.