Created
July 1, 2013 05:05
-
-
Save evantahler/5898472 to your computer and use it in GitHub Desktop.
Creating the API for a tic-tac-toe game with AI
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
// actions/game.js | |
// A collection of tasks and helper methods to create a simpel tic-tac-toe game | |
// - single player | |
// - simple AI | |
// - each connection can only have one active game at a time | |
exports.gameCreate = { | |
name: "gameCreate", | |
description: "I create a new game for this connection", | |
inputs: { | |
required: [], | |
optional: [], | |
}, | |
outputExample: {}, | |
version: 1.0, | |
run: function(api, connection, next){ | |
var game = new api.game.gamePrototype(); | |
api.game.saveGame(connection, game, function(error){ | |
connection.error = error; | |
next(connection, true); | |
}); | |
} | |
}; | |
exports.gameView = { | |
name: "gameView", | |
description: "I view the game board", | |
inputs: { | |
required: [], | |
optional: [], | |
}, | |
outputExample: {}, | |
version: 1.0, | |
run: function(api, connection, next){ | |
api.game.loadGame(connection, function(error, game){ | |
connection.error = error; | |
connection.response.game = game; | |
next(connection, true); | |
}); | |
} | |
}; | |
exports.gameMove = { | |
name: "gameMove", | |
description: "a move by a human player of tic-tac-toe", | |
inputs: { | |
required: ['x', 'y'], | |
optional: [], | |
}, | |
outputExample: {}, | |
version: 1.0, | |
run: function(api, connection, next){ | |
var x = parseInt(connection.params.x); | |
var y = parseInt(connection.params.y); | |
api.game.loadGame(connection, function(error, game){ | |
if(error != null){ | |
connection.error = error; | |
next(connection, true); | |
}else if(game.state != "playing"){ | |
connection.error = "this game is over"; | |
next(connection, true); | |
}else if(game.board[y][x] != null){ | |
connection.error = "you can only draw a new shape on a blank tile"; | |
next(connection, true); | |
}else{ | |
game.board[y][x] = game.playerMarker; | |
game.state = api.game.determineGameState(game); | |
api.game.aiTurn(game); | |
game.state = api.game.determineGameState(game); | |
game.turn++; | |
api.game.saveGame(connection, game, function(){ | |
connection.response.game = game; | |
next(connection, true); | |
}); | |
} | |
}); | |
} | |
}; |
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
// initializers/game.js | |
exports.game = function(api, next){ | |
api.game = { | |
gamePrototype: function(data){ | |
if(data == null){ | |
this.board = [ | |
[null, null, null], | |
[null, null, null], | |
[null, null, null], | |
]; | |
this.turn = 0; | |
this.state = "playing"; | |
this.playerMarker = "X"; | |
this.computerMarker = "O"; | |
}else{ | |
for(var i in data){ | |
this[i] = data[i]; | |
} | |
} | |
}, | |
loadGame: function(connection, callback){ | |
var key = "game_" + connection.id; | |
api.cache.load(key, function(error, data){ | |
callback(error, new api.game.gamePrototype(data)); | |
}); | |
}, | |
saveGame: function(connection, game, callback){ | |
var key = "game_" + connection.id; | |
var data = { | |
board: game.board, | |
turn: game.turn, | |
state: game.state, | |
playerMarker: game.playerMarker, | |
computerMarker: game.computerMarker, | |
} | |
api.cache.save(key, data, function(error){ | |
callback(error); | |
}); | |
}, | |
determineGameState: function(game){ | |
// horizontal win | |
for(var i in game.board){ | |
var row = game.board[i] | |
if(row[0] != null && row[0] == row[1] && row[0] == row[2]){ | |
return api.game.determineWinningState(game, row[0]); | |
} | |
}; | |
// vertical win | |
var i = 0; | |
while (i <= 2){ | |
if(game.board[0][i] != null && game.board[0][i] == game.board[1][i] && game.board[0][i] == game.board[2][i]){ | |
return api.game.determineWinningState(game, game.board[0][i]); | |
} | |
i++; | |
} | |
// diagonal win | |
if( | |
(game.board[1][1] != null && game.board[0][0] == game.board[1][1] && game.board[0][0] == game.board[2][2]) | |
|| (game.board[1][1] != null && game.board[0][2] == game.board[1][1] && game.board[0][2] == game.board[2][0]) | |
){ | |
return api.game.determineWinningState(game, game.board[1][1]); | |
} | |
// tie | |
var tied = true; | |
game.board.forEach(function(row){ | |
row.forEach(function(square){ | |
if(square == null){ | |
tied = false; | |
} | |
}); | |
}); | |
if(tied == true){ | |
return "tied"; | |
}else{ | |
return "playing" | |
} | |
}, | |
determineWinningState: function(game, symbol){ | |
if(symbol == game.playerMarker){ | |
return "you won" | |
}else{ | |
return "you lost" | |
} | |
}, | |
aiTurn: function(game){ | |
var options = [ | |
[], [], [] | |
]; | |
var bestMove = [null, null]; | |
var bestScore = null; | |
var y = -1; | |
game.board.forEach(function(row){ | |
y++; | |
x = -1; | |
row.forEach(function(square){ | |
x++; | |
if(square != null){ | |
options[y][x] = null | |
}else{ | |
var proposedGameAi = api.utils.objClone(game); | |
var proposedGamePlayer = api.utils.objClone(game); | |
proposedGameAi.board = JSON.parse(JSON.stringify(game.board)); | |
proposedGamePlayer.board = JSON.parse(JSON.stringify(game.board)); | |
proposedGameAi.board[y][x] = game.computerMarker; | |
proposedGamePlayer.board[y][x] = game.playerMarker; | |
if(api.game.determineGameState(proposedGameAi) == "you lost"){ | |
options[y][x] = 1 // I win | |
if(1 > bestScore || bestScore == null){ | |
bestScore = 1; | |
bestMove = [y,x] | |
} | |
}else if(api.game.determineGameState(proposedGamePlayer) == "you won"){ | |
options[y][x] = 0.5 // I blocked a player win | |
if(0.5 > bestScore || bestScore == null){ | |
bestScore = 0.5; | |
bestMove = [y,x] | |
} | |
}else{ | |
options[y][x] = 0 | |
if(0 > bestScore || bestScore == null){ | |
bestScore = 0; | |
bestMove = [y,x] | |
} | |
} | |
} | |
}); | |
}); | |
if(bestScore != null){ | |
game.board[bestMove[0]][bestMove[1]] = game.computerMarker; | |
} | |
}, | |
} | |
next(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
And here is the game played by telnet: