├── .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 | 
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 |
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) [](#sponsors)
5 | [](https://travis-ci.org/socketio/socket.io)
6 | [](https://david-dm.org/socketio/socket.io)
7 | [](https://david-dm.org/socketio/socket.io#info=devDependencies)
8 | [](https://www.npmjs.com/package/socket.io)
9 | 
10 | [](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 | [](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 | }
--------------------------------------------------------------------------------