├── .gitignore ├── Procfile ├── README.md ├── chess.html ├── images ├── B_bishop.png ├── B_king.png ├── B_knight.png ├── B_pawn.png ├── B_queen.png ├── B_rook.png ├── W_bishop.png ├── W_king.png ├── W_knight.png ├── W_pawn.png ├── W_queen.png ├── W_rook.png ├── arrow_down.png ├── arrow_up.png └── chess-demo.gif ├── node_modules └── socket.io │ ├── Readme.md │ ├── lib │ ├── namespace.js │ └── socket.js │ └── package.json ├── package.json ├── scripts ├── Analyzer.js ├── GamePlay.js ├── OnlinePlay.js ├── settings.js ├── ui.js └── utils.js ├── secrets.example.js ├── server.js └── styles.css /.gitignore: -------------------------------------------------------------------------------- 1 | *.*.un~ 2 | *.*.swp 3 | node_modules 4 | secrets.js 5 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node server.js 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chess 2 | 3 | An online Chess game made with [NodeJS](http://nodejs.org/) and Websockets (socket.io). 4 | 5 | ![Demo](./images/chess-demo.gif) 6 | 7 | To run: 8 | ``` 9 | npm install package.json 10 | node server.js 11 | ``` 12 | 13 | Browse to `localhost:5000`. -------------------------------------------------------------------------------- /chess.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Chess 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 |
17 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /images/B_bishop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/B_bishop.png -------------------------------------------------------------------------------- /images/B_king.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/B_king.png -------------------------------------------------------------------------------- /images/B_knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/B_knight.png -------------------------------------------------------------------------------- /images/B_pawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/B_pawn.png -------------------------------------------------------------------------------- /images/B_queen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/B_queen.png -------------------------------------------------------------------------------- /images/B_rook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/B_rook.png -------------------------------------------------------------------------------- /images/W_bishop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/W_bishop.png -------------------------------------------------------------------------------- /images/W_king.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/W_king.png -------------------------------------------------------------------------------- /images/W_knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/W_knight.png -------------------------------------------------------------------------------- /images/W_pawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/W_pawn.png -------------------------------------------------------------------------------- /images/W_queen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/W_queen.png -------------------------------------------------------------------------------- /images/W_rook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/W_rook.png -------------------------------------------------------------------------------- /images/arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/arrow_down.png -------------------------------------------------------------------------------- /images/arrow_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/arrow_up.png -------------------------------------------------------------------------------- /images/chess-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinAlbs/Chess/70b6209822bda7e77e3c05973d581ed1fee647bf/images/chess-demo.gif -------------------------------------------------------------------------------- /node_modules/socket.io/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # socket.io 3 | 4 | [![Backers on Open Collective](https://opencollective.com/socketio/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/socketio/sponsors/badge.svg)](#sponsors) 5 | [![Build Status](https://secure.travis-ci.org/socketio/socket.io.svg?branch=master)](https://travis-ci.org/socketio/socket.io) 6 | [![Dependency Status](https://david-dm.org/socketio/socket.io.svg)](https://david-dm.org/socketio/socket.io) 7 | [![devDependency Status](https://david-dm.org/socketio/socket.io/dev-status.svg)](https://david-dm.org/socketio/socket.io#info=devDependencies) 8 | [![NPM version](https://badge.fury.io/js/socket.io.svg)](https://www.npmjs.com/package/socket.io) 9 | ![Downloads](https://img.shields.io/npm/dm/socket.io.svg?style=flat) 10 | [![](https://slackin-socketio.now.sh/badge.svg)](https://slackin-socketio.now.sh) 11 | 12 | ## Features 13 | 14 | Socket.IO enables real-time bidirectional event-based communication. It consists of: 15 | 16 | - a Node.js server (this repository) 17 | - a [Javascript client library](https://github.com/socketio/socket.io-client) for the browser (or a Node.js client) 18 | 19 | Some implementations in other languages are also available: 20 | 21 | - [Java](https://github.com/socketio/socket.io-client-java) 22 | - [C++](https://github.com/socketio/socket.io-client-cpp) 23 | - [Swift](https://github.com/socketio/socket.io-client-swift) 24 | - [Dart](https://github.com/rikulo/socket.io-client-dart) 25 | 26 | Its main features are: 27 | 28 | #### Reliability 29 | 30 | Connections are established even in the presence of: 31 | - proxies and load balancers. 32 | - personal firewall and antivirus software. 33 | 34 | For this purpose, it relies on [Engine.IO](https://github.com/socketio/engine.io), which first establishes a long-polling connection, then tries to upgrade to better transports that are "tested" on the side, like WebSocket. Please see the [Goals](https://github.com/socketio/engine.io#goals) section for more information. 35 | 36 | #### Auto-reconnection support 37 | 38 | Unless instructed otherwise a disconnected client will try to reconnect forever, until the server is available again. Please see the available reconnection options [here](https://github.com/socketio/socket.io-client/blob/master/docs/API.md#new-managerurl-options). 39 | 40 | #### Disconnection detection 41 | 42 | A heartbeat mechanism is implemented at the Engine.IO level, allowing both the server and the client to know when the other one is not responding anymore. 43 | 44 | That functionality is achieved with timers set on both the server and the client, with timeout values (the `pingInterval` and `pingTimeout` parameters) shared during the connection handshake. Those timers require any subsequent client calls to be directed to the same server, hence the `sticky-session` requirement when using multiples nodes. 45 | 46 | #### Binary support 47 | 48 | Any serializable data structures can be emitted, including: 49 | 50 | - [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) and [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) in the browser 51 | - [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) and [Buffer](https://nodejs.org/api/buffer.html) in Node.js 52 | 53 | #### Simple and convenient API 54 | 55 | Sample code: 56 | 57 | ```js 58 | io.on('connection', socket => { 59 | socket.emit('request', /* … */); // emit an event to the socket 60 | io.emit('broadcast', /* … */); // emit an event to all connected sockets 61 | socket.on('reply', () => { /* … */ }); // listen to the event 62 | }); 63 | ``` 64 | 65 | #### Cross-browser 66 | 67 | Browser support is tested in Saucelabs: 68 | 69 | [![Sauce Test Status](https://saucelabs.com/browser-matrix/socket.svg)](https://saucelabs.com/u/socket) 70 | 71 | #### Multiplexing support 72 | 73 | In order to create separation of concerns within your application (for example per module, or based on permissions), Socket.IO allows you to create several `Namespaces`, which will act as separate communication channels but will share the same underlying connection. 74 | 75 | #### Room support 76 | 77 | Within each `Namespace`, you can define arbitrary channels, called `Rooms`, that sockets can join and leave. You can then broadcast to any given room, reaching every socket that has joined it. 78 | 79 | This is a useful feature to send notifications to a group of users, or to a given user connected on several devices for example. 80 | 81 | 82 | **Note:** Socket.IO is not a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds some metadata to each packet: the packet type, the namespace and the ack id when a message acknowledgement is needed. That is why a WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a WebSocket server (like `ws://echo.websocket.org`) either. Please see the protocol specification [here](https://github.com/socketio/socket.io-protocol). 83 | 84 | ## Installation 85 | 86 | ```bash 87 | npm install socket.io 88 | ``` 89 | 90 | ## How to use 91 | 92 | The following example attaches socket.io to a plain Node.JS 93 | HTTP server listening on port `3000`. 94 | 95 | ```js 96 | const server = require('http').createServer(); 97 | const io = require('socket.io')(server); 98 | io.on('connection', client => { 99 | client.on('event', data => { /* … */ }); 100 | client.on('disconnect', () => { /* … */ }); 101 | }); 102 | server.listen(3000); 103 | ``` 104 | 105 | ### Standalone 106 | 107 | ```js 108 | const io = require('socket.io')(); 109 | io.on('connection', client => { ... }); 110 | io.listen(3000); 111 | ``` 112 | 113 | ### In conjunction with Express 114 | 115 | Starting with **3.0**, express applications have become request handler 116 | functions that you pass to `http` or `http` `Server` instances. You need 117 | to pass the `Server` to `socket.io`, and not the express application 118 | function. Also make sure to call `.listen` on the `server`, not the `app`. 119 | 120 | ```js 121 | const app = require('express')(); 122 | const server = require('http').createServer(app); 123 | const io = require('socket.io')(server); 124 | io.on('connection', () => { /* … */ }); 125 | server.listen(3000); 126 | ``` 127 | 128 | ### In conjunction with Koa 129 | 130 | Like Express.JS, Koa works by exposing an application as a request 131 | handler function, but only by calling the `callback` method. 132 | 133 | ```js 134 | const app = require('koa')(); 135 | const server = require('http').createServer(app.callback()); 136 | const io = require('socket.io')(server); 137 | io.on('connection', () => { /* … */ }); 138 | server.listen(3000); 139 | ``` 140 | 141 | ## Documentation 142 | 143 | Please see the documentation [here](/docs/README.md). Contributions are welcome! 144 | 145 | ## Debug / logging 146 | 147 | Socket.IO is powered by [debug](https://github.com/visionmedia/debug). 148 | In order to see all the debug output, run your app with the environment variable 149 | `DEBUG` including the desired scope. 150 | 151 | To see the output from all of Socket.IO's debugging scopes you can use: 152 | 153 | ``` 154 | DEBUG=socket.io* node myapp 155 | ``` 156 | 157 | ## Testing 158 | 159 | ``` 160 | npm test 161 | ``` 162 | This runs the `gulp` task `test`. By default the test will be run with the source code in `lib` directory. 163 | 164 | Set the environmental variable `TEST_VERSION` to `compat` to test the transpiled es5-compat version of the code. 165 | 166 | The `gulp` task `test` will always transpile the source code into es5 and export to `dist` first before running the test. 167 | 168 | 169 | ## Backers 170 | 171 | Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/socketio#backer)] 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | ## Sponsors 206 | 207 | Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/socketio#sponsor)] 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | ## License 242 | 243 | [MIT](LICENSE) 244 | -------------------------------------------------------------------------------- /node_modules/socket.io/lib/namespace.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Socket = require('./socket'); 7 | var Emitter = require('events').EventEmitter; 8 | var parser = require('socket.io-parser'); 9 | var hasBin = require('has-binary2'); 10 | var debug = require('debug')('socket.io:namespace'); 11 | 12 | /** 13 | * Module exports. 14 | */ 15 | 16 | module.exports = exports = Namespace; 17 | 18 | /** 19 | * Blacklisted events. 20 | */ 21 | 22 | exports.events = [ 23 | 'connect', // for symmetry with client 24 | 'connection', 25 | 'newListener' 26 | ]; 27 | 28 | /** 29 | * Flags. 30 | */ 31 | 32 | exports.flags = [ 33 | 'json', 34 | 'volatile', 35 | 'local' 36 | ]; 37 | 38 | /** 39 | * `EventEmitter#emit` reference. 40 | */ 41 | 42 | var emit = Emitter.prototype.emit; 43 | 44 | /** 45 | * Namespace constructor. 46 | * 47 | * @param {Server} server instance 48 | * @param {Socket} name 49 | * @api private 50 | */ 51 | 52 | function Namespace(server, name){ 53 | this.name = name; 54 | this.server = server; 55 | this.sockets = {}; 56 | this.connected = {}; 57 | this.fns = []; 58 | this.ids = 0; 59 | this.rooms = []; 60 | this.flags = {}; 61 | this.initAdapter(); 62 | } 63 | 64 | /** 65 | * Inherits from `EventEmitter`. 66 | */ 67 | 68 | Namespace.prototype.__proto__ = Emitter.prototype; 69 | 70 | /** 71 | * Apply flags from `Socket`. 72 | */ 73 | 74 | exports.flags.forEach(function(flag){ 75 | Object.defineProperty(Namespace.prototype, flag, { 76 | get: function() { 77 | this.flags[flag] = true; 78 | return this; 79 | } 80 | }); 81 | }); 82 | 83 | /** 84 | * Initializes the `Adapter` for this nsp. 85 | * Run upon changing adapter by `Server#adapter` 86 | * in addition to the constructor. 87 | * 88 | * @api private 89 | */ 90 | 91 | Namespace.prototype.initAdapter = function(){ 92 | this.adapter = new (this.server.adapter())(this); 93 | }; 94 | 95 | /** 96 | * Sets up namespace middleware. 97 | * 98 | * @return {Namespace} self 99 | * @api public 100 | */ 101 | 102 | Namespace.prototype.use = function(fn){ 103 | if (this.server.eio && this.name === '/') { 104 | debug('removing initial packet'); 105 | delete this.server.eio.initialPacket; 106 | } 107 | this.fns.push(fn); 108 | return this; 109 | }; 110 | 111 | /** 112 | * Executes the middleware for an incoming client. 113 | * 114 | * @param {Socket} socket that will get added 115 | * @param {Function} fn last fn call in the middleware 116 | * @api private 117 | */ 118 | 119 | Namespace.prototype.run = function(socket, fn){ 120 | var fns = this.fns.slice(0); 121 | if (!fns.length) return fn(null); 122 | 123 | function run(i){ 124 | fns[i](socket, function(err){ 125 | // upon error, short-circuit 126 | if (err) return fn(err); 127 | 128 | // if no middleware left, summon callback 129 | if (!fns[i + 1]) return fn(null); 130 | 131 | // go on to next 132 | run(i + 1); 133 | }); 134 | } 135 | 136 | run(0); 137 | }; 138 | 139 | /** 140 | * Targets a room when emitting. 141 | * 142 | * @param {String} name 143 | * @return {Namespace} self 144 | * @api public 145 | */ 146 | 147 | Namespace.prototype.to = 148 | Namespace.prototype.in = function(name){ 149 | if (!~this.rooms.indexOf(name)) this.rooms.push(name); 150 | return this; 151 | }; 152 | 153 | /** 154 | * Adds a new client. 155 | * 156 | * @return {Socket} 157 | * @api private 158 | */ 159 | 160 | Namespace.prototype.add = function(client, query, fn){ 161 | debug('adding socket to nsp %s', this.name); 162 | var socket = new Socket(this, client, query); 163 | var self = this; 164 | this.run(socket, function(err){ 165 | process.nextTick(function(){ 166 | if ('open' == client.conn.readyState) { 167 | if (err) return socket.error(err.data || err.message); 168 | 169 | // track socket 170 | self.sockets[socket.id] = socket; 171 | 172 | // it's paramount that the internal `onconnect` logic 173 | // fires before user-set events to prevent state order 174 | // violations (such as a disconnection before the connection 175 | // logic is complete) 176 | socket.onconnect(); 177 | if (fn) fn(); 178 | 179 | // fire user-set events 180 | self.emit('connect', socket); 181 | self.emit('connection', socket); 182 | } else { 183 | debug('next called after client was closed - ignoring socket'); 184 | } 185 | }); 186 | }); 187 | return socket; 188 | }; 189 | 190 | /** 191 | * Removes a client. Called by each `Socket`. 192 | * 193 | * @api private 194 | */ 195 | 196 | Namespace.prototype.remove = function(socket){ 197 | if (this.sockets.hasOwnProperty(socket.id)) { 198 | delete this.sockets[socket.id]; 199 | } else { 200 | debug('ignoring remove for %s', socket.id); 201 | } 202 | }; 203 | 204 | /** 205 | * Emits to all clients. 206 | * 207 | * @return {Namespace} self 208 | * @api public 209 | */ 210 | 211 | Namespace.prototype.emit = function(ev){ 212 | if (~exports.events.indexOf(ev)) { 213 | emit.apply(this, arguments); 214 | return this; 215 | } 216 | // set up packet object 217 | var args = Array.prototype.slice.call(arguments); 218 | var packet = { 219 | type: (this.flags.binary !== undefined ? this.flags.binary : hasBin(args)) ? parser.BINARY_EVENT : parser.EVENT, 220 | data: args 221 | }; 222 | 223 | if ('function' == typeof args[args.length - 1]) { 224 | throw new Error('Callbacks are not supported when broadcasting'); 225 | } 226 | 227 | var rooms = this.rooms.slice(0); 228 | var flags = Object.assign({}, this.flags); 229 | 230 | // reset flags 231 | this.rooms = []; 232 | this.flags = {}; 233 | 234 | this.adapter.broadcast(packet, { 235 | rooms: rooms, 236 | flags: flags 237 | }); 238 | 239 | return this; 240 | }; 241 | 242 | /** 243 | * Sends a `message` event to all clients. 244 | * 245 | * @return {Namespace} self 246 | * @api public 247 | */ 248 | 249 | Namespace.prototype.send = 250 | Namespace.prototype.write = function(){ 251 | var args = Array.prototype.slice.call(arguments); 252 | args.unshift('message'); 253 | this.emit.apply(this, args); 254 | return this; 255 | }; 256 | 257 | /** 258 | * Gets a list of clients. 259 | * 260 | * @return {Namespace} self 261 | * @api public 262 | */ 263 | 264 | Namespace.prototype.clients = function(fn){ 265 | if(!this.adapter){ 266 | throw new Error('No adapter for this namespace, are you trying to get the list of clients of a dynamic namespace?') 267 | } 268 | this.adapter.clients(this.rooms, fn); 269 | // reset rooms for scenario: 270 | // .in('room').clients() (GH-1978) 271 | this.rooms = []; 272 | return this; 273 | }; 274 | 275 | /** 276 | * Sets the compress flag. 277 | * 278 | * @param {Boolean} compress if `true`, compresses the sending data 279 | * @return {Socket} self 280 | * @api public 281 | */ 282 | 283 | Namespace.prototype.compress = function(compress){ 284 | this.flags.compress = compress; 285 | return this; 286 | }; 287 | 288 | /** 289 | * Sets the binary flag 290 | * 291 | * @param {Boolean} Encode as if it has binary data if `true`, Encode as if it doesnt have binary data if `false` 292 | * @return {Socket} self 293 | * @api public 294 | */ 295 | 296 | Namespace.prototype.binary = function (binary) { 297 | this.flags.binary = binary; 298 | return this; 299 | }; 300 | -------------------------------------------------------------------------------- /node_modules/socket.io/lib/socket.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Emitter = require('events').EventEmitter; 7 | var parser = require('socket.io-parser'); 8 | var hasBin = require('has-binary2'); 9 | var url = require('url'); 10 | var debug = require('debug')('socket.io:socket'); 11 | 12 | /** 13 | * Module exports. 14 | */ 15 | 16 | module.exports = exports = Socket; 17 | 18 | /** 19 | * Blacklisted events. 20 | * 21 | * @api public 22 | */ 23 | 24 | exports.events = [ 25 | 'error', 26 | 'connect', 27 | 'disconnect', 28 | 'disconnecting', 29 | 'newListener', 30 | 'removeListener' 31 | ]; 32 | 33 | /** 34 | * Flags. 35 | * 36 | * @api private 37 | */ 38 | 39 | var flags = [ 40 | 'json', 41 | 'volatile', 42 | 'broadcast', 43 | 'local' 44 | ]; 45 | 46 | /** 47 | * `EventEmitter#emit` reference. 48 | */ 49 | 50 | var emit = Emitter.prototype.emit; 51 | 52 | /** 53 | * Interface to a `Client` for a given `Namespace`. 54 | * 55 | * @param {Namespace} nsp 56 | * @param {Client} client 57 | * @api public 58 | */ 59 | 60 | function Socket(nsp, client, query){ 61 | this.nsp = nsp; 62 | this.server = nsp.server; 63 | this.adapter = this.nsp.adapter; 64 | this.id = nsp.name !== '/' ? nsp.name + '#' + client.id : client.id; 65 | this.client = client; 66 | this.conn = client.conn; 67 | this.rooms = {}; 68 | this.acks = {}; 69 | this.connected = true; 70 | this.disconnected = false; 71 | this.handshake = this.buildHandshake(query); 72 | this.fns = []; 73 | this.flags = {}; 74 | this._rooms = []; 75 | } 76 | 77 | /** 78 | * Inherits from `EventEmitter`. 79 | */ 80 | 81 | Socket.prototype.__proto__ = Emitter.prototype; 82 | 83 | /** 84 | * Apply flags from `Socket`. 85 | */ 86 | 87 | flags.forEach(function(flag){ 88 | Object.defineProperty(Socket.prototype, flag, { 89 | get: function() { 90 | this.flags[flag] = true; 91 | return this; 92 | } 93 | }); 94 | }); 95 | 96 | /** 97 | * `request` engine.io shortcut. 98 | * 99 | * @api public 100 | */ 101 | 102 | Object.defineProperty(Socket.prototype, 'request', { 103 | get: function() { 104 | return this.conn.request; 105 | } 106 | }); 107 | 108 | /** 109 | * Builds the `handshake` BC object 110 | * 111 | * @api private 112 | */ 113 | 114 | Socket.prototype.buildHandshake = function(query){ 115 | var self = this; 116 | function buildQuery(){ 117 | var requestQuery = url.parse(self.request.url, true).query; 118 | //if socket-specific query exist, replace query strings in requestQuery 119 | return Object.assign({}, query, requestQuery); 120 | } 121 | return { 122 | headers: this.request.headers, 123 | time: (new Date) + '', 124 | address: this.conn.remoteAddress, 125 | xdomain: !!this.request.headers.origin, 126 | secure: !!this.request.connection.encrypted, 127 | issued: +(new Date), 128 | url: this.request.url, 129 | query: buildQuery() 130 | }; 131 | }; 132 | 133 | /** 134 | * Emits to this client. 135 | * 136 | * @return {Socket} self 137 | * @api public 138 | */ 139 | 140 | Socket.prototype.emit = function(ev){ 141 | if (~exports.events.indexOf(ev)) { 142 | emit.apply(this, arguments); 143 | return this; 144 | } 145 | 146 | var args = Array.prototype.slice.call(arguments); 147 | var packet = { 148 | type: (this.flags.binary !== undefined ? this.flags.binary : hasBin(args)) ? parser.BINARY_EVENT : parser.EVENT, 149 | data: args 150 | }; 151 | 152 | // access last argument to see if it's an ACK callback 153 | if (typeof args[args.length - 1] === 'function') { 154 | if (this._rooms.length || this.flags.broadcast) { 155 | throw new Error('Callbacks are not supported when broadcasting'); 156 | } 157 | 158 | debug('emitting packet with ack id %d', this.nsp.ids); 159 | this.acks[this.nsp.ids] = args.pop(); 160 | packet.id = this.nsp.ids++; 161 | } 162 | 163 | var rooms = this._rooms.slice(0); 164 | var flags = Object.assign({}, this.flags); 165 | 166 | // reset flags 167 | this._rooms = []; 168 | this.flags = {}; 169 | 170 | if (rooms.length || flags.broadcast) { 171 | this.adapter.broadcast(packet, { 172 | except: [this.id], 173 | rooms: rooms, 174 | flags: flags 175 | }); 176 | } else { 177 | // dispatch packet 178 | this.packet(packet, flags); 179 | } 180 | return this; 181 | }; 182 | 183 | /** 184 | * Targets a room when broadcasting. 185 | * 186 | * @param {String} name 187 | * @return {Socket} self 188 | * @api public 189 | */ 190 | 191 | Socket.prototype.to = 192 | Socket.prototype.in = function(name){ 193 | if (!~this._rooms.indexOf(name)) this._rooms.push(name); 194 | return this; 195 | }; 196 | 197 | /** 198 | * Sends a `message` event. 199 | * 200 | * @return {Socket} self 201 | * @api public 202 | */ 203 | 204 | Socket.prototype.send = 205 | Socket.prototype.write = function(){ 206 | var args = Array.prototype.slice.call(arguments); 207 | args.unshift('message'); 208 | this.emit.apply(this, args); 209 | return this; 210 | }; 211 | 212 | /** 213 | * Writes a packet. 214 | * 215 | * @param {Object} packet object 216 | * @param {Object} opts options 217 | * @api private 218 | */ 219 | 220 | Socket.prototype.packet = function(packet, opts){ 221 | packet.nsp = this.nsp.name; 222 | opts = opts || {}; 223 | opts.compress = false !== opts.compress; 224 | this.client.packet(packet, opts); 225 | }; 226 | 227 | /** 228 | * Joins a room. 229 | * 230 | * @param {String|Array} room or array of rooms 231 | * @param {Function} fn optional, callback 232 | * @return {Socket} self 233 | * @api private 234 | */ 235 | 236 | Socket.prototype.join = function(rooms, fn){ 237 | debug('joining room %s', rooms); 238 | var self = this; 239 | if (!Array.isArray(rooms)) { 240 | rooms = [rooms]; 241 | } 242 | rooms = rooms.filter(function (room) { 243 | return !self.rooms.hasOwnProperty(room); 244 | }); 245 | if (!rooms.length) { 246 | fn && fn(null); 247 | return this; 248 | } 249 | this.adapter.addAll(this.id, rooms, function(err){ 250 | if (err) return fn && fn(err); 251 | debug('joined room %s', rooms); 252 | rooms.forEach(function (room) { 253 | self.rooms[room] = room; 254 | }); 255 | fn && fn(null); 256 | }); 257 | return this; 258 | }; 259 | 260 | /** 261 | * Leaves a room. 262 | * 263 | * @param {String} room 264 | * @param {Function} fn optional, callback 265 | * @return {Socket} self 266 | * @api private 267 | */ 268 | 269 | Socket.prototype.leave = function(room, fn){ 270 | debug('leave room %s', room); 271 | var self = this; 272 | this.adapter.del(this.id, room, function(err){ 273 | if (err) return fn && fn(err); 274 | debug('left room %s', room); 275 | delete self.rooms[room]; 276 | fn && fn(null); 277 | }); 278 | return this; 279 | }; 280 | 281 | /** 282 | * Leave all rooms. 283 | * 284 | * @api private 285 | */ 286 | 287 | Socket.prototype.leaveAll = function(){ 288 | this.adapter.delAll(this.id); 289 | this.rooms = {}; 290 | }; 291 | 292 | /** 293 | * Called by `Namespace` upon successful 294 | * middleware execution (ie: authorization). 295 | * Socket is added to namespace array before 296 | * call to join, so adapters can access it. 297 | * 298 | * @api private 299 | */ 300 | 301 | Socket.prototype.onconnect = function(){ 302 | debug('socket connected - writing packet'); 303 | this.nsp.connected[this.id] = this; 304 | this.join(this.id); 305 | var skip = this.nsp.name === '/' && this.nsp.fns.length === 0; 306 | if (skip) { 307 | debug('packet already sent in initial handshake'); 308 | } else { 309 | this.packet({ type: parser.CONNECT }); 310 | } 311 | }; 312 | 313 | /** 314 | * Called with each packet. Called by `Client`. 315 | * 316 | * @param {Object} packet 317 | * @api private 318 | */ 319 | 320 | Socket.prototype.onpacket = function(packet){ 321 | debug('got packet %j', packet); 322 | switch (packet.type) { 323 | case parser.EVENT: 324 | this.onevent(packet); 325 | break; 326 | 327 | case parser.BINARY_EVENT: 328 | this.onevent(packet); 329 | break; 330 | 331 | case parser.ACK: 332 | this.onack(packet); 333 | break; 334 | 335 | case parser.BINARY_ACK: 336 | this.onack(packet); 337 | break; 338 | 339 | case parser.DISCONNECT: 340 | this.ondisconnect(); 341 | break; 342 | 343 | case parser.ERROR: 344 | this.onerror(new Error(packet.data)); 345 | } 346 | }; 347 | 348 | /** 349 | * Called upon event packet. 350 | * 351 | * @param {Object} packet object 352 | * @api private 353 | */ 354 | 355 | Socket.prototype.onevent = function(packet){ 356 | var args = packet.data || []; 357 | debug('emitting event %j', args); 358 | 359 | if (null != packet.id) { 360 | debug('attaching ack callback to event'); 361 | args.push(this.ack(packet.id)); 362 | } 363 | 364 | this.dispatch(args); 365 | }; 366 | 367 | /** 368 | * Produces an ack callback to emit with an event. 369 | * 370 | * @param {Number} id packet id 371 | * @api private 372 | */ 373 | 374 | Socket.prototype.ack = function(id){ 375 | var self = this; 376 | var sent = false; 377 | return function(){ 378 | // prevent double callbacks 379 | if (sent) return; 380 | var args = Array.prototype.slice.call(arguments); 381 | debug('sending ack %j', args); 382 | 383 | self.packet({ 384 | id: id, 385 | type: hasBin(args) ? parser.BINARY_ACK : parser.ACK, 386 | data: args 387 | }); 388 | 389 | sent = true; 390 | }; 391 | }; 392 | 393 | /** 394 | * Called upon ack packet. 395 | * 396 | * @api private 397 | */ 398 | 399 | Socket.prototype.onack = function(packet){ 400 | var ack = this.acks[packet.id]; 401 | if ('function' == typeof ack) { 402 | debug('calling ack %s with %j', packet.id, packet.data); 403 | ack.apply(this, packet.data); 404 | delete this.acks[packet.id]; 405 | } else { 406 | debug('bad ack %s', packet.id); 407 | } 408 | }; 409 | 410 | /** 411 | * Called upon client disconnect packet. 412 | * 413 | * @api private 414 | */ 415 | 416 | Socket.prototype.ondisconnect = function(){ 417 | debug('got disconnect packet'); 418 | this.onclose('client namespace disconnect'); 419 | }; 420 | 421 | /** 422 | * Handles a client error. 423 | * 424 | * @api private 425 | */ 426 | 427 | Socket.prototype.onerror = function(err){ 428 | if (this.listeners('error').length) { 429 | this.emit('error', err); 430 | } else { 431 | console.error('Missing error handler on `socket`.'); 432 | console.error(err.stack); 433 | } 434 | }; 435 | 436 | /** 437 | * Called upon closing. Called by `Client`. 438 | * 439 | * @param {String} reason 440 | * @throw {Error} optional error object 441 | * @api private 442 | */ 443 | 444 | Socket.prototype.onclose = function(reason){ 445 | if (!this.connected) return this; 446 | debug('closing socket - reason %s', reason); 447 | this.emit('disconnecting', reason); 448 | this.leaveAll(); 449 | this.nsp.remove(this); 450 | this.client.remove(this); 451 | this.connected = false; 452 | this.disconnected = true; 453 | delete this.nsp.connected[this.id]; 454 | this.emit('disconnect', reason); 455 | }; 456 | 457 | /** 458 | * Produces an `error` packet. 459 | * 460 | * @param {Object} err error object 461 | * @api private 462 | */ 463 | 464 | Socket.prototype.error = function(err){ 465 | this.packet({ type: parser.ERROR, data: err }); 466 | }; 467 | 468 | /** 469 | * Disconnects this client. 470 | * 471 | * @param {Boolean} close if `true`, closes the underlying connection 472 | * @return {Socket} self 473 | * @api public 474 | */ 475 | 476 | Socket.prototype.disconnect = function(close){ 477 | if (!this.connected) return this; 478 | if (close) { 479 | this.client.disconnect(); 480 | } else { 481 | this.packet({ type: parser.DISCONNECT }); 482 | this.onclose('server namespace disconnect'); 483 | } 484 | return this; 485 | }; 486 | 487 | /** 488 | * Sets the compress flag. 489 | * 490 | * @param {Boolean} compress if `true`, compresses the sending data 491 | * @return {Socket} self 492 | * @api public 493 | */ 494 | 495 | Socket.prototype.compress = function(compress){ 496 | this.flags.compress = compress; 497 | return this; 498 | }; 499 | 500 | /** 501 | * Sets the binary flag 502 | * 503 | * @param {Boolean} Encode as if it has binary data if `true`, Encode as if it doesnt have binary data if `false` 504 | * @return {Socket} self 505 | * @api public 506 | */ 507 | 508 | Socket.prototype.binary = function (binary) { 509 | this.flags.binary = binary; 510 | return this; 511 | }; 512 | 513 | /** 514 | * Dispatch incoming event to socket listeners. 515 | * 516 | * @param {Array} event that will get emitted 517 | * @api private 518 | */ 519 | 520 | Socket.prototype.dispatch = function(event){ 521 | debug('dispatching an event %j', event); 522 | var self = this; 523 | function dispatchSocket(err) { 524 | process.nextTick(function(){ 525 | if (err) { 526 | return self.error(err.data || err.message); 527 | } 528 | emit.apply(self, event); 529 | }); 530 | } 531 | this.run(event, dispatchSocket); 532 | }; 533 | 534 | /** 535 | * Sets up socket middleware. 536 | * 537 | * @param {Function} middleware function (event, next) 538 | * @return {Socket} self 539 | * @api public 540 | */ 541 | 542 | Socket.prototype.use = function(fn){ 543 | this.fns.push(fn); 544 | return this; 545 | }; 546 | 547 | /** 548 | * Executes the middleware for an incoming event. 549 | * 550 | * @param {Array} event that will get emitted 551 | * @param {Function} last fn call in the middleware 552 | * @api private 553 | */ 554 | Socket.prototype.run = function(event, fn){ 555 | var fns = this.fns.slice(0); 556 | if (!fns.length) return fn(null); 557 | 558 | function run(i){ 559 | fns[i](event, function(err){ 560 | // upon error, short-circuit 561 | if (err) return fn(err); 562 | 563 | // if no middleware left, summon callback 564 | if (!fns[i + 1]) return fn(null); 565 | 566 | // go on to next 567 | run(i + 1); 568 | }); 569 | } 570 | 571 | run(0); 572 | }; 573 | -------------------------------------------------------------------------------- /node_modules/socket.io/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socket.io", 3 | "version": "2.3.0", 4 | "description": "node.js realtime framework server", 5 | "keywords": [ 6 | "realtime", 7 | "framework", 8 | "websocket", 9 | "tcp", 10 | "events", 11 | "socket", 12 | "io" 13 | ], 14 | "main": "./lib/index", 15 | "files": [ 16 | "lib/" 17 | ], 18 | "license": "MIT", 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/socketio/socket.io" 22 | }, 23 | "scripts": { 24 | "test": "nyc mocha --reporter spec --slow 200 --bail --timeout 10000 test/socket.io.js" 25 | }, 26 | "dependencies": { 27 | "debug": "~4.1.0", 28 | "engine.io": "~3.4.0", 29 | "has-binary2": "~1.0.2", 30 | "socket.io-adapter": "~1.1.0", 31 | "socket.io-client": "2.3.0", 32 | "socket.io-parser": "~3.4.0" 33 | }, 34 | "devDependencies": { 35 | "expect.js": "0.3.1", 36 | "mocha": "^3.5.3", 37 | "nyc": "^11.2.1", 38 | "superagent": "^3.8.2", 39 | "supertest": "^3.0.0" 40 | }, 41 | "contributors": [ 42 | { 43 | "name": "Guillermo Rauch", 44 | "email": "rauchg@gmail.com" 45 | }, 46 | { 47 | "name": "Arnout Kazemier", 48 | "email": "info@3rd-eden.com" 49 | }, 50 | { 51 | "name": "Vladimir Dronnikov", 52 | "email": "dronnikov@gmail.com" 53 | }, 54 | { 55 | "name": "Einar Otto Stangvik", 56 | "email": "einaros@gmail.com" 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chess", 3 | "version" : "0.0.1", 4 | "dependencies":{ 5 | "socket.io" : "2.3.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /scripts/Analyzer.js: -------------------------------------------------------------------------------- 1 | /* 2 | singleton class for analyzing state of any array of piece objects to see each of their optional moves 3 | */ 4 | 5 | CHESSAPP.Analyzer ={ 6 | //information regarding whether the squares next to the kings are being attacked, which makes castling impossible 7 | castlingInfo: { 8 | W: { 9 | left: false, 10 | right: false 11 | }, 12 | B: { 13 | left: false, 14 | right: false 15 | } 16 | }, 17 | 18 | /* 19 | @return an array of option arrays all corresponding to each piece index 20 | */ 21 | makeAllOptions : function(settings){ 22 | var stg = { 23 | pieces: null 24 | }; 25 | 26 | //reset the castling info 27 | this.castlingInfo.B.left = false; 28 | this.castlingInfo.B.right = false; 29 | this.castlingInfo.W.left = false; 30 | this.castlingInfo.W.right = false; 31 | 32 | CHESSAPP.utils.extend(stg, settings); 33 | var pieces = stg.pieces; 34 | var max = pieces.length; 35 | var resp = { 36 | kingInCheck : false, 37 | allOptions : [] 38 | }; 39 | var r, 40 | whiteKingIndex,//temps for later 41 | blackKingIndex; 42 | for(var i = 0; i < pieces.length; i++){ 43 | if(pieces[i] && pieces[i].pieceType == "king"){ 44 | if(pieces[i].color == "W"){ 45 | whiteKingIndex = i; 46 | } 47 | else{ 48 | blackKingIndex = i; 49 | } 50 | } 51 | if(pieces[i] && CHESSAPP.GamePlay.getTurn() == pieces[i].color){//pieces can be null if taken 52 | pieces[i].justMoved = false; 53 | } 54 | r = this.getOptions({pieces: pieces, piece: pieces[i], checkTest : false}); 55 | if(r && r.checkDetected){ 56 | if(r.checkDetected){ 57 | resp.kingInCheck = r.checkDetected; 58 | } 59 | } 60 | 61 | resp.allOptions.push(r.pieceOptions); 62 | } 63 | 64 | //now check for castling here. The reason being that we need to know whether or not the two spaces 65 | //on either side are being attacked. This is checked one by one for each piece, so the castling needs to be checked at the end 66 | 67 | if(resp.kingInCheck != "W"){ 68 | //you cannot castle if you're in check 69 | r = this.getOptions({pieces: pieces, piece: pieces[whiteKingIndex], checkTest: false, castleTest: true}); 70 | if(r && r.checkDetected){ 71 | if(r.checkDetected){ 72 | resp.kingInCheck = r.checkDetected; 73 | } 74 | } 75 | console.log("HERE : " , r); 76 | resp.allOptions[whiteKingIndex] = resp.allOptions[whiteKingIndex].concat(r.pieceOptions); 77 | } 78 | if(resp.kingInCheck != "B"){ 79 | resp.allOptions.push(r.pieceOptions); 80 | r = this.getOptions({pieces: pieces, piece: pieces[blackKingIndex], checkTest: false, castleTest: true}); 81 | if(r && r.checkDetected){ 82 | if(r.checkDetected){ 83 | resp.kingInCheck = r.checkDetected; 84 | } 85 | } 86 | resp.allOptions[blackKingIndex] = resp.allOptions[blackKingIndex].concat(r.pieceOptions); 87 | } 88 | 89 | resp.allOptions.push(r.pieceOptions); 90 | return resp; 91 | }, 92 | /* 93 | returns true or false if the king of the specified color (in the parameter object) is in check 94 | */ 95 | checkTest : function(settings){ 96 | var stg = { 97 | pieces: null, 98 | color: 'W' 99 | }; 100 | CHESSAPP.utils.extend(stg, settings); 101 | var pieces = stg.pieces, 102 | color = stg.color; 103 | 104 | for(var i = 0; i < pieces.length; i++){ 105 | var r = this.getOptions({pieces: pieces, piece: pieces[i], checkTest : color}); 106 | if(r && r.checkDetected == color){ 107 | //console.log("Check detected"); 108 | return true; 109 | } 110 | 111 | } 112 | return false; 113 | }, 114 | /* 115 | gets the options for a single piece in the array pieces 116 | @return 117 | */ 118 | getOptions : function(settings){ 119 | var stg = { 120 | pieces: null, 121 | piece: null, 122 | checkTest : false, 123 | castleTest: false 124 | }; 125 | CHESSAPP.utils.extend(stg, settings); 126 | 127 | var piece = stg.piece, 128 | pieces = stg.pieces; 129 | 130 | var resp = { 131 | checkDetected : false, 132 | pieceOptions: null 133 | }; 134 | if(!piece){ 135 | return resp; 136 | } 137 | var pieceOptions = [], 138 | curx = parseInt(piece.x), 139 | cury = parseInt(piece.y), 140 | color = piece.color, 141 | type = piece.pieceType; 142 | 143 | var checkFound = false; 144 | /* shortcut method for getting options of a single piece at a single square */ 145 | var mk = function(x,y,m,a,s){ 146 | var r = CHESSAPP.Analyzer.makeOption({pieces: pieces, x: x, y: y, piece: piece, canMove: m, canAttack: a, checkTest: stg.checkTest, special: s}); 147 | if(r.checkDetected){ 148 | resp.checkDetected = r.checkDetected; 149 | } 150 | if(r.valid){ 151 | if(stg.castleTest){ 152 | console.log("Adding castle", r); 153 | } 154 | if(!stg.checkTest){ 155 | if(piece.color == "B"){ 156 | if((x == 3 || x == 2) && y == 7){ 157 | CHESSAPP.Analyzer.castlingInfo.W.left = true; 158 | } 159 | else if((x == 5 || x == 6) && y == 7){ 160 | CHESSAPP.Analyzer.castlingInfo.W.right = true; 161 | } 162 | } 163 | else if(piece.color == "W"){ 164 | if((x == 3 || x == 2)&& y == 0){ 165 | CHESSAPP.Analyzer.castlingInfo.B.left = true; 166 | } 167 | else if((x == 5 || x == 6) && y == 0){ 168 | CHESSAPP.Analyzer.castlingInfo.B.right = true; 169 | } 170 | } 171 | } 172 | pieceOptions.push(r); 173 | } 174 | return r.canMovePast; 175 | }; 176 | 177 | var flip = (color == 'B') ? 1 : -1; 178 | 179 | //The following checks the possible moves based on the piece type (pawns can move forward but attack diagonally etc.) 180 | switch(type){ 181 | case "pawn": 182 | 183 | var tmp = mk(curx,cury + 1 * flip, true, false); 184 | if(piece.numOfMoves == 0 && tmp){ 185 | //if the pawn hasn't yet move, add the second space available 186 | mk(curx, cury + 2 * flip, true, false); 187 | } 188 | //check for en passant on both sides 189 | var rp = CHESSAPP.Analyzer.pieceExists({pieces: pieces, x: (curx + 1), y: cury}); 190 | if(rp != null && rp.color != piece.color && rp.pieceType == "pawn" && rp.justMoved && rp.numOfMoves == 1 && (rp.y == 3 || rp.y == 4)){ 191 | var special = { 192 | type: "en", 193 | enx : curx + 1, 194 | eny : cury 195 | }; 196 | mk(curx+1, cury + 1 * flip, true, true, special); 197 | } 198 | //check for en passant on both sides 199 | rp = CHESSAPP.Analyzer.pieceExists({pieces: pieces, x: (curx - 1), y: cury}); 200 | if(rp != null && rp.color != piece.color && rp.pieceType == "pawn" && rp.justMoved && rp.numOfMoves == 1 && (rp.y == 3 || rp.y == 4)){ 201 | var special = { 202 | type: "en", 203 | enx : curx - 1, 204 | eny : cury 205 | }; 206 | mk(curx - 1, cury + 1 * flip, true, true, special); 207 | } 208 | 209 | //check if pieces in either attack location 210 | if(CHESSAPP.Analyzer.pieceExists({pieces: pieces, x: (curx + 1), y: (cury + 1 * flip)})){ 211 | mk(curx + 1,cury + 1 * flip, false, true); 212 | } 213 | if(CHESSAPP.Analyzer.pieceExists({pieces: pieces, x: (curx - 1), y: (cury + 1 * flip)})){ 214 | mk(curx - 1,cury + 1 * flip, false, true); 215 | } 216 | break; 217 | case "king": 218 | if(stg.castleTest){ 219 | //check for castling... 220 | 221 | var leftCastle = true, 222 | rightCastle = true; 223 | 224 | //king has moved 225 | if(piece.numOfMoves > 0 || CHESSAPP.GamePlay.kingInCheck == piece.color){ 226 | leftCastle = false; 227 | rightCastle = false; 228 | } 229 | else{ 230 | //check left side 231 | 232 | //check if those spaces are being attacked 233 | if(this.castlingInfo[piece.color].left){ 234 | //piece is attacking left side, this is invalid 235 | leftCastle = false; 236 | } 237 | else{ 238 | //check that spaces are empty and find the rooks on both sides 239 | var leftP;//will hold rook 240 | for(var i = 1; i <= 4; i++){ 241 | leftP = CHESSAPP.Analyzer.pieceExists({pieces: pieces, x: (curx - i), y : cury}); 242 | if(i < 4 && leftP != null){ 243 | //not possible, piece in the way 244 | leftCastle = false; 245 | } 246 | } 247 | //leftP should hold the left most piece 248 | if(leftP != null && leftP.pieceType == "rook" && leftP.color == piece.color && leftP.numOfMoves == 0){ 249 | //valid 250 | } 251 | else{ 252 | leftCastle = false; 253 | } 254 | } 255 | //check right side 256 | //check if those spaces are being attacked 257 | if(this.castlingInfo[piece.color].right){ 258 | //piece is attacking left side, this is invalid 259 | rightCastle = false; 260 | } 261 | else{ 262 | //check that spaces are empty and find the rooks on both sides 263 | var rightP;//will hold rook 264 | for(var i = 1; i <= 3; i++){ 265 | rightP = CHESSAPP.Analyzer.pieceExists({pieces: pieces, x: (curx + i), y : cury}); 266 | if(i < 3 && rightP != null){ 267 | //not possible, piece in the way 268 | rightCastle = false; 269 | } 270 | } 271 | //rightP should hold the right most piece 272 | if(rightP != null && rightP.pieceType == "rook" && rightP.color == piece.color && rightP.numOfMoves == 0){ 273 | //valid 274 | } 275 | else{ 276 | rightCastle = false; 277 | } 278 | } 279 | 280 | } 281 | if(leftCastle){ 282 | //should be valid 283 | //now make that move 284 | var special = { 285 | type:"castle", 286 | side: "left", 287 | rookx : curx - 4, 288 | rooky : cury, 289 | rooktox : curx - 1, 290 | rooktoy : cury 291 | }; 292 | mk(curx - 2, cury, true, false, special); 293 | } 294 | if(rightCastle){ 295 | //should be valid 296 | //now make that move 297 | var special = { 298 | type:"castle", 299 | side: "right", 300 | rookx : curx + 3, 301 | rooky : cury, 302 | rooktox : curx + 1, 303 | rooktoy : cury 304 | }; 305 | mk(curx + 2, cury, true, false, special); 306 | } 307 | if(leftCastle && !stg.checkTest){ 308 | console.log(leftCastle + " for castling color " + piece.color + " left"); 309 | } 310 | else if(!leftCastle && !stg.checkTest){ 311 | console.log(leftCastle + " for castling color " + piece.color + " left"); 312 | } 313 | 314 | if(rightCastle && !stg.checkTest){ 315 | console.log(rightCastle + " for castling color " + piece.color + " right"); 316 | } 317 | else if(!rightCastle && !stg.checkTest){ 318 | console.log(rightCastle + " for castling color " + piece.color + " right"); 319 | } 320 | } 321 | else{ 322 | //normal checks 323 | mk(curx - 1, cury + 1, true, true); 324 | mk(curx - 1, cury, true, true); 325 | mk(curx - 1, cury - 1, true, true); 326 | mk(curx + 1, cury + 1, true, true); 327 | mk(curx + 1, cury, true, true); 328 | mk(curx + 1, cury - 1, true, true); 329 | mk(curx, cury + 1, true, true); 330 | mk(curx, cury - 1, true, true); 331 | } 332 | 333 | break; 334 | case "knight": 335 | mk(curx - 1, cury + 2, true, true); 336 | mk(curx - 1, cury - 2, true, true); 337 | mk(curx + 1, cury + 2, true, true); 338 | mk(curx + 1, cury - 2, true, true); 339 | mk(curx - 2, cury + 1, true, true); 340 | mk(curx - 2, cury - 1, true, true); 341 | mk(curx + 2, cury + 1, true, true); 342 | mk(curx + 2, cury - 1, true, true); 343 | break; 344 | case "bishop": 345 | case "rook": 346 | case "queen": 347 | //this is only horizontal and vertical (applies only to bishop and rook) 348 | if(type != "bishop"){ 349 | //horizontal 350 | for(var i = curx - 1; i >= 0; i--){ 351 | if(!mk(i,cury, true, true)){ 352 | break;} 353 | } 354 | for(var j = curx + 1; j <= 7; j++){ 355 | if(!mk(j,cury,true, true)){ 356 | break; 357 | } 358 | } 359 | //vertical 360 | for(var k = cury - 1; k >= 0; k--){ 361 | if(!mk(curx,k,true, true)){ 362 | break; 363 | } 364 | } 365 | for(var l = cury + 1; l <= 7; l++){ 366 | if(!mk(curx,l,true, true)){ 367 | break; 368 | } 369 | } 370 | } 371 | //applies only to queen and bishop 372 | if(type != "rook"){ 373 | //top left 374 | for(var i = 1; i <= Math.min(curx, cury); i++){ 375 | if(!mk(curx - i,cury - i, true, true)){ 376 | break;} 377 | } 378 | //bottom left 379 | for(var i = 1; i <= 7 - Math.max(curx, cury); i++){ 380 | if(!mk(curx + i,cury + i, true, true)){ 381 | break;} 382 | } 383 | //top right 384 | for(var i = 1; i <= Math.min(7 - curx, cury); i++){ 385 | if(!mk(curx + i,cury - i, true, true)){ 386 | break;} 387 | } 388 | //bottom right 389 | for(var i = 1; i <= Math.min(curx, 7 - cury); i++){ 390 | if(!mk(curx - i,cury + i, true, true)){ 391 | break;} 392 | } 393 | } 394 | break; 395 | } 396 | 397 | if(stg.checkTest){ 398 | //this is only a check test, we don't need the actual options 399 | return resp; 400 | } 401 | resp.pieceOptions = pieceOptions; 402 | return resp; 403 | }, 404 | withinBounds : function(x,y){ 405 | return ((x >= 0 && x <= 7) && (y >= 0 && y <= 7)); 406 | }, 407 | /* 408 | * When making an option for a piece, it checks if making that move would leave the players king in check. 409 | * Therefore, for each option a piece has, every permutation of moves on the opponents team must be checked to see 410 | * if it would leave that players king in check. If it would, then it is an invalid move. 411 | * 412 | * This function does the work of a virtual board checking if the potential new state of the board would leave the player's king in check 413 | */ 414 | makeOption : function(settings){ 415 | var stg = { 416 | pieces: null,//set of pieces 417 | piece: null,//current piece 418 | canAttack: true, 419 | canMove: true, 420 | checkTest: false, 421 | x: -1, 422 | y: -1, 423 | special: null 424 | }; 425 | CHESSAPP.utils.extend(stg, settings); 426 | var x = stg.x, y = stg.y, piece = stg.piece, pieces = stg.pieces, special = stg.special; 427 | 428 | var resp = { 429 | x: x, 430 | y: y, 431 | valid : true, //same as saying (attackable || movable) says whether piece can actually move (with or without attacking) 432 | attackable : false, 433 | movable: false, 434 | canMovePast : true, 435 | checkDetected : false, 436 | special: special 437 | }; 438 | 439 | if(!this.withinBounds(x,y)){ 440 | resp.valid = false; 441 | return resp; 442 | } 443 | var pieceExists = null;//piece to be attackec 444 | if(special == null){ 445 | //normal move 446 | pieceExists = this.pieceExists({pieces: pieces, x : x, y : y, checkTest: stg.checkTest}); 447 | } 448 | else if(special.type == "en"){ 449 | //en passant 450 | 451 | pieceExists = this.pieceExists({pieces: pieces, x : special.enx, y : special.eny, checkTest: stg.checkTest}); 452 | if(!stg.checkTest){ 453 | console.log("Checking en passant piece"); 454 | console.log(pieceExists); 455 | } 456 | } 457 | else if(special.type == "castle"){ 458 | //the checkTest is not necessary because checks have already been made for if the squares to this castling side are 459 | //being attacked, and moving cannot introduce any new attacks since this is on the edge of the board 460 | //therefore nothing to do here :P 461 | resp.movable = true; 462 | return resp; 463 | } 464 | if(pieceExists){ 465 | //check if this is a valid location for possible option 466 | //pieceExists should refer to the actual piece 467 | if(stg.piece.color == pieceExists.color){ 468 | //ignore same color 469 | resp.valid = false; 470 | resp.canMovePast = false; //it cannot move past a piece of its own color 471 | } 472 | else{ 473 | if(stg.canAttack){ 474 | resp.attackable = true; 475 | if(pieceExists.pieceType == "king") 476 | { 477 | //if it is a check test, only set it equal if the color is equal to the color being looked for 478 | if((stg.checkTest && stg.checkTest == pieceExists.color) || !stg.checkTest){ 479 | resp.checkDetected = pieceExists.color; 480 | return resp; //return early, because more piece checking is unnecessary 481 | } 482 | else{ 483 | resp.checkDetected = pieceExists.color; 484 | } 485 | } 486 | 487 | resp.canMovePast = false;//can't move past it if it's attacking it 488 | } 489 | else{ 490 | //it will never be able to move on an occupied space if it can't attack it 491 | resp.valid = false; 492 | resp.canMovePast = false; //it cannot move past a piece it can't attack 493 | } 494 | } 495 | } 496 | if(stg.canMove && resp.valid){ 497 | resp.movable = true; 498 | } 499 | 500 | resp.valid = resp.attackable || resp.movable; 501 | 502 | if(!stg.checkTest && resp.valid){ 503 | 504 | var pieceObj = { 505 | pieceType: piece.pieceType, 506 | color: piece.color, 507 | x: x, 508 | y: y 509 | }; 510 | //if this is not a check test, check if this possible move would leave the own king in check 511 | //final check, see if this would leave the king of the own color in check 512 | var pieceOverrides = 513 | [ 514 | { 515 | pieceIndex: pieces.indexOf(piece), 516 | val: pieceObj 517 | } 518 | ]; 519 | 520 | if(resp.attackable){ 521 | //add override 522 | pieceOverrides.push({ 523 | pieceIndex: pieces.indexOf(pieceExists), 524 | val: null 525 | }); 526 | } 527 | 528 | 529 | var newPieces = this.copyAndReplace({pieces: pieces, overrides: pieceOverrides}); 530 | //console.log(newPieces); 531 | 532 | if(this.checkTest({pieces: newPieces, color: piece.color})){ 533 | //invalid move because it leaves the king in check 534 | // console.log("YOUR ARGUMENT IS INVALID"); 535 | resp.valid = false; 536 | } 537 | 538 | 539 | } 540 | 541 | return resp; 542 | }, 543 | 544 | pieceExists : function(settings){ 545 | var stg = { 546 | checkTest: false, 547 | pieces: null, 548 | x: -1, 549 | y: -1 550 | }; 551 | 552 | CHESSAPP.utils.extend(stg, settings); 553 | 554 | var pieces = stg.pieces, 555 | x = stg.x, 556 | y = stg.y; 557 | if(!this.withinBounds(x,y)){ 558 | return null; 559 | } 560 | for(var i = 0; i < pieces.length; i++){ 561 | if(pieces[i]){ 562 | if(pieces[i].x == x && pieces[i].y == y){ 563 | return pieces[i]; 564 | } 565 | } 566 | } 567 | return null; 568 | }, 569 | 570 | copyAndReplace : function(settings){ 571 | var stg = { 572 | pieces: null, 573 | overrides: null 574 | }, 575 | newArray, 576 | max, 577 | max_o; 578 | 579 | CHESSAPP.utils.extend(stg, settings); 580 | 581 | max = stg.pieces.length; 582 | max_o = stg.overrides.length; 583 | 584 | newArray = new Array(max); 585 | for(var i = 0; i < max; i++){ 586 | newArray[i] = CHESSAPP.utils.shallowCopy(stg.pieces[i]); 587 | } 588 | for(var j = 0; j < max_o; j++){ 589 | var index = stg.overrides[j].pieceIndex; 590 | newArray[index] = null; 591 | newArray[index] = stg.overrides[j].val; 592 | } 593 | 594 | 595 | 596 | return newArray; 597 | } 598 | }; 599 | -------------------------------------------------------------------------------- /scripts/GamePlay.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This class takes care of the setup of the pieces and is the bridge between the ui and logic 3 | */ 4 | CHESSAPP.GamePlay = (function(){ 5 | var that = {}, 6 | pieceGettingPromoted = null; 7 | 8 | that.pieces = []; 9 | that.cells; 10 | that.moveList = []; 11 | 12 | 13 | var _settings;//private variable which stores information about the player and state of the game 14 | 15 | var options = [],//array of options for every piece on actual board 16 | overrides = {},//object with keys of indexes corresponding to actual pieces, and values of their theoretical location 17 | selectedPieceIndex = -1; 18 | 19 | var toFile = function(num){ 20 | //returns letter A-H since A is number 65 in unicode 21 | console.log(65+num); 22 | return String.fromCharCode(96+parseInt(num)); 23 | }; 24 | 25 | 26 | var toAbbr = function(pieceType){ 27 | switch(pieceType){ 28 | case "pawn": 29 | return ""; 30 | break; 31 | case "queen": 32 | return "Q"; 33 | break; 34 | case "king": 35 | return "K"; 36 | break; 37 | case "bishop": 38 | return "B"; 39 | break; 40 | case "rook": 41 | return "R"; 42 | break; 43 | case "knight": 44 | return "N"; 45 | break; 46 | } 47 | }; 48 | 49 | that.getTurn = function(){ 50 | return _settings.turn; 51 | } 52 | /* 53 | * adds the move specified to the public moveList array, and updates the UI with the new move 54 | * @param move 55 | * an object that should have the following data: 56 | * { 57 | fromX: 58 | toX: 59 | toY: 60 | pieceType: 61 | killed: 62 | promoted: 63 | } 64 | */ 65 | that.addToMoveList = function(move){ 66 | var tos = ""; 67 | 68 | that.moveList.push(move); 69 | 70 | if(move.promotion){ 71 | console.log("HERE"); 72 | tos += toFile(parseInt(move.fromX)+1); 73 | tos += (8 - (parseInt(move.toY))); 74 | tos += "="; 75 | tos += toAbbr(move.pieceType);//was recently promoted to the type it is at now 76 | } 77 | else{ 78 | tos = toAbbr(move.pieceType); 79 | 80 | if(move.killed){ 81 | if(tos == ""){ 82 | //add pawn file 83 | tos += toFile(parseInt(move.fromX)+1); 84 | } 85 | tos += "x"; 86 | } 87 | 88 | tos += toFile(parseInt(move.toX)+1); 89 | tos += (8 - (parseInt(move.toY))); 90 | } 91 | CHESSAPP.ui.addMove(tos); 92 | console.log("Move notation: " + tos); 93 | }; 94 | that.statusUpdate = function(stg){ 95 | CHESSAPP.ui.statusUpdate(stg); 96 | } 97 | that.setOnlineColor = function(color){ 98 | if(color == 'W' || color == 'B'){ 99 | _settings.onlineColor = color; 100 | } 101 | }; 102 | that.sendMove = function(move){ 103 | if(_settings.online && move){ 104 | //if this is online play, let the other player know 105 | CHESSAPP.onlinePlay.sendMove(move); 106 | } 107 | }; 108 | 109 | that.switchTurn = function(){ 110 | if(_settings.turn == "W"){ 111 | _settings.turn = "B"; 112 | }else{ 113 | _settings.turn = "W"; 114 | } 115 | }; 116 | 117 | that.pieceClicked = function(piece){ 118 | var color = piece.color; 119 | //if the color does not match the current one playing, exit this function 120 | if(color != _settings.turn){return;} 121 | //if this is an online game, ignore clicks if the local color does not match the turn 122 | if(_settings.online && (_settings.onlineColor != _settings.turn)){return;} 123 | 124 | that.clearAllOptionStyles(); 125 | selectedPieceIndex = that.pieces.indexOf(piece); 126 | 127 | var pieceOptions = options[selectedPieceIndex]; 128 | 129 | for(var i = 0; i < pieceOptions.length; i++){ 130 | var opt = pieceOptions[i]; 131 | CHESSAPP.ui.addOptionStyles(that.cells[opt.x][opt.y], opt); 132 | } 133 | 134 | //clear all option classes (remove movable, attackable, and selected classes) 135 | // that.clearAllOptionStyles(); 136 | 137 | }; 138 | 139 | that.cellClicked = function(x,y){ 140 | 141 | var cell = that.cells[x][y]; 142 | if(selectedPieceIndex != -1){ 143 | var piece = that.pieces[selectedPieceIndex]; 144 | var opt = that.isOption(piece, cell); 145 | if(opt){ 146 | var moveOptions = { 147 | piece: piece, 148 | x: x, 149 | y: y, 150 | local: true, 151 | special: opt.special 152 | } 153 | that.movePieceTo(moveOptions); 154 | } 155 | } 156 | }; 157 | 158 | that.isOption = function(piece, cell){ 159 | var index = that.pieces.indexOf(piece); 160 | var pieceOptions = options[index], 161 | cellX = cell.x, 162 | cellY = cell.y; 163 | 164 | for(var i =0; i < pieceOptions.length; i++){ 165 | if(pieceOptions[i].x == cellX && pieceOptions[i].y == cellY){ 166 | return pieceOptions[i]; 167 | } 168 | } 169 | return false; 170 | }; 171 | 172 | 173 | 174 | 175 | that.inCheck = function(overrides){ 176 | var inCheck = false; 177 | for(var i = 0; i < that.pieces.length; i++){ 178 | that.getOptions(that.pieces[i], null); 179 | } 180 | return inCheck; 181 | }; 182 | 183 | 184 | that.init = function(userSettings){ 185 | _settings = { 186 | containerID : "chessboard", 187 | online: false, 188 | preferredColor: false, 189 | turn : "W", 190 | onlineColor : false,//this is the color if the player is playing online 191 | locked: false //says whether user can move or not 192 | }; 193 | 194 | //override default settings with user settings 195 | CHESSAPP.utils.extend(_settings, userSettings); 196 | var container = document.getElementById(_settings['containerID']); 197 | if(container == null){ 198 | console.log("container element not found with id: " + _settings['containerID']); 199 | return false; 200 | } 201 | 202 | /* initialize the user interface */ 203 | var p = { 204 | container: container, 205 | online: _settings.online 206 | }; 207 | 208 | that.cells = CHESSAPP.ui.init(p); 209 | //get initial setting information from user 210 | that.lock(); 211 | //wait for user to pick a color and online/offline play 212 | CHESSAPP.ui.onInitialChoice(function(pref){ 213 | console.log(pref); 214 | if(pref.hasOwnProperty("color")){ 215 | _settings.preferredColor = pref.color; 216 | } 217 | if(pref.hasOwnProperty("online")){ 218 | _settings.online = pref.online; 219 | } 220 | console.log(_settings); 221 | //now check if local or online connect 222 | if(_settings.online){ 223 | console.log("connecting..."); 224 | CHESSAPP.onlinePlay.connect(_settings, function(){ 225 | that.setUpBoard.apply(that); 226 | }); 227 | } 228 | else{ 229 | CHESSAPP.ui.statusUpdate({type: "fb", msg: "Playing locally"}); 230 | that.setUpBoard(); 231 | } 232 | }); 233 | 234 | }; 235 | 236 | /* 237 | *this funciton locks out certain features until unlocked 238 | *TODO implement 239 | * 240 | */ 241 | that.lock = function(stg){ 242 | 243 | }; 244 | 245 | that.setUpBoard = function(){ 246 | if(that.pieces){ 247 | //reset pieces 248 | delete that.pieces; 249 | } 250 | //create pieces 251 | that.pieces = [ 252 | { 253 | x: 0, 254 | y: 0, 255 | color: 'B', 256 | pieceType: "rook" 257 | }, 258 | { 259 | x: 0, 260 | y: 7, 261 | color: 'W', 262 | pieceType: "rook" 263 | }, 264 | { 265 | x: 7, 266 | y: 0, 267 | color: 'B', 268 | pieceType: "rook" 269 | }, 270 | { 271 | x: 7, 272 | y: 7, 273 | color: 'W', 274 | pieceType: "rook" 275 | }, 276 | { 277 | x: 4, 278 | y: 7, 279 | color: 'W', 280 | pieceType: "king" 281 | }, 282 | { 283 | x: 4, 284 | y: 0, 285 | color: 'B', 286 | pieceType: "king" 287 | }, 288 | { 289 | x: 6, 290 | y: 0, 291 | color: 'B', 292 | pieceType: "knight" 293 | }, 294 | { 295 | x: 1, 296 | y: 0, 297 | color: 'B', 298 | pieceType: "knight" 299 | }, 300 | { 301 | x: 6, 302 | y: 7, 303 | color: 'W', 304 | pieceType: "knight" 305 | }, 306 | { 307 | x: 1, 308 | y: 7, 309 | color: 'W', 310 | pieceType: "knight" 311 | }, 312 | { 313 | x: 5, 314 | y: 0, 315 | color: 'B', 316 | pieceType: "bishop" 317 | }, 318 | { 319 | x: 2, 320 | y: 0, 321 | color: 'B', 322 | pieceType: "bishop" 323 | }, 324 | { 325 | x: 5, 326 | y: 7, 327 | color: 'W', 328 | pieceType: "bishop" 329 | }, 330 | { 331 | x: 2, 332 | y: 7, 333 | color: 'W', 334 | pieceType: "bishop" 335 | }, 336 | { 337 | x: 3, 338 | y: 0, 339 | color: 'B', 340 | pieceType: "queen" 341 | }, 342 | { 343 | x: 3, 344 | y: 7, 345 | color: 'W', 346 | pieceType: "queen" 347 | } 348 | ]; 349 | //add pawns 350 | for(var p = 0; p < 8; p++) 351 | { 352 | that.pieces.push({ 353 | x : p, 354 | y: 1, 355 | color: 'B', 356 | pieceType: "pawn" 357 | }); 358 | } 359 | for(var p = 0; p < 8; p++) 360 | { 361 | that.pieces.push({ 362 | x : p, 363 | y: 6, 364 | color: 'W', 365 | pieceType: "pawn" 366 | }); 367 | } 368 | //set initial numOfMoves to 0 369 | for(var i = 0; i < that.pieces.length; i++){ 370 | that.pieces[i].numOfMoves = 0; 371 | 372 | } 373 | CHESSAPP.ui.drawPieces(that.pieces,that.cells); 374 | that.updateOptions(); 375 | }; 376 | that.clearAllOptionStyles = function(){ 377 | for(var y = 0; y < 8; y++){ 378 | for(var x = 0; x < 8; x++){ 379 | CHESSAPP.ui.clearOptionStyles(that.cells[x][y]); 380 | } 381 | } 382 | }; 383 | that.updateOptions = function(){ 384 | var response = CHESSAPP.Analyzer.makeAllOptions({pieces: that.pieces}), 385 | currentColor = _settings.turn, //check all of the options of the other player, if they have none, they could be in stalemate 386 | stalemate = currentColor, //originally true, but set to false as soon as options found 387 | check = false, 388 | checkmate = false; //in reality, just stalemate and check together 389 | 390 | options = response.allOptions; 391 | //console.log("Options recieved: "); 392 | //console.log(options); 393 | 394 | for(var i = 0; i < options.length; i++){ 395 | //check if corresponding piece is this color 396 | if(!that.pieces[i]){ 397 | continue; 398 | } 399 | if(that.pieces[i].color == currentColor){ 400 | if(options[i].length == 0){ 401 | continue; 402 | } 403 | else{ 404 | stalemate = false; 405 | } 406 | } 407 | } 408 | 409 | if(response.kingInCheck){ 410 | check = response.kingInCheck; 411 | 412 | } 413 | if(stalemate && check){ 414 | checkmate = check; 415 | } 416 | 417 | 418 | var local = (currentColor == _settings.onlineColor), 419 | msg = "", 420 | type = "fb"; 421 | 422 | if(checkmate){ 423 | if(local){ 424 | msg = "You are in checkmate. Your opponent wins"; 425 | type = "e"; 426 | } 427 | else{ 428 | msg = "Your opponent is in checkmate. You win"; 429 | type = "s"; 430 | } 431 | } 432 | else if(stalemate){ 433 | msg = "You are in stalemate"; 434 | type = "f"; 435 | } 436 | else if(check){ 437 | if(local){ 438 | msg = "You are in check"; 439 | type = "e"; 440 | } 441 | else{ 442 | msg = "Your opponent is in check"; 443 | type = "s"; 444 | } 445 | } 446 | if(check || checkmate || stalemate){ 447 | that.statusUpdate({msg : msg, type : type}); 448 | } 449 | /*console.log("Status : "); 450 | console.log("Check : " + check); 451 | console.log("Stalemate : " + stalemate); 452 | console.log("Checkmate : " + checkmate);*/ 453 | 454 | } 455 | /* 456 | * stg params: 457 | * x 458 | * y 459 | * piece 460 | * moveType: (null means regular, "en" means enpassant, "ca" means castle) 461 | * 462 | */ 463 | that.movePieceTo = function(stg){ 464 | 465 | var piece = stg.piece, 466 | x = stg.x, 467 | y = stg.y, 468 | cell = that.cells[x][y], 469 | pieceAtLocation = (stg.special == null) ? CHESSAPP.Analyzer.pieceExists({pieces: that.pieces, x:x, y:y}) : null,//wait if special 470 | callback = stg.callback, 471 | moveData = { 472 | pieceType: piece.pieceType, 473 | fromX: piece.x, 474 | toX: x, 475 | toY: y 476 | };//data to be sent to update movelist function 477 | 478 | if(_settings.locked == true){ 479 | //all moving is locked 480 | return false; 481 | } 482 | if(!that.isOption(piece, cell)){ 483 | //this is not a valid option 484 | return false; 485 | } 486 | 487 | 488 | if(stg.local){ 489 | //check if this is a promotion 490 | if(piece.pieceType == "pawn" && (y == 0 || y == 7)){ 491 | var cb = function(){ 492 | stg.promotion = true; 493 | that.movePieceTo(stg); 494 | }; 495 | //show the promotion selection, and wait until user selects a piece, 496 | //then call the movePieceTo method again with the newly promoted selection 497 | that.showPromotion({piece: piece, callback : cb}); 498 | return; 499 | } 500 | } 501 | if(stg.special != null){ 502 | //special move 503 | console.log("Special move!", stg.special); 504 | if(stg.special.type=="en"){ 505 | //get the en passant piece 506 | pieceAtLocation = CHESSAPP.Analyzer.pieceExists({pieces:that.pieces, x:stg.special.enx, y:stg.special.eny}); 507 | } 508 | else if(stg.special.type=="castle"){ 509 | console.log("Castling"); 510 | //move that rook 511 | var rook = CHESSAPP.Analyzer.pieceExists({pieces:that.pieces, x:stg.special.rookx, y:stg.special.rooky}); 512 | 513 | rook.y = stg.special.rooktoy; 514 | rook.x = stg.special.rooktox; 515 | rook.numOfMoves++; 516 | rook.justMoved = true; 517 | CHESSAPP.ui.addPiece(rook, that.cells[rook.x][rook.y]); 518 | 519 | } 520 | } 521 | //check if there is a piece of the opposing color occupying this space 522 | if(pieceAtLocation != null){ 523 | if(pieceAtLocation.color != piece.color) 524 | { 525 | moveData.killed = true; 526 | //remove this piece (it was taken) 527 | that.killPiece(pieceAtLocation); 528 | } 529 | else{ 530 | //you can't move on the same space as another piece of that color 531 | console.log("Invalid move cannot move on another piece of same color"); 532 | return; 533 | } 534 | } 535 | 536 | if(stg.local){ 537 | //send move to remote player 538 | var params = {pieceX: piece.x, pieceY: piece.y, newX: x, newY: y, special: stg.special}; 539 | if(stg.promotion){//user just promoted this piece 540 | params.promotion = piece.pieceType; 541 | } 542 | that.sendMove(params); 543 | } 544 | 545 | if(stg.promotion){ 546 | moveData.promotion = stg.promotion; 547 | } 548 | 549 | piece.y = y; 550 | piece.x = x; 551 | piece.numOfMoves++; 552 | piece.justMoved = true; 553 | 554 | that.switchTurn(); //switch the turn 555 | that.addToMoveList(moveData);//add the move to the move list 556 | that.clearAllOptionStyles();//clear all of the option styles 557 | selectedPieceIndex = -1; 558 | CHESSAPP.ui.addPiece(piece, cell); 559 | that.updateOptions();//update all of the options here 560 | 561 | }; 562 | that.killPiece = function(piece){ 563 | that.removePieceFromDom(piece); 564 | that.removePieceFromList(piece); 565 | } 566 | that.removePieceFromDom = function(piece){ 567 | 568 | var parent = piece.reference.parentNode; 569 | if(parent != null){ 570 | //remove from existing position 571 | parent.removeChild(piece.reference); 572 | } 573 | }; 574 | 575 | that.removePieceFromList = function(piece){ 576 | that.pieces[that.pieces.indexOf(piece)] = null;//don't delete because we need it to be blank so it matches options array 577 | }; 578 | that.showPromotion = function(stg){ 579 | _settings.locked = true; 580 | stg.val = true; 581 | CHESSAPP.ui.setSelectionVisible(stg); 582 | }, 583 | that.promote = function(stg){ 584 | var type = stg.pieceType, 585 | pieceGettingPromoted = stg.piece; 586 | if(pieceGettingPromoted){ 587 | var local = pieceGettingPromoted.color == _settings.onlineColor; 588 | if(local || !_settings.online){ 589 | that.statusUpdate({msg: "You have promoted", type: "s"}); 590 | } 591 | else{ 592 | that.statusUpdate({msg: "Your opponent has been promoted", type: "e"}); 593 | } 594 | pieceGettingPromoted.pieceType = type;//change pawn to new type 595 | CHESSAPP.ui.updatePiece(pieceGettingPromoted); //update piece image 596 | CHESSAPP.ui.setSelectionVisible({val: false});//hide selection 597 | _settings.locked = false;//unlock moving 598 | if(stg.callback){ 599 | stg.callback();//this calls the movePieceTo method again with the original data 600 | } 601 | } 602 | }; 603 | //gets a move made from the opposing player online 604 | //makes it locally to match 605 | that.onlineMove = function(data){ 606 | console.log(data); 607 | //get the piece that moved 608 | var pieceMoved = CHESSAPP.Analyzer.pieceExists({pieces: that.pieces, x: data.pieceX, y: data.pieceY}); 609 | if(pieceMoved){ 610 | if(data.promotion){ 611 | that.promote({piece: pieceMoved, pieceType: data.promotion}); 612 | } 613 | that.movePieceTo({piece: pieceMoved, x: data.newX, y: data.newY, promotion: data.promotion, special: data.special}); 614 | } 615 | } 616 | 617 | that.chatMessage = function(stg){ 618 | if(!stg.msg){ 619 | return;//no message 620 | } 621 | if(stg.local){ 622 | //add the color of the local player 623 | stg.color = _settings.onlineColor; 624 | //send to other player 625 | CHESSAPP.onlinePlay.sendChat(stg); 626 | } 627 | CHESSAPP.ui.addChatMessage(stg); 628 | } 629 | return that; 630 | })(); 631 | 632 | /* helper class for status scrolling 633 | stg is expected to have 634 | elem - element of container holding window 635 | maxLines - the number of lines shown at any time in the element 636 | */ 637 | var statusScroller = function(stg){ 638 | if(this == window){ 639 | //enforce new 640 | return new statusScroller(stg); 641 | } 642 | var lineHeight = 0, 643 | offset = 0, 644 | maxLines = stg.maxLines, 645 | totalLines = 0, 646 | containerElem = stg.elem, 647 | windowElem = document.createElement("div"); 648 | 649 | windowElem.style.position = "relative"; 650 | containerElem.appendChild(windowElem); 651 | 652 | this.updateClasses = function(){ 653 | return; 654 | CHESSAPP.utils.removeClass(containerElem, "upDisabled"); 655 | CHESSAPP.utils.removeClass(containerElem, "downDisabled"); 656 | if(totalLines < maxLines){ 657 | CHESSAPP.utils.addClass(containerElem, "upDisabled"); 658 | CHESSAPP.utils.addClass(containerElem, "downDisabled"); 659 | } 660 | else if(offset == (maxLines - totalLines) - 1){ 661 | CHESSAPP.utils.addClass(containerElem, "downDisabled"); 662 | } 663 | else if(offset == 0){ 664 | CHESSAPP.utils.addClass(containerElem, "upDisabled"); 665 | } 666 | } 667 | this.move = function(up){ 668 | if(stg.scroll){return;}//this is only for non scrolling 669 | if(totalLines <= maxLines){ 670 | return; 671 | } 672 | if(up){ 673 | if(offset >= 0){ 674 | return; 675 | } 676 | else{ 677 | offset++; 678 | } 679 | } 680 | else{ 681 | if(offset <= (maxLines - totalLines) - 1){ 682 | return; 683 | } 684 | else{ 685 | offset--; 686 | } 687 | } 688 | windowElem.style.top = (offset * lineHeight) + "px"; 689 | this.updateClasses(); 690 | }; 691 | this.goToBottom = function(){ 692 | if(stg.scroll){ 693 | containerElem.scrollTop = containerElem.scrollHeight; 694 | } 695 | else{ 696 | if(totalLines > maxLines){ 697 | offset = (maxLines - totalLines); 698 | windowElem.style.top = (offset * lineHeight) + "px"; 699 | } 700 | } 701 | this.updateClasses(); 702 | }; 703 | this.add = function(stg){ 704 | var def = { 705 | msg : "", 706 | type : "fb", //fb - feedback, e - error, s - success, W - white, B - black (chat messgae) 707 | showTime: false 708 | }, 709 | textNode, 710 | textNode2, 711 | p = document.createElement("p"), 712 | time = new Date(),//get the current time 713 | timetext = time.toLocaleTimeString(), 714 | timeEl = document.createElement("time"); 715 | 716 | CHESSAPP.utils.extend(def, stg); 717 | 718 | if(def.msg == null){ 719 | return false; 720 | } 721 | 722 | 723 | //show feedback 724 | textNode = document.createTextNode(timetext); 725 | timeEl.appendChild(textNode); 726 | p.appendChild(timeEl); 727 | 728 | textNode2 = document.createTextNode(stg.msg); 729 | p.appendChild(textNode2); 730 | p.setAttribute("class", def.type); 731 | windowElem.appendChild(p); 732 | 733 | //set the position to hide messages that are two lines old 734 | totalLines++; 735 | lineHeight = p.offsetHeight; 736 | this.goToBottom(); 737 | } 738 | }; 739 | -------------------------------------------------------------------------------- /scripts/OnlinePlay.js: -------------------------------------------------------------------------------- 1 | CHESSAPP.onlinePlay = { 2 | sk : null, 3 | /* 4 | connects to websocket server, and sets up events for when a matched player is found 5 | */ 6 | connect: function(stg, callback){ 7 | var op = CHESSAPP.onlinePlay; 8 | var hostPort = "http://localhost:" + CHESSAPP.globalSettings.port; 9 | if(CHESSAPP.globalSettings.live){ 10 | hostPort = CHESSAPP.globalSettings.host; 11 | } 12 | this.sk = io.connect(hostPort); 13 | CHESSAPP.ui.statusUpdate({type: 'fb', msg: 'Searching for partner...'}); 14 | this.sk.emit('setup', {color: stg.preferredColor}); 15 | this.sk.on("chat", function(data){ 16 | CHESSAPP.GamePlay.chatMessage(data); 17 | }); 18 | this.sk.on("partnerDisconnect", function(){ 19 | CHESSAPP.GamePlay.statusUpdate({type: 'e', msg: 'Your partner has disconnected'}); 20 | //CHESSAPP.GamePlay.showSplash(); 21 | }); 22 | this.sk.on("disconnect", function(){ 23 | CHESSAPP.GamePlay.statusUpdate({type: 'e', msg: 'The server seems to be down. Please refresh the page to try again. We are sorry for the inconvenience.'}); 24 | }); 25 | this.sk.on('matchfound', function (data) { 26 | 27 | CHESSAPP.GamePlay.statusUpdate({type: 'fb', msg: 'Partner found, game has begun'}); 28 | CHESSAPP.GamePlay.statusUpdate({type: 'fb', msg : 'Playing as ' + (data.color == 'W' ? "white" : 'black')}) 29 | CHESSAPP.GamePlay.setOnlineColor(data.color); //maybe change this to decouple 30 | callback(); 31 | }); 32 | this.sk.on('opposing_move', function(data){ 33 | CHESSAPP.GamePlay.onlineMove(data); 34 | CHESSAPP.GamePlay.statusUpdate({type: 's', msg: "It's your move!"}); 35 | }); 36 | }, 37 | sendMove: function(stg){ 38 | this.sk.emit('movemade', stg); 39 | CHESSAPP.GamePlay.statusUpdate({type: 's', msg: "Move made, waiting for partner"}); 40 | console.log("Sending messsage"); 41 | }, 42 | sendChat: function(stg){ 43 | stg.local = false;//because the recieved message will not be local 44 | this.sk.emit('chat', stg); 45 | }, 46 | handleMsg : function(e){ 47 | var resp = JSON.parse(e.data); 48 | console.log(resp); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /scripts/settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Online chess game with websockets, webworkers, and more 3 | * @namespace CHESSAPP 4 | */ 5 | var CHESSAPP = {}; 6 | /** 7 | * Directory where resources for chess pieces are 8 | * @property imageDir 9 | */ 10 | CHESSAPP.globalSettings = { 11 | imageDir : "images/", 12 | debug : false, 13 | live: true, 14 | port: 5000, 15 | host: "https://kevinalbs.com:5000" 16 | }; 17 | 18 | var gameSettings = { 19 | containerID : "container", 20 | online: true, 21 | preferredColor: false 22 | }; 23 | -------------------------------------------------------------------------------- /scripts/ui.js: -------------------------------------------------------------------------------- 1 | /** 2 | Funcionality for user interface, creates dialogs, responds to click events, manages status bar, message box, and move box 3 | 4 | @module ui 5 | **/ 6 | CHESSAPP.ui = (function(){ 7 | var that = {}, 8 | chessboard= null, //element where chessboard is :P 9 | rightCol = null, 10 | selection = null, //element where promotion selection box is 11 | color = null, //element where color selection box is 12 | initial = null, //element where initial selection box is 13 | moveList = null,//table of moves 14 | moveListCurRow = null, //current row which move is being displayed 15 | rowCount = 1,//move row count 16 | overlay= null, //overlay element 17 | overlayScreens = {}, //list of overlay screens, selection, settings, etc. overlay screen object has these props: elem, onShow, onHide 18 | status = null,//element where status updates are shown 19 | statusWindow = null,//inner area in status where text actually is 20 | lineSize= 0,//size of each paragraph line (for scrolling in status) 21 | promotion_data = null,//stores the piece to be promoted, and the callback while the user is choosing a piece 22 | chatWindow = null, 23 | chatInput= null, 24 | online= false, 25 | preferredColor= "W", 26 | chatActive= false, 27 | initSub= null,//subscriber to the initial 28 | elementsCreated = false;//tells whether the necessary elements are in place (at first no, but created in init) 29 | 30 | var createRightCol = function(){ 31 | rightCol = document.createElement("div"); 32 | rightCol.className = "rightCol"; 33 | container.appendChild(rightCol); 34 | }; 35 | //UI creation methods follow 36 | var createChat= function(chatID){ 37 | chatContainer = document.createElement("div"); 38 | chatContainer.className = "chat"; 39 | chatInput = document.createElement("input"); 40 | chatWindow = document.createElement("div"); 41 | chatWindow.className = "chatContainer"; 42 | 43 | var cw = chatWindow, 44 | ci = chatInput, 45 | def = "type something and press enter"; 46 | 47 | ci.value = def; 48 | //remove the default text in the textbox when a user focuses 49 | CHESSAPP.utils.bind(ci, "focus", function(e){ 50 | if(ci.value == def){ 51 | ci.value = ""; 52 | } 53 | }); 54 | //add it back if there is nothing in the textbox 55 | CHESSAPP.utils.bind(ci, "blur", function(e){ 56 | if(ci.value == ""){ 57 | ci.value = def; 58 | } 59 | }); 60 | CHESSAPP.utils.bind(ci, "keypress", function(e){ 61 | var key=(e.charCode)?e.charCode:((e.keyCode)?e.keyCode:((e.which)?e.which:0)); 62 | if(key=="13"){ 63 | CHESSAPP.GamePlay.chatMessage({msg : ci.value, local : true}); 64 | ci.value = ""; 65 | } 66 | }); 67 | 68 | 69 | var header = document.createElement("h2"); 70 | header.appendChild(document.createTextNode("chat")); 71 | 72 | chatContainer.appendChild(header); 73 | chatContainer.appendChild(cw); 74 | chatContainer.appendChild(ci); 75 | 76 | rightCol.appendChild(chatContainer); 77 | }; 78 | var activateChat= function(){ 79 | chatActive = true; 80 | CHESSAPP.utils.addClass(chatContainer, "active"); 81 | }; 82 | var deactivateChat= function(){ 83 | chatActive = false; 84 | CHESSAPP.utils.removeClass(chatContainer, "active"); 85 | }; 86 | var createMovelist = function(){ 87 | var moveListContainer = document.createElement("div"), 88 | moveListScroll = document.createElement("div"), 89 | header = document.createElement("h2"); 90 | 91 | moveList = document.createElement("table"); 92 | header.appendChild(document.createTextNode("moves")); 93 | moveListContainer.className = "movelist"; 94 | moveListScroll.className = "scroll"; 95 | moveListScroll.appendChild(moveList); 96 | moveListContainer.appendChild(header); 97 | moveListContainer.appendChild(moveListScroll); 98 | moveListCurRow = document.createElement("tr"); 99 | moveList.appendChild(moveListCurRow); 100 | rightCol.appendChild(moveListContainer); 101 | }; 102 | var createOverlay = function(){ 103 | overlay = document.createElement("div"); 104 | overlay.className = "overlay"; 105 | container.appendChild(overlay); 106 | //create overlay screens 107 | createSelection(); 108 | createColor(); 109 | createInitial(); 110 | }; 111 | var createSelection = function(){ 112 | var selection = document.createElement("div"), 113 | frag = document.createDocumentFragment(), 114 | a = document.createElement("a"), 115 | a2 = document.createElement("a"), 116 | a3 = document.createElement("a"), 117 | a4 = document.createElement("a"); 118 | 119 | selection.className = "selection overscreen"; 120 | 121 | a.setAttribute("data-pieceType", "knight"); 122 | a.appendChild(document.createTextNode("Knight")); 123 | a2.setAttribute("data-pieceType", "bishop"); 124 | a2.appendChild(document.createTextNode("Bishop")); 125 | a3.setAttribute("data-pieceType", "rook"); 126 | a3.appendChild(document.createTextNode("Rook")); 127 | a4.setAttribute("data-pieceType", "queen"); 128 | a4.appendChild(document.createTextNode("Queen")); 129 | 130 | 131 | frag.appendChild(a); 132 | frag.appendChild(a2); 133 | frag.appendChild(a3); 134 | frag.appendChild(a4); 135 | 136 | selection.appendChild(frag); 137 | overlay.appendChild(selection); 138 | 139 | CHESSAPP.utils.bind(selection, "click", that.promotionClicked); 140 | 141 | selection = selection; 142 | overlayScreens["selection"] = {elem: selection}; 143 | }; 144 | 145 | var createColor = function(){ 146 | var color = document.createElement("div"), 147 | frag = document.createDocumentFragment(), 148 | a = document.createElement("a"), 149 | a2 = document.createElement("a"), 150 | a3 = document.createElement("a"), 151 | a4 = document.createElement("a"), 152 | h2 = document.createElement("h2"), 153 | span = document.createElement("span"); 154 | 155 | color.className = "color overscreen"; 156 | 157 | h2.appendChild(document.createTextNode("Choose your preferred color")); 158 | a.setAttribute("data-color", "W"); 159 | a.appendChild(document.createTextNode("White")); 160 | a2.setAttribute("data-color", "B"); 161 | a2.appendChild(document.createTextNode("Black")); 162 | a3.setAttribute("data-color", "U"); 163 | a3.appendChild(document.createTextNode("Unspecified")); 164 | span.appendChild(document.createTextNode("Matches up with anyone, either black or white player")); 165 | a3.appendChild(span); 166 | 167 | frag.appendChild(h2); 168 | frag.appendChild(a); 169 | frag.appendChild(a2); 170 | frag.appendChild(a3); 171 | 172 | color.appendChild(frag); 173 | overlay.appendChild(color); 174 | 175 | CHESSAPP.utils.bind(color, "click", that.preferredClicked); 176 | 177 | color = color; 178 | overlayScreens["color"] = {elem: color}; 179 | }; 180 | createInitial = function(){ 181 | var initial = document.createElement("div"), 182 | frag = document.createDocumentFragment(), 183 | a = document.createElement("a"), 184 | a2 = document.createElement("a"), 185 | h2 = document.createElement("h2"); 186 | 187 | initial.className = "initial overscreen"; 188 | 189 | h2.appendChild(document.createTextNode("Choose your mode")); 190 | a.setAttribute("data-mode", "offline"); 191 | a.appendChild(document.createTextNode("Offline Play")); 192 | a2.setAttribute("data-mode", "online"); 193 | a2.appendChild(document.createTextNode("Online Play")); 194 | 195 | frag.appendChild(h2); 196 | frag.appendChild(a); 197 | frag.appendChild(a2); 198 | 199 | initial.appendChild(frag); 200 | overlay.appendChild(initial); 201 | 202 | CHESSAPP.utils.bind(initial, "click", that.modeClicked); 203 | 204 | initial = initial; 205 | overlayScreens["initial"] = {elem: initial}; 206 | }; 207 | 208 | /* creates container for status and scrolling */ 209 | var createStatus = function(statusID){ 210 | status = document.createElement("div"), 211 | arrow_up = document.createElement("a"), 212 | arrow_down = document.createElement("a"); 213 | 214 | status.className = "status"; 215 | arrow_up.className = "arrow_up"; 216 | arrow_down.className = "arrow_down"; 217 | 218 | statusWindow = new statusScroller({elem: status, maxLines: 2}); 219 | CHESSAPP.utils.bind(arrow_up, "click", function(e){ 220 | statusWindow.move(true); 221 | e.preventDefault(); 222 | return false; 223 | }); 224 | CHESSAPP.utils.bind(arrow_down, "click", function(e){ 225 | statusWindow.move(false); 226 | e.preventDefault(); 227 | return false; 228 | }); 229 | 230 | 231 | status.appendChild(arrow_up); 232 | status.appendChild(arrow_down); 233 | container.appendChild(status); 234 | }; 235 | 236 | 237 | 238 | var readyToPlay= function(){ 239 | if(online){ 240 | activateChat(); 241 | } 242 | else{ 243 | deactivateChat(); 244 | } 245 | initSub.apply(window, [{color: preferredColor, online: online}]); 246 | }; 247 | 248 | 249 | var toggleOverlay= function(val, screen){ 250 | overlay.style.display = val ? "block" : "none"; 251 | //hide all screens 252 | for(var i in overlayScreens){ 253 | if(overlayScreens.hasOwnProperty(i)){ 254 | overlayScreens[i].elem.style.display = "none"; 255 | } 256 | } 257 | //show screen if there is one 258 | if(val && !!screen){ 259 | overlayScreens[screen].elem.style.display = "block"; 260 | } 261 | }; 262 | 263 | //adds cells to the elem, returns an array of the cells 264 | var drawCells = function(){ 265 | chessboard = document.createElement("div"), 266 | frag = document.createDocumentFragment(), 267 | cellDiv = document.createElement("div"), 268 | cells = new Array(8); 269 | 270 | chessboard.className = "chessboard"; 271 | /* the reason that the divs are created here is for performance, otherwise 272 | the divs would normally be created in the Cell class */ 273 | 274 | //create multi-dimensional array 275 | for(var x = 0; x < 8; x++){ 276 | cells[x] = new Array(8); 277 | } 278 | //create board and cell references 279 | for(var y = 0; y < 8; y++){ 280 | for(var x = 0; x < 8; x++){ 281 | var clone = cellDiv.cloneNode(); 282 | if((x % 2 == 1 && y % 2 == 1) || (x % 2 == 0 && y % 2 == 0)){ 283 | CHESSAPP.utils.addClass(clone, "W"); 284 | }else{ 285 | CHESSAPP.utils.addClass(clone, "B"); 286 | } 287 | clone.setAttribute("data-x", x); 288 | clone.setAttribute("data-y", y); 289 | //create cell object 290 | cells[x][y] = { 291 | reference: clone, 292 | x: x, 293 | y: y 294 | }; 295 | /* just for testing */ 296 | if(CHESSAPP.globalSettings.debug){ 297 | var coords = document.createElement("p"); 298 | coords.innerHTML = x + " , " + y; 299 | cells[x][y].reference.appendChild(coords); 300 | } 301 | frag.appendChild(clone); 302 | 303 | } 304 | 305 | 306 | } 307 | chessboard.appendChild(frag); 308 | 309 | //add chessboard to container 310 | container.appendChild(chessboard); 311 | CHESSAPP.utils.bind(chessboard, "click", CHESSAPP.ui.boardClicked); 312 | 313 | //returns list of cells 314 | return cells; 315 | }; 316 | 317 | 318 | 319 | 320 | 321 | /* 322 | * Public Methods 323 | */ 324 | that.init = function(stg){ 325 | container = stg.container; 326 | if(!elementsCreated){ 327 | //create the UI here... ugh 328 | createStatus(); 329 | createOverlay(); 330 | createRightCol(); 331 | createChat(); 332 | createMovelist(); 333 | elementsCreated = true; 334 | } 335 | toggleOverlay(true, "initial"); 336 | return drawCells(); //not sure what to do about this right now 337 | }; 338 | that.addChatMessage = function(stg){ 339 | var prefix = (stg.color == 'W') ? "White - " : (stg.color == "B") ? "Black - " : "", 340 | p = document.createElement("p"), 341 | textNode = document.createTextNode(prefix + stg.msg); 342 | p.appendChild(textNode); 343 | chatWindow.appendChild(p); 344 | chatWindow.scrollTop = chatWindow.scrollHeight; 345 | 346 | }; 347 | that.addMove = function(txt){ 348 | console.log("Showing move:" + txt); 349 | var cell = document.createElement("td"); 350 | //create dom elements 351 | if(CHESSAPP.GamePlay.getTurn() == "B"){ 352 | //this means that white just moved (first move on the row) 353 | //add move count and turn 354 | cell.appendChild(document.createTextNode("" + rowCount)); 355 | moveListCurRow.appendChild(cell); 356 | cell = document.createElement("td"); 357 | cell.appendChild(document.createTextNode(txt)); 358 | moveListCurRow.appendChild(cell); 359 | } 360 | else{ 361 | //end of row 362 | cell.appendChild(document.createTextNode(txt)); 363 | moveListCurRow.appendChild(cell); 364 | rowCount++; 365 | //create next row 366 | moveListCurRow = document.createElement("tr"); 367 | moveList.appendChild(moveListCurRow); 368 | } 369 | }; 370 | /* 371 | this updates the status element with the user specified message 372 | */ 373 | that.statusUpdate= function(stg){ 374 | stg.showTime = true; 375 | statusWindow.add(stg); 376 | }; 377 | that.drawPieces= function(pieces, cells){ 378 | var i=0, max=pieces.length; 379 | for(; i 0){ 282 | b = queue['B'].shift(); 283 | //start new game 284 | GameList.addGame(sk, b); 285 | } 286 | else if(queue['U'].length > 0){ 287 | b = queue['U'].shift(); 288 | //start new game 289 | GameList.addGame(sk, b); 290 | } 291 | else{ 292 | queue['W'].push(sk); 293 | } 294 | } 295 | else if(skColor == 'B'){ 296 | if(queue['W'].length > 0){ 297 | w = queue['W'].shift(); 298 | //start new game 299 | GameList.addGame(w, sk); 300 | } 301 | else if(queue['U'].length > 0){ 302 | w = queue['U'].shift(); 303 | //start new game 304 | GameList.addGame(w, sk); 305 | } 306 | else{ 307 | queue['B'].push(sk); 308 | } 309 | } 310 | else{ 311 | //either white or no color specified, add player to whichever queue is waiting for oponent 312 | if(queue['W'].length > 0){ 313 | w = queue['W'].shift(); 314 | //start new game 315 | GameList.addGame(w, sk); 316 | } 317 | else if(queue['B'].length > 0){ 318 | b = queue['B'].shift(); 319 | //start new game 320 | GameList.addGame(sk, b); 321 | } 322 | else if(queue['U'].length > 0){ 323 | w = queue['U'].shift();//just give it to white 324 | //start new game 325 | GameList.addGame(w, sk); 326 | } 327 | else{ 328 | queue['U'].push(sk); 329 | } 330 | } 331 | 332 | }); 333 | }); 334 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0px; 3 | padding: 0px; 4 | font-family: 'Droid Sans', sans-serif; 5 | } 6 | .clearfix:after { 7 | content: "."; 8 | display: block; 9 | clear: both; 10 | visibility: hidden; 11 | line-height: 0; 12 | height: 0; 13 | } 14 | 15 | .clearfix { 16 | display: inline-block; 17 | } 18 | 19 | html[xmlns] .clearfix { 20 | display: block; 21 | } 22 | 23 | * html .clearfix { 24 | height: 1%; 25 | } 26 | #wrapper, #container{ 27 | width: 960px; 28 | margin: 0px auto; 29 | position: relative; 30 | } 31 | .overlay{ 32 | width: 100%; 33 | height: 684px; 34 | display: none; 35 | background: rgba(255,255,255, .65); 36 | position: absolute; 37 | top: 0px; 38 | left: 0px; 39 | } 40 | .overscreen{ 41 | width: 500px; 42 | height: 300px; 43 | background: #FFF; 44 | border: 2px black solid; 45 | margin: 100px auto 0px auto; 46 | clear: both; 47 | } 48 | .color span{ 49 | font-size: 10px; 50 | display: block; 51 | } 52 | .chessboard{ 53 | width: 600px; 54 | height: 600px; 55 | border: 2px #bbc8f0 solid; 56 | float: left; 57 | } 58 | .chessboard div{ 59 | width: 12.5%; 60 | height: 12.5%; 61 | float: left; 62 | 63 | } 64 | .chessboard div img{ 65 | width: 100%; 66 | height: 100%; 67 | } 68 | .chessboard div p{ 69 | position: absolute; 70 | } 71 | .chessboard div.B{ 72 | background-color: #bbc8f0; 73 | } 74 | .chessboard div.W{ 75 | background-color: #FFF; 76 | } 77 | .chessboard div.movable{ 78 | background-color: green; 79 | box-shadow: inset 0px 0px 0px 2px rgb(0,100,0); 80 | -moz-box-shadow: inset 0px 0px 0px 2px rgb(0,100,0); 81 | -webkit-box-shadow: inset 0px 0px 0px 2px rgb(0,100,0); 82 | } 83 | .chessboard div.attackable{ 84 | background-color: red; 85 | } 86 | .chessboard div.selected{ 87 | background-color: yellow; 88 | } 89 | .selection a, .color a, .initial a{ 90 | cursor: pointer; 91 | padding: 10px; 92 | width: 105px; 93 | padding-top: 100px; 94 | height: 190px; 95 | display: block; 96 | float: left; 97 | text-align: center; 98 | } 99 | .color h2{ 100 | display: block; 101 | } 102 | .color a, .initial a{ 103 | height: 100px; 104 | width: 146px; 105 | } 106 | .initial a{ 107 | width: 230px; 108 | } 109 | 110 | .selection a:hover, .color a:hover, .initial a:hover{ 111 | background: #CCC; 112 | } 113 | .rightCol{ 114 | float: right; 115 | } 116 | .chat, .movelist{ 117 | width: 300px; 118 | height: 250px; 119 | padding: 20px; 120 | min-height: 20px; 121 | border: 2px #bbc8f0 solid; 122 | display: none; 123 | margin-bottom: 16px; 124 | } 125 | .movelist{ 126 | display: block; 127 | } 128 | .movelist .scroll{ 129 | overflow: auto; 130 | height: 220px; 131 | } 132 | .movelist table{ 133 | width: 100%; 134 | border-collapse: collapse; 135 | } 136 | .movelist table tr td{ 137 | width: 45%; 138 | font-size: 12px; 139 | margin: 0px; 140 | padding: 0px; 141 | background: rgb(234, 237, 247); 142 | } 143 | .movelist table tr:nth-child(2n+1) td{ 144 | background: #bbc8f0; 145 | } 146 | .movelist table tr td:first-child{ 147 | width: 10%; 148 | } 149 | .chat.active{ 150 | display: block; 151 | } 152 | 153 | .chat p{ 154 | color: #000; 155 | font-family: 'Droid Sans', sans-serif; 156 | } 157 | .chat input{ 158 | width : 94%; 159 | background: #f0e1bb; 160 | color: #6f7aa3; 161 | font-style: italic; 162 | border: 1px #a3915e solid; 163 | font-size: 12px; 164 | height: 27px; 165 | padding: 0px 3%; 166 | -webkit-transition: background .2s linear; 167 | outline: none; 168 | 169 | } 170 | .chat input:focus{ 171 | font-style: normal; 172 | background: #fff4d7; 173 | color: #0b2a41; 174 | } 175 | .movelist h2, .chat h2{ 176 | font-weight: normal; 177 | color: #6f7da3; 178 | font-size: 24px; 179 | } 180 | .chat .chatContainer, .movelist .movelistContainer{ 181 | overflow: auto; 182 | height: 165px; 183 | margin: 15px 0px; 184 | } 185 | .chat .chatContainer p{ 186 | font-size: 14px; 187 | } 188 | .status{ 189 | background: #f0e1bb; 190 | padding-left: 12px; 191 | height: 40px; 192 | overflow: hidden; 193 | margin: 20px 0px; 194 | position: relative; 195 | } 196 | .status .arrow_up, .status .arrow_down{ 197 | position: absolute; 198 | z-index: 2; 199 | text-align: center; 200 | right: 0px; 201 | display: block; 202 | width: 20px; 203 | height: 20px; 204 | background-color: #6f7da3; 205 | cursor: pointer; 206 | -webkit-transition: background-color .2s linear; 207 | -moz-transition: background-color .2s linear; 208 | -o-transition: background-color .2s linear; 209 | -ms-transition: background-color .2s linear; 210 | transition: background-color .2s linear; 211 | } 212 | .status .arrow_up:hover, .status .arrow_down:hover{ 213 | background-color: #bbc8f0; 214 | } 215 | .status.upDisabled .arrow_up, .status.downDisabled .arrow_down{ 216 | background-color: #9b9b9b; 217 | -webkit-transition: none; 218 | -moz-transition: none; 219 | -o-transition: none; 220 | -ms-transition: none; 221 | transition: none; 222 | } 223 | .status .arrow_up{ 224 | top: 0px; 225 | background: url("images/arrow_up.png") no-repeat center #6f7da3; 226 | } 227 | .status .arrow_down{ 228 | bottom: 0px; 229 | background: url("images/arrow_down.png") no-repeat center #6f7da3; 230 | } 231 | .status p{ 232 | color: #0b2a41; 233 | font-size: 13px; 234 | padding: 2px 0px; 235 | margin: 0px; 236 | font-family: 'Droid Sans', sans-serif; 237 | } 238 | .status p.e{ 239 | color: #541717; 240 | } 241 | .status p.s{ 242 | color: #175419; 243 | } 244 | .status time{ 245 | color: #54686b; 246 | margin-right: 5px; 247 | } 248 | footer{ 249 | } --------------------------------------------------------------------------------