Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Rw.io adn stuff

  • Loading branch information...
commit d0f9a8157de73919610895a3ac91b5be48ea5dae 1 parent a998ca0
@1602 1602 authored
View
52 app/controllers/game_controller.js
@@ -0,0 +1,52 @@
+
+load('application');
+layout('application');
+
+if (!app.games) app.games = [];
+
+action('thegame', function () {
+ this.title = 'Reversi Game';
+
+ // already in game
+ if (session.color && session.game) {
+ return ok();
+ }
+
+ // pending game (was), join opponent
+ if (app.game) {
+ session.color = app.game.join(req.sessionID);
+ session.game = app.games.length;
+ socket(app.game.starter).emit('opponent joined');
+ app.games.push(app.game);
+ app.game = false;
+ }
+
+ // new game, join starter
+ else {
+ app.game = new Game;
+ session.color = app.game.join(req.sessionID);
+ session.game = app.games.length;
+ }
+
+ return ok();
+
+ function ok() {
+ render({player: session.color, game: app.games[session.game] || app.game});
+ }
+
+});
+
+action('move', function () {
+ var game = app.games[session.game];
+ var success = game.move(session.player, params);
+ if (success) {
+ var opponent = game.other(req.sessionID);
+ socket(opponent).emit('move', params);
+ socket().emit('wait');
+ if (game.end()) {
+ socket(opponent).emit('end');
+ socket().emit('end');
+ }
+ }
+});
+
View
2  app/helpers/game_helper.js
@@ -0,0 +1,2 @@
+module.exports = {
+};
View
51 app/models/game.js
@@ -0,0 +1,51 @@
+var reversi = require(app.root + '/public/javascripts/reversi.js');
+
+function Game() {
+ this.reversi = reversi.createGame();
+}
+
+Game.prototype.join = function (id) {
+ if (!this.starter) {
+ console.log('Starter joined', id);
+ this.starter = id;
+ } else {
+ console.log('Opponent joined', id);
+ this.opponent = id;
+ }
+ return this.reversi.join();
+};
+
+Game.prototype.state = function (player) {
+ if (!this.opponent) {
+ return 'wait opponent';
+ } else if (this.reversi.can_player_move(player)) {
+ return 'move';
+ } else {
+ return 'wait';
+ }
+};
+
+Game.prototype.move = function (player, coords) {
+ this.player = player;
+ return this.reversi.move(coords);
+};
+
+Game.prototype.other = function (id) {
+ return this.opponent === id ? this.starter : this.opponent;
+};
+
+Game.prototype.end = function () {
+ return this.reversi.board.terminal_board;
+};
+
+Game.prototype.winner = function () {
+ var s = this.reversi.board.board_stats;
+ return s.b > s.w ? 'b' : 'w';
+};
+
+Game.prototype.boardToJSON = function () {
+ return JSON.stringify(this.reversi.board.position);
+};
+
+module.exports = Game;
+
View
1  app/views/game/thegame.ejs
@@ -0,0 +1 @@
+<div class="page-header"><h1>game#thegame</h1></div>
View
50 app/views/layouts/application_layout.ejs
@@ -14,28 +14,36 @@
</div>
</div>
</div>
-
<div class="container">
- <% var flash = request.flash('info').pop(); if (flash) { %>
- <div class="alert alert-info">
- <a class="close" data-dismiss="alert">×</a>
- <%- flash %>
+ <div id="board">
+ <div class="head">
+ <div class="info">
+ </div>
+ <div id="counter">
+ <div class="black"></div>
+ <div class="delim"></div>
+ <div class="white"></div>
+ </div>
</div>
- <% } %>
-
- <% flash = request.flash('error').pop(); if (flash) { %>
- <div class="alert alert-error">
- <a class="close" data-dismiss="alert">×</a>
- <%- flash %>
- </div>
- <% }; %>
-
- <%- body %>
-
- <hr />
- <footer>
- <p>&copy; Company 2012</p>
- </footer>
+ <canvas width="300" height="300"></canvas>
+ </div>
</div>
+ <footer>
+ <p>&copy; Rolling on <a href="http://railwayjs.com">RailwayJS</a> and <a href="socket.io">Socket.IO</a>, 2012</p>
+ </footer>
</body>
-</html>
+
+ <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
+ <script type="text/javascript" src="/javascripts/rails.js"></script>
+ <script type="text/javascript" src="/javascripts/reversi.js"></script>
+ <script type="text/javascript" src="/javascripts/reversi-cli.js"></script>
+ <script type="text/javascript" src="/socket.io/socket.io.js"></script>
+ <script>
+ var BOARD = {
+ player: '<%= player %>',
+ color: '<%= game.color || 'w' %>',
+ state: '<%= game.state(player) %>',
+ position: <%- game.boardToJSON() %>
+ };
+ </script>
+</html>
View
4 config/routes.js
@@ -1,7 +1,11 @@
+
exports.routes = function (map) {
+ map.root('game#thegame');
+ map.socket('move', 'game#move');
// Generic routes. Add all your routes below this line
// feel free to remove generic routes
map.all(':controller/:action');
map.all(':controller/:action/:id');
};
+
View
1  npmfile.js
@@ -1,3 +1,4 @@
+require('rw.io');
require('ejs-ext');
require('jugglingdb');
require('seedjs');
View
2  package.json
@@ -9,6 +9,8 @@
, "railway": ">= 0.2.6"
, "jugglingdb": ">= 0.1.0"
, "coffee-script": ">= 1.1.1"
+ , "socket.io": "latest"
+ , "rw.io": "latest"
},
"devDependencies":
{ "nodeunit": "*"
View
96 public/index.html
@@ -1,96 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <title>Welcome to Railway</title>
- <link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.3.0/bootstrap.min.css">
- <meta name="viewport" content="width=500, initial-scale=0.5">
- <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
- <script type="text/javascript" src="/javascripts/rails.js"></script>
- </head>
- <body>
- <div class="container">
- <div class="page-header">
- <h1 style="text-align: center; padding: 35px;">Welcome to Railway</h1>
- </div>
- <h6 style="text-align: center;">
- <a href="/railway/environment.json" id="show-env-info-link" data-remote="true" data-jsonp="load">Information about application environment</a>
- </h6>
- <div id="env-info"></div>
- <hr />
- <div class="row">
- <div class="span8 offset4">
- <a href="http://github.com/1602/express-on-railway"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://d3nwyuy0nl342s.cloudfront.net/img/e6bef7a091f5f3138b8cd40bc3e114258dd68ddf/687474703a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub"></a>
- <div class="content">
- <h6>1. Start with <a href="http://railwayjs.com/#generators">generators</a></h6>
- <p> this is a fastest way to create application:</p>
- <pre>railway g crud post title content date:date published:boolean</pre>
- <h6>2. Then describe <a href="http://railwayjs.com/#routing">routes</a></h6>
- <p>in <code>config/routes.js</code> and remove this file (<code>public/index.html</code>)</p>
- <pre>exports.routes = function (map) {
- map.get('/', 'posts#index');
-};</pre>
- <h6>3. Design your <a href="http://railwayjs.com/#orm">database</a></h6>
- <p>in <code>db/schema.js</code> and describe models in <code>app/models/*</code></p>
- <h6>4. Keep you <a href="http://railwayjs.com/#controllers">controllers</a> thin</h6>
- <p>write tests, and good luck.<br> If you have any questions feel free to ask at <a href="http://groups.google.com/group/railwayjs">RailwayJS Google Group</a>.</p>
- <p>Track RailwayJS project state on <a href="https://trello.com/board/railwayjs/4f0a0d49128365065e008a1d">trello board</a>, vote for features, discuss. Help us to get better!</p>
- </div>
- </div>
-
- <div class="span4">
- <p>
- <h6>5. Links in sidebar</h6>
- <ul style="padding-left: 0px;">
- <li><a href="http://railwayjs.com">RailwayJS project docs</a></li>
- <li><a href="http://modules.node-js.ru">NodeJS modules rating</a></li>
- <li><a href="http://expressjs.com">ExpressJS project homepage</a></li>
- <li><a href="http://mongoosejs.com">MongooseJS ORM project homepage</a></li>
- <li><a href="http://railwayjs.com/juggling.html">Try JugglingDB in your browser, docs</a></li>
- </ul>
- </p>
- </div>
-
- </div>
- <footer>
- </footer>
- <script>
- function load(data) {
- $('#show-env-info-link').hide();
- if (data.forbidden) {
- return '';
- }
- var html = '<table class=".zebra-striped">';
- html += makeList('Versions', data.versions);
- html += makeList('Settings', data.settings);
- html += makeList('Application', data.application);
- html += makeList('ENV', data.env);
- html += '</table>';
-
- $('#env-info').html(html);
- }
-
- function makeList(title, obj) {
- var res = '<tr><td colspan="2"><h6>' + title + '</h6></td></tr>';
- for (var i in obj) {
- res += '<tr><td>' + i + '</td><td>' + toS(obj[i]) + '</td></tr>';
- }
- return res;
- }
-
- function toS(obj) {
- if (obj instanceof Array) {
- return obj.join('<br />');
- } else if (typeof obj === 'object') {
- var s = [];
- for (var i in obj) {
- s.push(i + ': ' + obj[i]);
- }
- return s.join(', ');
- } else {
- return obj;
- }
- }
- </script>
- </div>
- </body>
-</html>
View
355 public/javascripts/reversi-cli.js
@@ -0,0 +1,355 @@
+$.fn.reversi = function (player, current_color, board, drawer) {
+ return new window.exports.ReversiGame(player, current_color, board, drawer, this);
+};
+$(function () {
+ var undef;
+ var cell_size = 40;
+ var padding = 10;
+ var board_size = cell_size * 8 + padding * 2;
+ var board = $('#board canvas')[0];
+ var info = $('#board .info');
+ var game;
+ var CURRENT_POSITION;
+
+ board.width = board_size;
+ board.height = board_size;
+
+ function clear(r) {
+ context.fillStyle = '#ccb';
+ context.fillRect(-cell_size/2, -cell_size/2, cell_size, cell_size);
+ }
+
+ function circle(r, style) {
+ if (!style) {
+ style = '#bbb,#ddd';
+ }
+ style = style.split(',');
+ var relief = style[2] ? 1 : -1;
+ var grad = style[0].split('-');
+ if (grad.length > 1) {
+ style[0] = context.createRadialGradient(
+ relief * cell_size*.25, relief * cell_size*.25, cell_size*.05,
+ 0, 0, cell_size*.5
+ );
+ style[0].addColorStop(0, grad[0]);
+ style[0].addColorStop(.6, grad[1]);
+ style[0].addColorStop(1, grad[2]);
+ }
+
+ context.beginPath();
+ context.arc(0, 0, r, 0, Math.PI * 2, true);
+ context.closePath();
+ context.fillStyle = style[0];
+ context.fill();
+
+ context.strokeWeight = 1;
+ context.beginPath();
+ context.arc(0, 0, r, 0, Math.PI * 2, true);
+ context.globalAlpha = 0.5;
+ context.strokeStyle = style[1];
+ context.stroke();
+ }
+
+ var s = {
+ b: '#666-#222-#000,#000',
+ w: '#fff-#e3e3e3-#afafaf,#aaa',
+ 0: '#c2c2b4-#c2c2b0-#b0b0a0,#bbb,-',
+ // 0: '#b4b4b4-#b0b0b0-#999,#bbb,-',
+ W: '#ccc-#ccc-#aaa,#ccc,-',
+ B: '#888-#888-#666,#888,-' };
+
+ function place_chip(i, j, c) {
+ context.save();
+ context.translate((j + 0.5) * cell_size, (i + 0.5) * cell_size);
+ clear();
+ circle(cell_size / 2 - 2, s[c]);
+ context.restore();
+ }
+
+ function initial_draw(position) {
+ if (OVER_CHIP) {
+ place_chip(OVER_CHIP.i, OVER_CHIP.j, '0');
+ OVER_CHIP = false;
+ }
+ CURRENT_POSITION = position;
+ for (var i = 0; i < 8; i++) {
+ for (var j = 0; j < 8; j++) {
+ place_chip(i, j, position[i][j]);
+ }
+ }
+ }
+
+ function animate_revert(diff, callback, step, what) {
+ var steps = [.8, .5, .3, .1, 'r', .1, .3, .5, .8, 1];
+ if (step === undef) {
+ step = 0;
+ }
+ if (what === undef) {
+ what = 'from';
+ }
+ if (!steps[step]) {
+ callback();
+ return;
+ }
+ if (steps[step] === 'r') {
+ what = 'to';
+ step++;
+ }
+ for (var p in diff) {
+ context.save();
+ context.translate((diff[p].j + 0.5) * cell_size, (diff[p].i + 0.5) * cell_size);
+ circle(cell_size / 2 - 0, s['0']);
+ context.rotate(Math.PI*.25);
+ context.scale(steps[step], 1);
+ circle(cell_size / 2 - 2, s[diff[p][what]]);
+ context.restore();
+ }
+ setTimeout(function () {
+ animate_revert(diff, callback, step + 1, what);
+ }, 1000/25);
+ }
+
+ function clicked(x, y) {
+ console.log(x, y);
+ if (!game.can_player_move()) return;
+ var i = Math.ceil((y - padding) / cell_size) - 1;
+ var j = Math.ceil((x - padding) / cell_size) - 1;
+
+ if (i < 0 || j < 0 || i > 7 || j > 7) {
+ return;
+ }
+
+ context.fillStyle = '#FFF';
+ OVER_CHIP.p = CURRENT_POSITION[i][j];
+ game.move(i + ':' + j);
+ }
+
+ var $board = $(board);
+ $(board).click(function (e) {
+ var offset = $board.offset();
+ clicked(e.clientX - offset.left + window.scrollX, e.clientY - offset.top + window.scrollY);
+ });
+
+ var OVER_CHIP = null;
+ function moved(x, y) {
+ if (!game.can_player_move()) return;
+ var i = Math.ceil((y - padding) / cell_size) - 1;
+ var j = Math.ceil((x - padding) / cell_size) - 1;
+ if (i < 0 || j < 0 || i > 7 || j > 7) {
+ return;
+ }
+
+ var p = CURRENT_POSITION[i][j];
+ if (OVER_CHIP && (OVER_CHIP.j != i || OVER_CHIP.i != j)) {
+ place_chip(OVER_CHIP.i, OVER_CHIP.j, OVER_CHIP.p);
+ OVER_CHIP = false;
+ }
+ if ((p == 'W' || p == 'B') && (!OVER_CHIP || (OVER_CHIP.i != i && OVER_CHIP.j != j))) {
+ OVER_CHIP = {i: i, j: j, p: p};
+ context.globalAlpha = 0.5;
+ place_chip(i, j, p.toLowerCase());
+ context.globalAlpha = 1;
+ }
+ }
+
+ $(board).mousemove(function (e) {
+ var offset = $board.offset();
+ moved(e.clientX - offset.left + window.scrollX, e.clientY - offset.top + window.scrollY);
+ });
+
+ var context = board.getContext('2d');
+ context.fillStyle = "#c4c4b3";
+ context.fillRect(0, 0, board_size, board_size);
+ context.translate(padding, padding);
+
+ function update_board(data) {
+ if (data.prev_position) {
+ var diff = [], from, to;
+ for (var i = 0; i < 8; i++) {
+ for (var j = 0; j < 8; j++) {
+ from = data.prev_position[i][j];
+ to = data.position[i][j];
+ if ((from == 'w' || from == 'b') && from != to) {
+ diff.push({i:i, j:j, from:from, to:to});
+ }
+ if (from == 'W' || from == 'B') {
+ if (to == 'b' || to == 'w') {
+ place_chip(i, j, to);
+ }
+ }
+ }
+ }
+ animate_revert(diff, function () {
+ initial_draw(data.position);
+ });
+ } else {
+ initial_draw(data.position);
+ }
+ }
+
+ var res = BOARD;
+
+ s[res.player == 'w' ? 'B' : 'W'] = s['0'];
+ game = $('#board').reversi(res.player, res.color == 'b', res.position, update_board);
+ game.join();
+ var send_to_socket = true;
+
+ function update_counter(s) {
+ $('#counter .black').html(s.b);
+ $('#counter .white').html(s.w);
+ }
+
+ function auto_move() {
+ console.log('automove', window.autoplay);
+ var len, move, p;
+ if (window.autoplay) {
+ len = game.board.availableCells.length;
+ if (len == 0) return;
+ switch (window.autoplay) {
+ case 'up':
+ move = 0;
+ break;
+ case 'down':
+ move = len - 1;
+ break;
+ default:
+ case 'random':
+ move = Math.round(Math.random() * (len - 1));
+ break;
+ }
+ p = game.board.availableCells[move];
+ game.move(p.i + ':' + p.j);
+ }
+ }
+ window.auto_move = auto_move;
+
+ var RIO = new io.connect(location.hostname);
+
+ game.afterMove = function (coords) {
+ console.log('after move ' + coords);
+ console.log(send_to_socket);
+ update_counter(game.board.board_stats);
+ if (!game.pwned_by(res.player)) {
+ info.html('Opponent\'s turn.');
+ setTimeout(function () {
+ // game.move();
+ }, 500);
+ } else {
+ info.html('Your turn.');
+ auto_move();
+ }
+ if (send_to_socket) {
+ RIO.emit('move', coords);
+ } else {
+ }
+ send_to_socket = true;
+ };
+
+ if (res.state == 'move') {
+ game.join();
+ game.board.color = res.color == 'b';
+ info.html('Your turn');
+ } else if (res.action == 'end') {
+ info.html('Game finished');
+ } else if (res.action == 'wait') {
+ info.html('Opponents turn');
+ } else {
+ info.html('Awaiting for opponent connection');
+ }
+ update_counter(game.board.board_stats);
+
+ RIO.on('disconnect', function () {
+ info.html('You are offline');
+ setTimeout(function () {
+ // RIO.disconnect();
+ info.html('Reconnecting');
+ try {
+ // RIO.connect();
+ } catch (e) {
+ // alert(e.message);
+ }
+ }, 1000);
+ });
+ RIO.on('move', function (coords) {
+ game.join();
+ send_to_socket = false;
+ game.move(coords);
+ });
+
+ RIO.on('end', function (win) {
+ info.html('End game. You ' + (
+ win === res.player ? 'win' : 'lose'
+ ) + '.');
+ });
+
+ RIO.on('message', function (msg) {
+ console.log('incoming message:', msg);
+ if (typeof msg == 'string') {
+ msg = JSON.parse(msg);
+ }
+ switch (msg.action) {
+ case 'restart_session':
+ location.href = location.href;
+ break;
+ case 'opponent_connected':
+ game.join();
+ info.html('Opponent joined. You can move.');
+ var $opp = $('#opponent_info');
+ $opp.find('.avatar img').attr('src', 'http://graph.facebook.com/' + msg.user.id + '/picture');
+ $opp.find('.username').html(msg.user.name);
+ $opp.show();
+ break;
+ case 'opponent_disconnected':
+ info.html('Opponent disconnected');
+ break;
+ case 'opponent_reconnected':
+ info.html('Opponent reconnected');
+ break;
+ case 'move':
+ game.join();
+ send_to_socket = false;
+ game.move(msg.coords);
+ if (typeof window.autoplay !== 'undefined') {
+ setTimeout(function () {
+ auto_move(window.autoplay);
+ }, 1000);
+ }
+ break;
+ case 'end':
+ break;
+ }
+ });
+});
+$(function () {
+ // bind cheets
+ // $('#autoplay_random').click(function () {
+ // window.autoplay = !window.autoplay;
+ // if (window.autoplay) {
+ // //window.auto_move();
+ // }
+ // $(this).html(window.autoplay ? 'Switch autoplay off' : 'Switch autoplay on');
+ // });
+
+ function autoplay_strategy(str) {
+ return function () {
+ console.log('autoplay ' + str);
+ if (window.autoplay) {
+ $('#autoplay_' + window.autoplay).html('Autoplay ' + window.autoplay);
+ window.autoplay = false;
+ } else {
+ $(this).html('Stop autoplay');
+ window.autoplay = str;
+ //window.auto_move();
+ }
+ }
+ };
+ var strategies = ['up', 'down', 'random'];
+ $(strategies).each(function (i, val) {
+ console.log(val);
+ $('#autoplay_' + val).click(autoplay_strategy(val));
+ });
+
+ // autoplay_strategy('random')();
+});
+
+
View
363 public/javascripts/reversi.js
@@ -0,0 +1,363 @@
+(function () {
+ var $ = function (str) {
+ var arr = typeof str == 'string' ? str.split(',') : str;
+ arr.each = function (callback) {
+ for (var i = 0, l = this.length; i < l; i++) {
+ callback.apply(arr[i], [i, arr[i]]);
+ }
+ };
+ return arr;
+ };
+ var s = /\s+/, undef,
+ matrix = { 1: [0, 1], 2: [0, -1], 3: [1, 0], 4: [-1, 0], 5: [1, -1], 6: [1, 1], 7: [-1, -1], 8: [-1, 1] };
+ function Board(position, color) {
+ // boolean color (false => white, true => black)
+ // default black
+ this.color = ( color == undef ) ? true : color;
+
+ this.init_position(position);
+ }
+
+ Board.prototype.init_position = function (position) {
+ var point,
+ i, j, w = 0, b = 0;
+
+ this.position = position;
+ this.empty_cells = [];
+ this.nonempty_cells = [];
+
+ // empty/nonempty cells and board stats
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 8; j++) {
+ point = {i: i, j: j};
+ if (this.empty(i, j)) {
+ this.empty_cells.push(point);
+ } else {
+ this.nonempty_cells.push(point);
+ if (this.position[i][j] == 'b') {
+ b++;
+ } else if (this.position[i][j] == 'w') {
+ w++;
+ }
+ }
+ }
+ }
+
+ if (!this.can_move()) {
+ this.color = !this.color;
+ if (!this.can_move()) {
+ // no. game is over
+ this.terminal_board = true;
+ } else {
+ // yep. repeat same player
+ this.same_color_again = true;
+ }
+ }
+
+ this.board_stats = {b: b, w: w, moves: this.availableCells.length};
+ };
+
+ Board.prototype.empty = function (i, j) {
+ var c = this.position[i][j];
+ return c != 'w' && c != 'b';
+ };
+
+ $('empty_cell,nonempty_cell,availableCell').each(function(i, name) {
+ Board.prototype['each_' + name] = function (callback) {
+ var self = this
+ $(self[name + 's']).each(function (i, p) {
+ callback.apply(self, [p]);
+ });
+ };
+ });
+
+ Board.prototype.draw = function (player) {
+ return {
+ player: player,
+ prev_position: this.parent ? this.parent.position : undef,
+ position: this.position,
+ map: {
+ w: 'white', b: 'black', 0: 'empty',
+ W: this.color || player == 'b' ? 'empty' : 'move_here white',
+ B: !this.color || player == 'w' ? 'empty' : 'move_here black',
+ },
+ right_info: this.board_stats.w + ':' + this.board_stats.b,
+ left_info: this.terminal_board ?
+ 'Win of ' + (this.board_stats.b > this.board_stats.w ? 'blacks' : 'whites') :
+ 'Move of ' + (this.color ? 'blacks' : 'whites')
+ };
+ };
+
+ Board.prototype.move = function (coords) {
+ if (this.availableCells.length == 0) {
+ return false;
+ } else if (coords !== undef) {
+ var can_move = false;
+ this.each_availableCell(function (p) {
+ if (p.i + ":" + p.j == coords) {
+ can_move = true;
+ }
+ });
+ if (!can_move) {
+ return false;
+ }
+ }
+ if (coords === undef) {
+ coords = this.ai();
+ }
+ if (!this.childBoards) {
+ this.calcDepth(1, coords);
+ }
+ return this.childBoards[coords];
+ };
+ Board.prototype.ai = function () {
+ if (this.color) {
+ var variants = [];
+ this.calcDepth(1);
+ this.each_availableCell(function (p) {
+ var pp = p.i + ':' + p.j;
+ variants.push({cost: this.childBoards[pp].cost, p: pp});
+ });
+ variants.sort(function (x, y) {
+ return -y.cost + x.cost;
+ });
+ return variants[0].p;
+ } else {
+ var i = Math.round((this.availableCells.length - 1) * Math.random());
+ }
+ return this.availableCells[i].i + ':' + this.availableCells[i].j;
+ };
+ Board.prototype.calcDepth = function (howDeep, point) {
+ var self = this;
+ if (!this.childBoards) {
+ this.childBoards = {};
+ }
+ if (point) {
+ var position = this.clonePosition();
+ if (vectors(this.color, position, point, 'check')) {
+ board = new Board(position, !this.color);
+ board.parent = this;
+ this.childBoards[point] = board;
+ if (howDeep > 1) {
+ board.calcDepth(howDeep - 1);
+ }
+ }
+ } else {
+ this.each_availableCell(function (point) {
+ var position = this.clonePosition(), board;
+ if (vectors(this.color, position, point, 'check')) {
+ board = new Board(position, !this.color);
+ board.parent = this;
+ board.estimate();
+ self.childBoards[point.i + ':' + point.j] = board;
+ }
+ });
+ if (howDeep > 1) {
+ for (var c in this.childBoards) {
+ this.childBoards[c].calcDepth(howDeep - 1);
+ }
+ }
+ }
+ };
+ Board.prototype.can_move = function () {
+ this.availableCells = [];
+ this.each_empty_cell(function (point) {
+ this.position[point.i][point.j] = '0';
+ if (vectors(this.color, this.position, point, 'look')) {
+ this.availableCells.push(point);
+ }
+ });
+ return this.availableCells.length;
+ };
+ Board.prototype.clonePosition = function () {
+ var b = [], row;
+ for (var i = 0; i < 8; i++) {
+ row = [];
+ for (var j = 0; j < 8; j++) {
+ row.push(this.position[i][j]);
+ }
+ b.push(row);
+ }
+ return b;
+ };
+ Board.prototype.pwned_by = function (c) {
+ var color = this.color ? 'b' : 'w';
+ if (c) {
+ return color == c || c == 'x';
+ } else {
+ return color;
+ }
+ };
+ function vectors(black, board, point, action) {
+ var move_state = false;
+ if (typeof point == 'string') {
+ point = point.split(':');
+ point = {i: parseInt(point[0], 10), j: parseInt(point[1], 10)};
+ }
+ for (var d in matrix) {
+ if (matrix.hasOwnProperty(d)) {
+ if (try_vector(black, board, point, action, matrix[d], 1)) {
+ move_state = true;
+ }
+ }
+ }
+ return move_state;
+ }
+ function try_vector(black, board, point, action, dir, delta, move_state) {
+ var color = black ? 'b' : 'w',
+ opcolor = black ? 'w' : 'b',
+ t_i = point.i + delta * dir[0],
+ t_j = point.j + delta * dir[1], new_delta = 0;
+ if (typeof move_state == 'undefined') {
+ move_state = false;
+ }
+ if (action == 'turn' && delta >= 0) {
+ board[t_i][t_j] = color;
+ new_delta = delta - 1;
+ if (new_delta == 0) {
+ board[point.i][point.j] = color;
+ return true;
+ }
+ } else if (action == 'check' || action == 'look') {
+ if (0 <= t_i && t_i < 8 && 0 <= t_j && t_j < 8) {
+ if (board[t_i][t_j] == opcolor) {
+ new_delta = delta + 1;
+ } else if (board[t_i][t_j] == color && delta > 1) {
+ if (action == 'look') {
+ board[point.i][point.j] = color.toUpperCase();
+ return true;
+ }
+ move_state = true;
+ action = 'turn';
+ new_delta = delta - 1;
+ }
+ }
+ }
+ if (new_delta == 0) {
+ return move_state;
+ } else {
+ return try_vector(black, board, point, action, dir, new_delta, move_state)
+ }
+ }
+ var costs = [
+ '15 2 4 4 4 4 2 15'.split(s),
+ '2 1 3 3 3 3 1 2'.split(s),
+ '4 2 3 2 2 3 2 4'.split(s),
+ '4 3 2 1 1 2 3 4'.split(s),
+ '4 3 2 1 1 2 3 4'.split(s),
+ '4 2 3 2 2 3 2 4'.split(s),
+ '2 1 3 3 3 3 1 2'.split(s),
+ '15 2 4 4 4 4 2 15'.split(s)
+ ];
+ Board.prototype.estimate = function (color) {
+ var cost = 0, m = 1, c, board = this.position, color = (this.color ? 'b' : 'w');
+ this.each_nonempty_cell(function (point) {
+ c = board[point.i][point.j];
+ m = c == color ? 1 : -1;
+ cost += parseInt(costs[point.i][point.j], 10) * m;
+ });
+ if (this.same_color_again) {
+ cost += 40;
+ }
+ this.cost = cost;
+ };
+
+ function Game(player, nextStepColor, base_position, drawer, container) { // {{{
+ base_position = base_position || [
+ '0 0 0 0 0 0 0 0'.split(s),
+ '0 0 0 0 0 0 0 0'.split(s),
+ '0 0 0 0 0 0 0 0'.split(s),
+ '0 0 0 w b 0 0 0'.split(s),
+ '0 0 0 b w 0 0 0'.split(s),
+ '0 0 0 0 0 0 0 0'.split(s),
+ '0 0 0 0 0 0 0 0'.split(s),
+ '0 0 0 0 0 0 0 0'.split(s)
+ ];
+ var lock = false;
+ this.player = player;
+ this.externalDrawer = drawer;
+ this.container = container;
+
+ this.start = function (nextStepColor) {
+ this.boards = [];
+ this.opponents = 0;
+ this.board = new Board(base_position, nextStepColor);
+ this.draw();
+ };
+
+ this.state = function () {
+ if (this.opponents < 2) {
+ return 'not enough players';
+ }
+ if (this.board.terminal_board) {
+ return 'end';
+ }
+ return 'turn';
+ };
+
+ this.pwned_by = function (c) {
+ if (c) {
+ return this.state() == 'turn' && this.board.pwned_by(c);
+ } else {
+ return this.board.pwned_by();
+ }
+ };
+
+ this.can_player_move = function (player) {
+ var x = this.pwned_by(player);
+ return x;
+ };
+
+ this.join = function () {
+ if (this.opponents > 2) {
+ return '0';
+ }
+ this.opponents = parseInt(this.opponents, 10) + 1;
+ return this.opponents == 1 ? 'b' : 'w';
+ };
+
+ this.move = function (coords, skip_callback) {
+ if (lock) {
+ return;
+ }
+ var self = this;
+ var board = this.board.move(coords);
+ if (board) {
+ this.boards.push(this.board);
+ this.board = board;
+ this.draw();
+ if (!skip_callback && this.afterMove) {
+ this.afterMove(coords);
+ }
+ } else {
+ console.log('Invalid move ' + coords);
+ console.log(this.board.availableCells);
+ }
+ return !!board;
+ };
+
+ this.draw = function () {
+ if (this.externalDrawer) {
+ this.externalDrawer.call(this.container, this.board.draw(this.player));
+ }
+ };
+
+ this.boardToJSON = function () {
+ return JSON.stringify(this.board);
+ }
+
+ this.start(nextStepColor);
+
+ // }}}
+ }
+
+ if (typeof exports == 'undefined') {
+ exports = {};
+ }
+ exports.ReversiGame = Game;
+ exports.createGame = function (db_game) {
+ return new Game('w', false, null);
+ };
+
+})();
+// vim:set foldmethod=marker
Please sign in to comment.
Something went wrong with that request. Please try again.