chesscademy 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 67ffcc44810b626fd9ebfe0948e5f3a0c791715b
4
+ data.tar.gz: e4f3553b49b30481a4564619697e780a39cdb039
5
+ SHA512:
6
+ metadata.gz: 96eb00206cd9e97a6981c7a20612fed38289bd3179d1750f0e4a8cba48db88bfab1cc7f9268b82412cf9c4e33326d3e476924833b3a7f004dda176111d538060
7
+ data.tar.gz: 14e8d0cad23d7062bff907c19cfa653557847e2156c8a93b90ce2c18750c51faab9210efa8c745aa4737aa7d0490e5e77f22a346357284420058d640512350b2
data/.DS_Store ADDED
Binary file
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in chesscademyA.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Francis Hinson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # ChesscademyA
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'chesscademyA'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install chesscademyA
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( https://github.com/[my-github-username]/chesscademyA/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'chesscademyA/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "chesscademy"
8
+ spec.version = "0.0.2"
9
+ spec.authors = ["Francis Hinson"]
10
+ spec.email = ["francis@orchive.com"]
11
+ spec.summary = %q{Chesscademy chess engine assets.}
12
+ spec.description = %q{}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "railties", "~> 4.0"
24
+ end
@@ -0,0 +1,8 @@
1
+ require "chesscademyA/version"
2
+
3
+ module ChesscademyA
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module ChesscademyA
2
+ module Rails
3
+ VERSION = "0.0.2"
4
+ end
5
+ end
data/vendor/.DS_Store ADDED
Binary file
Binary file
Binary file
@@ -0,0 +1,1646 @@
1
+ // downloaded from https://github.com/jhlywa/chess.js/blob/master/chess.js
2
+ // on 27 July 2013
3
+ // commit: eed8f8a3b96d99fcd570b7ced105e8415409a800
4
+
5
+ 'use strict';
6
+
7
+ /*
8
+ * Copyright (c) 2011, Jeff Hlywa (jhlywa@gmail.com)
9
+ * All rights reserved.
10
+ *
11
+ * Redistribution and use in source and binary forms, with or without
12
+ * modification, are permitted provided that the following conditions are met:
13
+ *
14
+ * 1. Redistributions of source code must retain the above copyright notice,
15
+ * this list of conditions and the following disclaimer.
16
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
17
+ * this list of conditions and the following disclaimer in the documentation
18
+ * and/or other materials provided with the distribution.
19
+ *
20
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ * POSSIBILITY OF SUCH DAMAGE.
31
+ *
32
+ *----------------------------------------------------------------------------*/
33
+
34
+ window['Chess'] = window['Chess'] || function(fen) {
35
+ //var Chess = function(fen) {
36
+
37
+ /* jshint indent: false */
38
+
39
+ var BLACK = 'b';
40
+ var WHITE = 'w';
41
+
42
+ var EMPTY = -1;
43
+
44
+ var PAWN = 'p';
45
+ var KNIGHT = 'n';
46
+ var BISHOP = 'b';
47
+ var ROOK = 'r';
48
+ var QUEEN = 'q';
49
+ var KING = 'k';
50
+
51
+ var SYMBOLS = 'pnbrqkPNBRQK';
52
+
53
+ var DEFAULT_POSITION = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
54
+
55
+ var POSSIBLE_RESULTS = ['1-0', '0-1', '1/2-1/2', '*'];
56
+
57
+ var PAWN_OFFSETS = {
58
+ b: [16, 32, 17, 15],
59
+ w: [-16, -32, -17, -15]
60
+ };
61
+
62
+ var PIECE_OFFSETS = {
63
+ n: [-18, -33, -31, -14, 18, 33, 31, 14],
64
+ b: [-17, -15, 17, 15],
65
+ r: [-16, 1, 16, -1],
66
+ q: [-17, -16, -15, 1, 17, 16, 15, -1],
67
+ k: [-17, -16, -15, 1, 17, 16, 15, -1]
68
+ };
69
+
70
+ var ATTACKS = [
71
+ 20, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0,20, 0,
72
+ 0,20, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0,20, 0, 0,
73
+ 0, 0,20, 0, 0, 0, 0, 24, 0, 0, 0, 0,20, 0, 0, 0,
74
+ 0, 0, 0,20, 0, 0, 0, 24, 0, 0, 0,20, 0, 0, 0, 0,
75
+ 0, 0, 0, 0,20, 0, 0, 24, 0, 0,20, 0, 0, 0, 0, 0,
76
+ 0, 0, 0, 0, 0,20, 2, 24, 2,20, 0, 0, 0, 0, 0, 0,
77
+ 0, 0, 0, 0, 0, 2,53, 56, 53, 2, 0, 0, 0, 0, 0, 0,
78
+ 24,24,24,24,24,24,56, 0, 56,24,24,24,24,24,24, 0,
79
+ 0, 0, 0, 0, 0, 2,53, 56, 53, 2, 0, 0, 0, 0, 0, 0,
80
+ 0, 0, 0, 0, 0,20, 2, 24, 2,20, 0, 0, 0, 0, 0, 0,
81
+ 0, 0, 0, 0,20, 0, 0, 24, 0, 0,20, 0, 0, 0, 0, 0,
82
+ 0, 0, 0,20, 0, 0, 0, 24, 0, 0, 0,20, 0, 0, 0, 0,
83
+ 0, 0,20, 0, 0, 0, 0, 24, 0, 0, 0, 0,20, 0, 0, 0,
84
+ 0,20, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0,20, 0, 0,
85
+ 20, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0,20
86
+ ];
87
+
88
+ var RAYS = [
89
+ 17, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 15, 0,
90
+ 0, 17, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 15, 0, 0,
91
+ 0, 0, 17, 0, 0, 0, 0, 16, 0, 0, 0, 0, 15, 0, 0, 0,
92
+ 0, 0, 0, 17, 0, 0, 0, 16, 0, 0, 0, 15, 0, 0, 0, 0,
93
+ 0, 0, 0, 0, 17, 0, 0, 16, 0, 0, 15, 0, 0, 0, 0, 0,
94
+ 0, 0, 0, 0, 0, 17, 0, 16, 0, 15, 0, 0, 0, 0, 0, 0,
95
+ 0, 0, 0, 0, 0, 0, 17, 16, 15, 0, 0, 0, 0, 0, 0, 0,
96
+ 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1,-1, -1, -1, -1, 0,
97
+ 0, 0, 0, 0, 0, 0,-15,-16,-17, 0, 0, 0, 0, 0, 0, 0,
98
+ 0, 0, 0, 0, 0,-15, 0,-16, 0,-17, 0, 0, 0, 0, 0, 0,
99
+ 0, 0, 0, 0,-15, 0, 0,-16, 0, 0,-17, 0, 0, 0, 0, 0,
100
+ 0, 0, 0,-15, 0, 0, 0,-16, 0, 0, 0,-17, 0, 0, 0, 0,
101
+ 0, 0,-15, 0, 0, 0, 0,-16, 0, 0, 0, 0,-17, 0, 0, 0,
102
+ 0,-15, 0, 0, 0, 0, 0,-16, 0, 0, 0, 0, 0,-17, 0, 0,
103
+ -15, 0, 0, 0, 0, 0, 0,-16, 0, 0, 0, 0, 0, 0,-17
104
+ ];
105
+
106
+ var SHIFTS = { p: 0, n: 1, b: 2, r: 3, q: 4, k: 5 };
107
+
108
+ var FLAGS = {
109
+ NORMAL: 'n',
110
+ CAPTURE: 'c',
111
+ BIG_PAWN: 'b',
112
+ EP_CAPTURE: 'e',
113
+ PROMOTION: 'p',
114
+ KSIDE_CASTLE: 'k',
115
+ QSIDE_CASTLE: 'q'
116
+ };
117
+
118
+ var BITS = {
119
+ NORMAL: 1,
120
+ CAPTURE: 2,
121
+ BIG_PAWN: 4,
122
+ EP_CAPTURE: 8,
123
+ PROMOTION: 16,
124
+ KSIDE_CASTLE: 32,
125
+ QSIDE_CASTLE: 64
126
+ };
127
+
128
+ var RANK_1 = 7;
129
+ var RANK_2 = 6;
130
+ var RANK_3 = 5;
131
+ var RANK_4 = 4;
132
+ var RANK_5 = 3;
133
+ var RANK_6 = 2;
134
+ var RANK_7 = 1;
135
+ var RANK_8 = 0;
136
+
137
+ var SQUARES = {
138
+ a8: 0, b8: 1, c8: 2, d8: 3, e8: 4, f8: 5, g8: 6, h8: 7,
139
+ a7: 16, b7: 17, c7: 18, d7: 19, e7: 20, f7: 21, g7: 22, h7: 23,
140
+ a6: 32, b6: 33, c6: 34, d6: 35, e6: 36, f6: 37, g6: 38, h6: 39,
141
+ a5: 48, b5: 49, c5: 50, d5: 51, e5: 52, f5: 53, g5: 54, h5: 55,
142
+ a4: 64, b4: 65, c4: 66, d4: 67, e4: 68, f4: 69, g4: 70, h4: 71,
143
+ a3: 80, b3: 81, c3: 82, d3: 83, e3: 84, f3: 85, g3: 86, h3: 87,
144
+ a2: 96, b2: 97, c2: 98, d2: 99, e2: 100, f2: 101, g2: 102, h2: 103,
145
+ a1: 112, b1: 113, c1: 114, d1: 115, e1: 116, f1: 117, g1: 118, h1: 119
146
+ };
147
+
148
+ var ROOKS = {
149
+ w: [{square: SQUARES.a1, flag: BITS.QSIDE_CASTLE},
150
+ {square: SQUARES.h1, flag: BITS.KSIDE_CASTLE}],
151
+ b: [{square: SQUARES.a8, flag: BITS.QSIDE_CASTLE},
152
+ {square: SQUARES.h8, flag: BITS.KSIDE_CASTLE}]
153
+ };
154
+
155
+ var board = new Array(128);
156
+ var kings = {w: EMPTY, b: EMPTY};
157
+ var turn = WHITE;
158
+ var castling = {w: 0, b: 0};
159
+ var ep_square = EMPTY;
160
+ var half_moves = 0;
161
+ var move_number = 1;
162
+ var history = [];
163
+ var header = {};
164
+
165
+ /* if the user passes in a fen string, load it, else default to
166
+ * starting position
167
+ */
168
+ if (typeof fen === 'undefined') {
169
+ load(DEFAULT_POSITION);
170
+ } else {
171
+ load(fen);
172
+ }
173
+
174
+ function clear() {
175
+ board = new Array(128);
176
+ kings = {w: EMPTY, b: EMPTY};
177
+ turn = WHITE;
178
+ castling = {w: 0, b: 0};
179
+ ep_square = EMPTY;
180
+ half_moves = 0;
181
+ move_number = 1;
182
+ history = [];
183
+ header = {};
184
+ update_setup(generate_fen());
185
+ }
186
+
187
+ function reset() {
188
+ load(DEFAULT_POSITION);
189
+ }
190
+
191
+ function load(fen) {
192
+ var tokens = fen.split(/\s+/);
193
+ var position = tokens[0];
194
+ var square = 0;
195
+ var valid = SYMBOLS + '12345678/';
196
+
197
+ if (!validate_fen(fen).valid) {
198
+ return false;
199
+ }
200
+
201
+ clear();
202
+
203
+ for (var i = 0; i < position.length; i++) {
204
+ var piece = position.charAt(i);
205
+
206
+ if (piece === '/') {
207
+ square += 8;
208
+ } else if (is_digit(piece)) {
209
+ square += parseInt(piece, 10);
210
+ } else {
211
+ var color = (piece < 'a') ? WHITE : BLACK;
212
+ put({type: piece.toLowerCase(), color: color}, algebraic(square));
213
+ square++;
214
+ }
215
+ }
216
+
217
+ turn = tokens[1];
218
+
219
+ if (tokens[2].indexOf('K') > -1) {
220
+ castling.w |= BITS.KSIDE_CASTLE;
221
+ }
222
+ if (tokens[2].indexOf('Q') > -1) {
223
+ castling.w |= BITS.QSIDE_CASTLE;
224
+ }
225
+ if (tokens[2].indexOf('k') > -1) {
226
+ castling.b |= BITS.KSIDE_CASTLE;
227
+ }
228
+ if (tokens[2].indexOf('q') > -1) {
229
+ castling.b |= BITS.QSIDE_CASTLE;
230
+ }
231
+
232
+ ep_square = (tokens[3] === '-') ? EMPTY : SQUARES[tokens[3]];
233
+ half_moves = parseInt(tokens[4], 10);
234
+ move_number = parseInt(tokens[5], 10);
235
+
236
+ update_setup(generate_fen());
237
+
238
+ return true;
239
+ }
240
+
241
+ function validate_fen(fen) {
242
+ var errors = {
243
+ 0: 'No errors.',
244
+ 1: 'FEN string must contain six space-delimited fields.',
245
+ 2: '6th field (move number) must be a positive integer.',
246
+ 3: '5th field (half move counter) must be a non-negative integer.',
247
+ 4: '4th field (en-passant square) is invalid.',
248
+ 5: '3rd field (castling availability) is invalid.',
249
+ 6: '2nd field (side to move) is invalid.',
250
+ 7: '1st field (piece positions) does not contain 8 \'/\'-delimited rows.',
251
+ 8: '1st field (piece positions) is invalid [consecutive numbers].',
252
+ 9: '1st field (piece positions) is invalid [invalid piece].',
253
+ 10: '1st field (piece positions) is invalid [row too large].',
254
+ };
255
+
256
+ /* 1st criterion: 6 space-seperated fields? */
257
+ var tokens = fen.split(/\s+/);
258
+ if (tokens.length !== 6) {
259
+ return {valid: false, error_number: 1, error: errors[1]};
260
+ }
261
+
262
+ /* 2nd criterion: move number field is a integer value > 0? */
263
+ if (isNaN(tokens[5]) || (parseInt(tokens[5], 10) <= 0)) {
264
+ return {valid: false, error_number: 2, error: errors[2]};
265
+ }
266
+
267
+ /* 3rd criterion: half move counter is an integer >= 0? */
268
+ if (isNaN(tokens[4]) || (parseInt(tokens[4], 10) < 0)) {
269
+ return {valid: false, error_number: 3, error: errors[3]};
270
+ }
271
+
272
+ /* 4th criterion: 4th field is a valid e.p.-string? */
273
+ if (!/^(-|[abcdefgh][36])$/.test(tokens[3])) {
274
+ return {valid: false, error_number: 4, error: errors[4]};
275
+ }
276
+
277
+ /* 5th criterion: 3th field is a valid castle-string? */
278
+ if( !/^(KQ?k?q?|Qk?q?|kq?|q|-)$/.test(tokens[2])) {
279
+ return {valid: false, error_number: 5, error: errors[5]};
280
+ }
281
+
282
+ /* 6th criterion: 2nd field is "w" (white) or "b" (black)? */
283
+ if (!/^(w|b)$/.test(tokens[1])) {
284
+ return {valid: false, error_number: 6, error: errors[6]};
285
+ }
286
+
287
+ /* 7th criterion: 1st field contains 8 rows? */
288
+ var rows = tokens[0].split('/');
289
+ if (rows.length !== 8) {
290
+ return {valid: false, error_number: 7, error: errors[7]};
291
+ }
292
+
293
+ /* 8th criterion: every row is valid? */
294
+ for (var i = 0; i < rows.length; i++) {
295
+ /* check for right sum of fields AND not two numbers in succession */
296
+ var sum_fields = 0;
297
+ var previous_was_number = false;
298
+
299
+ for (var k = 0; k < rows[i].length; k++) {
300
+ if (!isNaN(rows[i][k])) {
301
+ if (previous_was_number) {
302
+ return {valid: false, error_number: 8, error: errors[8]};
303
+ }
304
+ sum_fields += parseInt(rows[i][k], 10);
305
+ previous_was_number = true;
306
+ } else {
307
+ if (!/^[prnbqkPRNBQK]$/.test(rows[i][k])) {
308
+ return {valid: false, error_number: 9, error: errors[9]};
309
+ }
310
+ sum_fields += 1;
311
+ previous_was_number = false;
312
+ }
313
+ }
314
+ if (sum_fields !== 8) {
315
+ return {valid: false, error_number: 10, error: errors[10]};
316
+ }
317
+ }
318
+
319
+ /* everything's okay! */
320
+ return {valid: true, error_number: 0, error: errors[0]};
321
+ }
322
+
323
+ function generate_fen() {
324
+ var empty = 0;
325
+ var fen = '';
326
+
327
+ for (var i = SQUARES.a8; i <= SQUARES.h1; i++) {
328
+ if (board[i] == null) {
329
+ empty++;
330
+ } else {
331
+ if (empty > 0) {
332
+ fen += empty;
333
+ empty = 0;
334
+ }
335
+ var color = board[i].color;
336
+ var piece = board[i].type;
337
+
338
+ fen += (color === WHITE) ?
339
+ piece.toUpperCase() : piece.toLowerCase();
340
+ }
341
+
342
+ if ((i + 1) & 0x88) {
343
+ if (empty > 0) {
344
+ fen += empty;
345
+ }
346
+
347
+ if (i !== SQUARES.h1) {
348
+ fen += '/';
349
+ }
350
+
351
+ empty = 0;
352
+ i += 8;
353
+ }
354
+ }
355
+
356
+ var cflags = '';
357
+ if (castling[WHITE] & BITS.KSIDE_CASTLE) { cflags += 'K'; }
358
+ if (castling[WHITE] & BITS.QSIDE_CASTLE) { cflags += 'Q'; }
359
+ if (castling[BLACK] & BITS.KSIDE_CASTLE) { cflags += 'k'; }
360
+ if (castling[BLACK] & BITS.QSIDE_CASTLE) { cflags += 'q'; }
361
+
362
+ /* do we have an empty castling flag? */
363
+ cflags = cflags || '-';
364
+ var epflags = (ep_square === EMPTY) ? '-' : algebraic(ep_square);
365
+
366
+ return [fen, turn, cflags, epflags, half_moves, move_number].join(' ');
367
+ }
368
+
369
+ function set_header(args) {
370
+ for (var i = 0; i < args.length; i += 2) {
371
+ if (typeof args[i] === 'string' &&
372
+ typeof args[i + 1] === 'string') {
373
+ header[args[i]] = args[i + 1];
374
+ }
375
+ }
376
+ return header;
377
+ }
378
+
379
+ /* called when the initial board setup is changed with put() or remove().
380
+ * modifies the SetUp and FEN properties of the header object. if the FEN is
381
+ * equal to the default position, the SetUp and FEN are deleted
382
+ * the setup is only updated if history.length is zero, ie moves haven't been
383
+ * made.
384
+ */
385
+ function update_setup(fen) {
386
+ if (history.length > 0) return;
387
+
388
+ if (fen !== DEFAULT_POSITION) {
389
+ //header['SetUp'] = fen;
390
+ //header['FEN'] = '1';
391
+ } else {
392
+ delete header['SetUp'];
393
+ delete header['FEN'];
394
+ }
395
+ }
396
+
397
+ function get(square) {
398
+ var piece = board[SQUARES[square]];
399
+ return (piece) ? {type: piece.type, color: piece.color} : null;
400
+ }
401
+
402
+ function put(piece, square) {
403
+ /* check for valid piece object */
404
+ if (!('type' in piece && 'color' in piece)) {
405
+ return false;
406
+ }
407
+
408
+ /* check for piece */
409
+ if (SYMBOLS.indexOf(piece.type.toLowerCase()) === -1) {
410
+ return false;
411
+ }
412
+
413
+ /* check for valid square */
414
+ if (!(square in SQUARES)) {
415
+ return false;
416
+ }
417
+
418
+ var sq = SQUARES[square];
419
+ board[sq] = {type: piece.type, color: piece.color};
420
+ if (piece.type === KING) {
421
+ kings[piece.color] = sq;
422
+ }
423
+
424
+ update_setup(generate_fen());
425
+
426
+ return true;
427
+ }
428
+
429
+ function remove(square) {
430
+ var piece = get(square);
431
+ board[SQUARES[square]] = null;
432
+ if (piece && piece.type === KING) {
433
+ kings[piece.color] = EMPTY;
434
+ }
435
+
436
+ update_setup(generate_fen());
437
+
438
+ return piece;
439
+ }
440
+
441
+ function build_move(board, from, to, flags, promotion) {
442
+ var move = {
443
+ color: turn,
444
+ from: from,
445
+ to: to,
446
+ flags: flags,
447
+ piece: board[from].type
448
+ };
449
+
450
+ if (promotion) {
451
+ move.flags |= BITS.PROMOTION;
452
+ move.promotion = promotion;
453
+ }
454
+
455
+ if (board[to]) {
456
+ move.captured = board[to].type;
457
+ } else if (flags & BITS.EP_CAPTURE) {
458
+ move.captured = PAWN;
459
+ }
460
+ return move;
461
+ }
462
+
463
+ function generate_moves(options) {
464
+ function add_move(board, moves, from, to, flags) {
465
+ /* if pawn promotion */
466
+ if (board[from].type === PAWN &&
467
+ (rank(to) === RANK_8 || rank(to) === RANK_1)) {
468
+ var pieces = [QUEEN, ROOK, BISHOP, KNIGHT];
469
+ for (var i = 0, len = pieces.length; i < len; i++) {
470
+ moves.push(build_move(board, from, to, flags, pieces[i]));
471
+ }
472
+ } else {
473
+ moves.push(build_move(board, from, to, flags));
474
+ }
475
+ }
476
+
477
+ var moves = [];
478
+ var us = turn;
479
+ var them = swap_color(us);
480
+ var second_rank = {b: RANK_7, w: RANK_2};
481
+
482
+ var first_sq = SQUARES.a8;
483
+ var last_sq = SQUARES.h1;
484
+ var single_square = false;
485
+
486
+ /* do we want legal moves? */
487
+ var legal = (typeof options !== 'undefined' && 'legal' in options) ?
488
+ options.legal : true;
489
+
490
+ /* are we generating moves for a single square? */
491
+ if (typeof options !== 'undefined' && 'square' in options) {
492
+ if (options.square in SQUARES) {
493
+ first_sq = last_sq = SQUARES[options.square];
494
+ single_square = true;
495
+ } else {
496
+ /* invalid square */
497
+ return [];
498
+ }
499
+ }
500
+
501
+ for (var i = first_sq; i <= last_sq; i++) {
502
+ /* did we run off the end of the board */
503
+ if (i & 0x88) { i += 7; continue; }
504
+
505
+ var piece = board[i];
506
+ if (piece == null || piece.color !== us) {
507
+ continue;
508
+ }
509
+
510
+ if (piece.type === PAWN) {
511
+ /* single square, non-capturing */
512
+ var square = i + PAWN_OFFSETS[us][0];
513
+ if (board[square] == null) {
514
+ add_move(board, moves, i, square, BITS.NORMAL);
515
+
516
+ /* double square */
517
+ var square = i + PAWN_OFFSETS[us][1];
518
+ if (second_rank[us] === rank(i) && board[square] == null) {
519
+ add_move(board, moves, i, square, BITS.BIG_PAWN);
520
+ }
521
+ }
522
+
523
+ /* pawn captures */
524
+ for (j = 2; j < 4; j++) {
525
+ var square = i + PAWN_OFFSETS[us][j];
526
+ if (square & 0x88) continue;
527
+
528
+ if (board[square] != null &&
529
+ board[square].color === them) {
530
+ add_move(board, moves, i, square, BITS.CAPTURE);
531
+ } else if (square === ep_square) {
532
+ add_move(board, moves, i, ep_square, BITS.EP_CAPTURE);
533
+ }
534
+ }
535
+ } else {
536
+ for (var j = 0, len = PIECE_OFFSETS[piece.type].length; j < len; j++) {
537
+ var offset = PIECE_OFFSETS[piece.type][j];
538
+ var square = i;
539
+
540
+ while (true) {
541
+ square += offset;
542
+ if (square & 0x88) break;
543
+
544
+ if (board[square] == null) {
545
+ add_move(board, moves, i, square, BITS.NORMAL);
546
+ } else {
547
+ if (board[square].color === us) break;
548
+ add_move(board, moves, i, square, BITS.CAPTURE);
549
+ break;
550
+ }
551
+
552
+ /* break, if knight or king */
553
+ if (piece.type === 'n' || piece.type === 'k') break;
554
+ }
555
+ }
556
+ }
557
+ }
558
+
559
+ /* check for castling if: a) we're generating all moves, or b) we're doing
560
+ * single square move generation on the king's square
561
+ */
562
+ if ((!single_square) || last_sq === kings[us]) {
563
+ /* king-side castling */
564
+ if (castling[us] & BITS.KSIDE_CASTLE) {
565
+ var castling_from = kings[us];
566
+ var castling_to = castling_from + 2;
567
+
568
+ if (board[castling_from + 1] == null &&
569
+ board[castling_to] == null &&
570
+ !attacked(them, kings[us]) &&
571
+ !attacked(them, castling_from + 1) &&
572
+ !attacked(them, castling_to)) {
573
+ add_move(board, moves, kings[us] , castling_to,
574
+ BITS.KSIDE_CASTLE);
575
+ }
576
+ }
577
+
578
+ /* queen-side castling */
579
+ if (castling[us] & BITS.QSIDE_CASTLE) {
580
+ var castling_from = kings[us];
581
+ var castling_to = castling_from - 2;
582
+
583
+ if (board[castling_from - 1] == null &&
584
+ board[castling_from - 2] == null &&
585
+ board[castling_from - 3] == null &&
586
+ !attacked(them, kings[us]) &&
587
+ !attacked(them, castling_from - 1) &&
588
+ !attacked(them, castling_to)) {
589
+ add_move(board, moves, kings[us], castling_to,
590
+ BITS.QSIDE_CASTLE);
591
+ }
592
+ }
593
+ }
594
+
595
+ /* return all pseudo-legal moves (this includes moves that allow the king
596
+ * to be captured)
597
+ */
598
+ if (!legal) {
599
+ return moves;
600
+ }
601
+
602
+ /* filter out illegal moves */
603
+ var legal_moves = [];
604
+ for (var i = 0, len = moves.length; i < len; i++) {
605
+ make_move(moves[i]);
606
+ if (!king_attacked(us)) {
607
+ legal_moves.push(moves[i]);
608
+ }
609
+ undo_move();
610
+ }
611
+
612
+ return legal_moves;
613
+ }
614
+
615
+ /* convert a move from 0x88 coordinates to Standard Algebraic Notation
616
+ * (SAN)
617
+ */
618
+ function move_to_san(move) {
619
+ var output = '';
620
+
621
+ if (move.flags & BITS.KSIDE_CASTLE) {
622
+ output = 'O-O';
623
+ } else if (move.flags & BITS.QSIDE_CASTLE) {
624
+ output = 'O-O-O';
625
+ } else {
626
+ var disambiguator = get_disambiguator(move);
627
+
628
+ if (move.piece !== PAWN) {
629
+ output += move.piece.toUpperCase() + disambiguator;
630
+ }
631
+
632
+ if (move.flags & (BITS.CAPTURE | BITS.EP_CAPTURE)) {
633
+ if (move.piece === PAWN) {
634
+ output += algebraic(move.from)[0];
635
+ }
636
+ output += 'x';
637
+ }
638
+
639
+ output += algebraic(move.to);
640
+
641
+ if (move.flags & BITS.PROMOTION) {
642
+ output += '=' + move.promotion.toUpperCase();
643
+ }
644
+ }
645
+
646
+ make_move(move);
647
+ if (in_check()) {
648
+ if (in_checkmate()) {
649
+ output += '#';
650
+ } else {
651
+ output += '+';
652
+ }
653
+ }
654
+ undo_move();
655
+
656
+ return output;
657
+ }
658
+
659
+ function attacked(color, square) {
660
+ for (var i = SQUARES.a8; i <= SQUARES.h1; i++) {
661
+ /* did we run off the end of the board */
662
+ if (i & 0x88) { i += 7; continue; }
663
+
664
+ /* if empty square or wrong color */
665
+ if (board[i] == null || board[i].color !== color) continue;
666
+
667
+ var piece = board[i];
668
+ var difference = i - square;
669
+ var index = difference + 119;
670
+
671
+ if (ATTACKS[index] & (1 << SHIFTS[piece.type])) {
672
+ if (piece.type === PAWN) {
673
+ if (difference > 0) {
674
+ if (piece.color === WHITE) return true;
675
+ } else {
676
+ if (piece.color === BLACK) return true;
677
+ }
678
+ continue;
679
+ }
680
+
681
+ /* if the piece is a knight or a king */
682
+ if (piece.type === 'n' || piece.type === 'k') return true;
683
+
684
+ var offset = RAYS[index];
685
+ var j = i + offset;
686
+
687
+ var blocked = false;
688
+ while (j !== square) {
689
+ if (board[j] != null) { blocked = true; break; }
690
+ j += offset;
691
+ }
692
+
693
+ if (!blocked) return true;
694
+ }
695
+ }
696
+
697
+ return false;
698
+ }
699
+
700
+ function king_attacked(color) {
701
+ return attacked(swap_color(color), kings[color]);
702
+ }
703
+
704
+ function in_check() {
705
+ return king_attacked(turn);
706
+ }
707
+
708
+ function in_checkmate() {
709
+ return in_check() && generate_moves().length === 0;
710
+ }
711
+
712
+ function in_stalemate() {
713
+ return !in_check() && generate_moves().length === 0;
714
+ }
715
+
716
+ function insufficient_material() {
717
+ var pieces = {};
718
+ var bishops = [];
719
+ var num_pieces = 0;
720
+ var sq_color = 0;
721
+
722
+ for (var i = SQUARES.a8; i<= SQUARES.h1; i++) {
723
+ sq_color = (sq_color + 1) % 2;
724
+ if (i & 0x88) { i += 7; continue; }
725
+
726
+ var piece = board[i];
727
+ if (piece) {
728
+ pieces[piece.type] = (piece.type in pieces) ?
729
+ pieces[piece.type] + 1 : 1;
730
+ if (piece.type === BISHOP) {
731
+ bishops.push(sq_color);
732
+ }
733
+ num_pieces++;
734
+ }
735
+ }
736
+
737
+ /* k vs. k */
738
+ if (num_pieces === 2) { return true; }
739
+
740
+ /* k vs. kn .... or .... k vs. kb */
741
+ else if (num_pieces === 3 && (pieces[BISHOP] === 1 ||
742
+ pieces[KNIGHT] === 1)) { return true; }
743
+
744
+ /* kb vs. kb where any number of bishops are all on the same color */
745
+ else if (num_pieces === pieces[BISHOP] + 2) {
746
+ var sum = 0;
747
+ var len = bishops.length;
748
+ for (var i = 0; i < len; i++) {
749
+ sum += bishops[i];
750
+ }
751
+ if (sum === 0 || sum === len) { return true; }
752
+ }
753
+
754
+ return false;
755
+ }
756
+
757
+ function in_threefold_repetition() {
758
+ /* TODO: while this function is fine for casual use, a better
759
+ * implementation would use a Zobrist key (instead of FEN). the
760
+ * Zobrist key would be maintained in the make_move/undo_move functions,
761
+ * avoiding the costly that we do below.
762
+ */
763
+ var moves = [];
764
+ var positions = {};
765
+ var repetition = false;
766
+
767
+ while (true) {
768
+ var move = undo_move();
769
+ if (!move) break;
770
+ moves.push(move);
771
+ }
772
+
773
+ while (true) {
774
+ /* remove the last two fields in the FEN string, they're not needed
775
+ * when checking for draw by rep */
776
+ var fen = generate_fen().split(' ').slice(0,4).join(' ');
777
+
778
+ /* has the position occurred three or move times */
779
+ positions[fen] = (fen in positions) ? positions[fen] + 1 : 1;
780
+ if (positions[fen] >= 3) {
781
+ repetition = true;
782
+ }
783
+
784
+ if (!moves.length) {
785
+ break;
786
+ }
787
+ make_move(moves.pop());
788
+ }
789
+
790
+ return repetition;
791
+ }
792
+
793
+ function push(move) {
794
+ history.push({
795
+ move: move,
796
+ kings: {b: kings.b, w: kings.w},
797
+ turn: turn,
798
+ castling: {b: castling.b, w: castling.w},
799
+ ep_square: ep_square,
800
+ half_moves: half_moves,
801
+ move_number: move_number
802
+ });
803
+ }
804
+
805
+ function make_move(move) {
806
+ var us = turn;
807
+ var them = swap_color(us);
808
+ push(move);
809
+
810
+ board[move.to] = board[move.from];
811
+ board[move.from] = null;
812
+
813
+ /* if ep capture, remove the captured pawn */
814
+ if (move.flags & BITS.EP_CAPTURE) {
815
+ if (turn === BLACK) {
816
+ board[move.to - 16] = null;
817
+ } else {
818
+ board[move.to + 16] = null;
819
+ }
820
+ }
821
+
822
+ /* if pawn promotion, replace with new piece */
823
+ if (move.flags & BITS.PROMOTION) {
824
+ board[move.to] = {type: move.promotion, color: us};
825
+ }
826
+
827
+ /* if we moved the king */
828
+ if (board[move.to].type === KING) {
829
+ kings[board[move.to].color] = move.to;
830
+
831
+ /* if we castled, move the rook next to the king */
832
+ if (move.flags & BITS.KSIDE_CASTLE) {
833
+ var castling_to = move.to - 1;
834
+ var castling_from = move.to + 1;
835
+ board[castling_to] = board[castling_from];
836
+ board[castling_from] = null;
837
+ } else if (move.flags & BITS.QSIDE_CASTLE) {
838
+ var castling_to = move.to + 1;
839
+ var castling_from = move.to - 2;
840
+ board[castling_to] = board[castling_from];
841
+ board[castling_from] = null;
842
+ }
843
+
844
+ /* turn off castling */
845
+ castling[us] = '';
846
+ }
847
+
848
+ /* turn off castling if we move a rook */
849
+ if (castling[us]) {
850
+ for (var i = 0, len = ROOKS[us].length; i < len; i++) {
851
+ if (move.from === ROOKS[us][i].square &&
852
+ castling[us] & ROOKS[us][i].flag) {
853
+ castling[us] ^= ROOKS[us][i].flag;
854
+ break;
855
+ }
856
+ }
857
+ }
858
+
859
+ /* turn off castling if we capture a rook */
860
+ if (castling[them]) {
861
+ for (var i = 0, len = ROOKS[them].length; i < len; i++) {
862
+ if (move.to === ROOKS[them][i].square &&
863
+ castling[them] & ROOKS[them][i].flag) {
864
+ castling[them] ^= ROOKS[them][i].flag;
865
+ break;
866
+ }
867
+ }
868
+ }
869
+
870
+ /* if big pawn move, update the en passant square */
871
+ if (move.flags & BITS.BIG_PAWN) {
872
+ if (turn === 'b') {
873
+ ep_square = move.to - 16;
874
+ } else {
875
+ ep_square = move.to + 16;
876
+ }
877
+ } else {
878
+ ep_square = EMPTY;
879
+ }
880
+
881
+ /* reset the 50 move counter if a pawn is moved or a piece is captured */
882
+ if (move.piece === PAWN) {
883
+ half_moves = 0;
884
+ } else if (move.flags & (BITS.CAPTURE | BITS.EP_CAPTURE)) {
885
+ half_moves = 0;
886
+ } else {
887
+ half_moves++;
888
+ }
889
+
890
+ if (turn === BLACK) {
891
+ move_number++;
892
+ }
893
+ turn = swap_color(turn);
894
+ }
895
+
896
+ function undo_move() {
897
+ var old = history.pop();
898
+ if (old == null) { return null; }
899
+
900
+ var move = old.move;
901
+ kings = old.kings;
902
+ turn = old.turn;
903
+ castling = old.castling;
904
+ ep_square = old.ep_square;
905
+ half_moves = old.half_moves;
906
+ move_number = old.move_number;
907
+
908
+ var us = turn;
909
+ var them = swap_color(turn);
910
+
911
+ board[move.from] = board[move.to];
912
+ board[move.from].type = move.piece; // to undo any promotions
913
+ board[move.to] = null;
914
+
915
+ if (move.flags & BITS.CAPTURE) {
916
+ board[move.to] = {type: move.captured, color: them};
917
+ } else if (move.flags & BITS.EP_CAPTURE) {
918
+ var index;
919
+ if (us === BLACK) {
920
+ index = move.to - 16;
921
+ } else {
922
+ index = move.to + 16;
923
+ }
924
+ board[index] = {type: PAWN, color: them};
925
+ }
926
+
927
+
928
+ if (move.flags & (BITS.KSIDE_CASTLE | BITS.QSIDE_CASTLE)) {
929
+ var castling_to, castling_from;
930
+ if (move.flags & BITS.KSIDE_CASTLE) {
931
+ castling_to = move.to + 1;
932
+ castling_from = move.to - 1;
933
+ } else if (move.flags & BITS.QSIDE_CASTLE) {
934
+ castling_to = move.to - 2;
935
+ castling_from = move.to + 1;
936
+ }
937
+
938
+ board[castling_to] = board[castling_from];
939
+ board[castling_from] = null;
940
+ }
941
+
942
+ return move;
943
+ }
944
+
945
+ /* this function is used to uniquely identify ambiguous moves */
946
+ function get_disambiguator(move) {
947
+ var moves = generate_moves();
948
+
949
+ var from = move.from;
950
+ var to = move.to;
951
+ var piece = move.piece;
952
+
953
+ var ambiguities = 0;
954
+ var same_rank = 0;
955
+ var same_file = 0;
956
+
957
+ for (var i = 0, len = moves.length; i < len; i++) {
958
+ var ambig_from = moves[i].from;
959
+ var ambig_to = moves[i].to;
960
+ var ambig_piece = moves[i].piece;
961
+
962
+ /* if a move of the same piece type ends on the same to square, we'll
963
+ * need to add a disambiguator to the algebraic notation
964
+ */
965
+ if (piece === ambig_piece && from !== ambig_from && to === ambig_to) {
966
+ ambiguities++;
967
+
968
+ if (rank(from) === rank(ambig_from)) {
969
+ same_rank++;
970
+ }
971
+
972
+ if (file(from) === file(ambig_from)) {
973
+ same_file++;
974
+ }
975
+ }
976
+ }
977
+
978
+ if (ambiguities > 0) {
979
+ /* if there exists a similar moving piece on the same rank and file as
980
+ * the move in question, use the square as the disambiguator
981
+ */
982
+ if (same_rank > 0 && same_file > 0) {
983
+ return algebraic(from);
984
+ }
985
+ /* if the moving piece rests on the same file, use the rank symbol as the
986
+ * disambiguator
987
+ */
988
+ else if (same_file > 0) {
989
+ return algebraic(from).charAt(1);
990
+ }
991
+ /* else use the file symbol */
992
+ else {
993
+ return algebraic(from).charAt(0);
994
+ }
995
+ }
996
+
997
+ return '';
998
+ }
999
+
1000
+ function ascii() {
1001
+ var s = ' +------------------------+\n';
1002
+ for (var i = SQUARES.a8; i <= SQUARES.h1; i++) {
1003
+ /* display the rank */
1004
+ if (file(i) === 0) {
1005
+ s += ' ' + '87654321'[rank(i)] + ' |';
1006
+ }
1007
+
1008
+ /* empty piece */
1009
+ if (board[i] == null) {
1010
+ s += ' . ';
1011
+ } else {
1012
+ var piece = board[i].type;
1013
+ var color = board[i].color;
1014
+ var symbol = (color === WHITE) ?
1015
+ piece.toUpperCase() : piece.toLowerCase();
1016
+ s += ' ' + symbol + ' ';
1017
+ }
1018
+
1019
+ if ((i + 1) & 0x88) {
1020
+ s += '|\n';
1021
+ i += 8;
1022
+ }
1023
+ }
1024
+ s += ' +------------------------+\n';
1025
+ s += ' a b c d e f g h\n';
1026
+
1027
+ return s;
1028
+ }
1029
+
1030
+ /*****************************************************************************
1031
+ * UTILITY FUNCTIONS
1032
+ ****************************************************************************/
1033
+ function rank(i) {
1034
+ return i >> 4;
1035
+ }
1036
+
1037
+ function file(i) {
1038
+ return i & 15;
1039
+ }
1040
+
1041
+ function algebraic(i){
1042
+ var f = file(i), r = rank(i);
1043
+ return 'abcdefgh'.substring(f,f+1) + '87654321'.substring(r,r+1);
1044
+ }
1045
+
1046
+ function swap_color(c) {
1047
+ return c === WHITE ? BLACK : WHITE;
1048
+ }
1049
+
1050
+ function is_digit(c) {
1051
+ return '0123456789'.indexOf(c) !== -1;
1052
+ }
1053
+
1054
+ /* pretty = external move object */
1055
+ function make_pretty(ugly_move) {
1056
+ var move = clone(ugly_move);
1057
+ move.san = move_to_san(move);
1058
+ move.to = algebraic(move.to);
1059
+ move.from = algebraic(move.from);
1060
+
1061
+ var flags = '';
1062
+
1063
+ for (var flag in BITS) {
1064
+ if (BITS[flag] & move.flags) {
1065
+ flags += FLAGS[flag];
1066
+ }
1067
+ }
1068
+ move.flags = flags;
1069
+
1070
+ return move;
1071
+ }
1072
+
1073
+ function clone(obj) {
1074
+ var dupe = (obj instanceof Array) ? [] : {};
1075
+
1076
+ for (var property in obj) {
1077
+ if (typeof property === 'object') {
1078
+ dupe[property] = clone(obj[property]);
1079
+ } else {
1080
+ dupe[property] = obj[property];
1081
+ }
1082
+ }
1083
+
1084
+ return dupe;
1085
+ }
1086
+
1087
+ function trim(str) {
1088
+ return str.replace(/^\s+|\s+$/g, '');
1089
+ }
1090
+
1091
+ /*****************************************************************************
1092
+ * DEBUGGING UTILITIES
1093
+ ****************************************************************************/
1094
+ function perft(depth) {
1095
+ var moves = generate_moves({legal: false});
1096
+ var nodes = 0;
1097
+ var color = turn;
1098
+
1099
+ for (var i = 0, len = moves.length; i < len; i++) {
1100
+ make_move(moves[i]);
1101
+ if (!king_attacked(color)) {
1102
+ if (depth - 1 > 0) {
1103
+ var child_nodes = perft(depth - 1);
1104
+ nodes += child_nodes;
1105
+ } else {
1106
+ nodes++;
1107
+ }
1108
+ }
1109
+ undo_move();
1110
+ }
1111
+
1112
+ return nodes;
1113
+ }
1114
+
1115
+ return {
1116
+ /***************************************************************************
1117
+ * PUBLIC CONSTANTS (is there a better way to do this?)
1118
+ **************************************************************************/
1119
+ WHITE: WHITE,
1120
+ BLACK: BLACK,
1121
+ PAWN: PAWN,
1122
+ KNIGHT: KNIGHT,
1123
+ BISHOP: BISHOP,
1124
+ ROOK: ROOK,
1125
+ QUEEN: QUEEN,
1126
+ KING: KING,
1127
+ SQUARES: (function() {
1128
+ /* from the ECMA-262 spec (section 12.6.4):
1129
+ * "The mechanics of enumerating the properties ... is
1130
+ * implementation dependent"
1131
+ * so: for (var sq in SQUARES) { keys.push(sq); } might not be
1132
+ * ordered correctly
1133
+ */
1134
+ var keys = [];
1135
+ for (var i = SQUARES.a8; i <= SQUARES.h1; i++) {
1136
+ if (i & 0x88) { i += 7; continue; }
1137
+ keys.push(algebraic(i));
1138
+ }
1139
+ return keys;
1140
+ })(),
1141
+ FLAGS: FLAGS,
1142
+
1143
+ /***************************************************************************
1144
+ * PUBLIC API
1145
+ **************************************************************************/
1146
+ load: function(fen) {
1147
+ return load(fen);
1148
+ },
1149
+
1150
+ reset: function() {
1151
+ return reset();
1152
+ },
1153
+
1154
+ moves: function(options) {
1155
+ /* The internal representation of a chess move is in 0x88 format, and
1156
+ * not meant to be human-readable. The code below converts the 0x88
1157
+ * square coordinates to algebraic coordinates. It also prunes an
1158
+ * unnecessary move keys resulting from a verbose call.
1159
+ */
1160
+
1161
+ var ugly_moves = generate_moves(options);
1162
+ var moves = [];
1163
+
1164
+ for (var i = 0, len = ugly_moves.length; i < len; i++) {
1165
+
1166
+ /* does the user want a full move object (most likely not), or just
1167
+ * SAN
1168
+ */
1169
+ if (typeof options !== 'undefined' && 'verbose' in options &&
1170
+ options.verbose) {
1171
+ moves.push(make_pretty(ugly_moves[i]));
1172
+ } else {
1173
+ moves.push(move_to_san(ugly_moves[i]));
1174
+ }
1175
+ }
1176
+
1177
+ return moves;
1178
+ },
1179
+
1180
+ in_check: function() {
1181
+ return in_check();
1182
+ },
1183
+
1184
+ in_checkmate: function() {
1185
+ return in_checkmate();
1186
+ },
1187
+
1188
+ in_stalemate: function() {
1189
+ return in_stalemate();
1190
+ },
1191
+
1192
+ in_draw: function() {
1193
+ return half_moves >= 100 ||
1194
+ in_stalemate() ||
1195
+ insufficient_material() ||
1196
+ in_threefold_repetition();
1197
+ },
1198
+
1199
+ insufficient_material: function() {
1200
+ return insufficient_material();
1201
+ },
1202
+
1203
+ in_threefold_repetition: function() {
1204
+ return in_threefold_repetition();
1205
+ },
1206
+
1207
+ game_over: function() {
1208
+ return half_moves >= 100 ||
1209
+ in_checkmate() ||
1210
+ in_stalemate() ||
1211
+ insufficient_material() ||
1212
+ in_threefold_repetition();
1213
+ },
1214
+
1215
+ validate_fen: function(fen) {
1216
+ return validate_fen(fen);
1217
+ },
1218
+
1219
+ fen: function() {
1220
+ return generate_fen();
1221
+ },
1222
+
1223
+ pgn: function(options) {
1224
+ /* using the specification from http://www.chessclub.com/help/PGN-spec
1225
+ * example for html usage: .pgn({ max_width: 72, newline_char: "<br />" })
1226
+ */
1227
+ var newline = (typeof options === 'object' &&
1228
+ typeof options.newline_char === 'string') ?
1229
+ options.newline_char : '\n';
1230
+ var max_width = (typeof options === 'object' &&
1231
+ typeof options.max_width === 'number') ?
1232
+ options.max_width : 0;
1233
+ var result = [];
1234
+ var header_exists = false;
1235
+
1236
+ /* add the PGN header headerrmation */
1237
+ for (var i in header) {
1238
+ /* TODO: order of enumerated properties in header object is not
1239
+ * guaranteed, see ECMA-262 spec (section 12.6.4)
1240
+ */
1241
+ result.push('[' + i + ' \"' + header[i] + '\"]' + newline);
1242
+ header_exists = true;
1243
+ }
1244
+
1245
+ if (header_exists && history.length) {
1246
+ result.push(newline);
1247
+ }
1248
+
1249
+ /* pop all of history onto reversed_history */
1250
+ var reversed_history = [];
1251
+ while (history.length > 0) {
1252
+ reversed_history.push(undo_move());
1253
+ }
1254
+
1255
+ var moves = [];
1256
+ var move_string = '';
1257
+ var pgn_move_number = 1;
1258
+
1259
+ /* build the list of moves. a move_string looks like: "3. e3 e6" */
1260
+ while (reversed_history.length > 0) {
1261
+ var move = reversed_history.pop();
1262
+
1263
+ /* if the position started with black to move, start PGN with 1. ... */
1264
+ if (pgn_move_number === 1 && move.color === 'b') {
1265
+ move_string = '1. ...';
1266
+ pgn_move_number++;
1267
+ } else if (move.color === 'w') {
1268
+ /* store the previous generated move_string if we have one */
1269
+ if (move_string.length) {
1270
+ moves.push(move_string);
1271
+ }
1272
+ move_string = pgn_move_number + '.';
1273
+ pgn_move_number++;
1274
+ }
1275
+
1276
+ move_string = move_string + ' ' + move_to_san(move);
1277
+ make_move(move);
1278
+ }
1279
+
1280
+ /* are there any other leftover moves? */
1281
+ if (move_string.length) {
1282
+ moves.push(move_string);
1283
+ }
1284
+
1285
+ /* is there a result? */
1286
+ if (typeof header.Result !== 'undefined') {
1287
+ moves.push(header.Result);
1288
+ }
1289
+
1290
+ /* history should be back to what is was before we started generating PGN,
1291
+ * so join together moves
1292
+ */
1293
+ if (max_width === 0) {
1294
+ return result.join('') + moves.join(' ');
1295
+ }
1296
+
1297
+ /* wrap the PGN output at max_width */
1298
+ var current_width = 0;
1299
+ for (var i = 0; i < moves.length; i++) {
1300
+ /* if the current move will push past max_width */
1301
+ if (current_width + moves[i].length > max_width && i !== 0) {
1302
+
1303
+ /* don't end the line with whitespace */
1304
+ if (result[result.length - 1] === ' ') {
1305
+ result.pop();
1306
+ }
1307
+
1308
+ result.push(newline);
1309
+ current_width = 0;
1310
+ } else if (i !== 0) {
1311
+ result.push(' ');
1312
+ current_width++;
1313
+ }
1314
+ result.push(moves[i]);
1315
+ current_width += moves[i].length;
1316
+ }
1317
+
1318
+ return result.join('');
1319
+ },
1320
+
1321
+ load_pgn: function(pgn, options) {
1322
+ function mask(str) {
1323
+ return str.replace(/\n/g, '\\n').replace(/\r/g, '\\r');
1324
+ }
1325
+
1326
+ /* convert a move from Standard Algebraic Notation (SAN) to 0x88
1327
+ * coordinates
1328
+ */
1329
+ function move_from_san(move) {
1330
+ var to, from, flags = BITS.NORMAL, promotion;
1331
+ var parse = move.match(/^([NBKRQ])?([abcdefgh12345678][12345678]?)?(x)?([abcdefgh][12345678])(=?[NBRQ])?/);
1332
+ if (move.slice(0, 5) === 'O-O-O') {
1333
+ from = kings[turn];
1334
+ to = from - 2;
1335
+ flags = BITS.QSIDE_CASTLE;
1336
+ } else if (move.slice(0, 3) === 'O-O') {
1337
+ from = kings[turn];
1338
+ to = from + 2;
1339
+ flags = BITS.KSIDE_CASTLE;
1340
+ } else if (parse && parse[1]) {
1341
+ // regular moves
1342
+ var piece = parse[1].toLowerCase();
1343
+ if (parse[3]) {
1344
+ // capture
1345
+ flags = BITS.CAPTURE;
1346
+ }
1347
+ to = SQUARES[parse[4]];
1348
+ for (var j = 0, len = PIECE_OFFSETS[piece].length; j < len; j++) {
1349
+ var offset = PIECE_OFFSETS[piece][j];
1350
+ var square = to;
1351
+
1352
+ while (true) {
1353
+ square += offset;
1354
+ if (square & 0x88) break;
1355
+
1356
+ var b = board[square];
1357
+ if (b) {
1358
+ if (b.color === turn && b.type === piece && (!parse[2] || algebraic(square).indexOf(parse[2]) >= 0)) {
1359
+ from = square;
1360
+ }
1361
+ break;
1362
+ }
1363
+
1364
+ /* break, if knight or king */
1365
+ if (piece === 'n' || piece === 'k') break;
1366
+ }
1367
+ }
1368
+ } else if (parse) {
1369
+ // pawn move
1370
+ if (parse[3]) {
1371
+ // capture
1372
+ to = SQUARES[parse[4]];
1373
+ for (var j = 2; j < 4; j++) {
1374
+ var square = to - PAWN_OFFSETS[turn][j];
1375
+ if (square & 0x88) continue;
1376
+
1377
+ if (board[square] != null &&
1378
+ board[square].color === turn &&
1379
+ algebraic(square)[0] === parse[2]) {
1380
+ from = square;
1381
+ }
1382
+ }
1383
+ if (board[to]) {
1384
+ flags = BITS.CAPTURE;
1385
+ } else {
1386
+ flags = BITS.EP_CAPTURE;
1387
+ }
1388
+ } else {
1389
+ // normal move
1390
+ to = SQUARES[move.slice(0,2)];
1391
+ var c = to - PAWN_OFFSETS[turn][0],
1392
+ b = board[c];
1393
+ if (b && b.type === PAWN && b.color === turn) {
1394
+ from = c;
1395
+ } else {
1396
+ c = to - PAWN_OFFSETS[turn][1];
1397
+ b = board[c];
1398
+ if (b && b.type === PAWN && b.color === turn) {
1399
+ from = c;
1400
+ flags = BITS.BIG_PAWN;
1401
+ }
1402
+ }
1403
+ }
1404
+ // promotion?
1405
+ if (parse[5]) {
1406
+ if(typeof parse[5][1] == 'undefined') {
1407
+ promotion = parse[5][0].toLowerCase();
1408
+ } else {
1409
+ promotion = parse[5][1].toLowerCase();
1410
+ }
1411
+ }
1412
+ }
1413
+ if (from >=0 && to >=0 && flags) {
1414
+ return build_move(board, from, to, flags, promotion);
1415
+ } else if (move.length > 0) {
1416
+ /* alert(move); // error in PGN, or in parsing. */
1417
+ }
1418
+ }
1419
+
1420
+ function get_move_obj(move) {
1421
+ return move_from_san(trim(move));
1422
+ }
1423
+
1424
+ function has_keys(object) {
1425
+ var has_keys = false;
1426
+ for (var key in object) {
1427
+ has_keys = true;
1428
+ }
1429
+ return has_keys;
1430
+ }
1431
+
1432
+ function parse_pgn_header(header, options) {
1433
+ var newline_char = (typeof options === 'object' &&
1434
+ typeof options.newline_char === 'string') ?
1435
+ options.newline_char : '\r?\n';
1436
+ var header_obj = {};
1437
+ var headers = header.split(newline_char);
1438
+ var key = '';
1439
+ var value = '';
1440
+
1441
+ for (var i = 0; i < headers.length; i++) {
1442
+ key = headers[i].replace(/^\[([A-Z][A-Za-z]*)\s.*\]$/, '$1');
1443
+ value = headers[i].replace(/^\[[A-Za-z]+\s"(.*)"\]$/, '$1');
1444
+ if (trim(key).length > 0) {
1445
+ header_obj[key] = value;
1446
+ }
1447
+ }
1448
+
1449
+ return header_obj;
1450
+ }
1451
+
1452
+ var newline_char = (typeof options === 'object' &&
1453
+ typeof options.newline_char === 'string') ?
1454
+ options.newline_char : '\r?\n';
1455
+ var regex = new RegExp('^(\\[(.|' + mask(newline_char) + ')*\\])' +
1456
+ '(' + mask(newline_char) + ')*' +
1457
+ '1.(' + mask(newline_char) + '|.)*$', 'g');
1458
+
1459
+ /* get header part of the PGN file */
1460
+ var header_string = pgn.replace(regex, '$1');
1461
+
1462
+ /* no info part given, begins with moves */
1463
+ if (header_string[0] !== '[') {
1464
+ header_string = '';
1465
+ }
1466
+
1467
+ reset();
1468
+
1469
+ /* parse PGN header */
1470
+ var headers = parse_pgn_header(header_string, options);
1471
+ for (var key in headers) {
1472
+ set_header([key, headers[key]]);
1473
+ }
1474
+
1475
+ /* delete header to get the moves */
1476
+ var ms = pgn.replace(header_string, '').replace(new RegExp(mask(newline_char), 'g'), ' ');
1477
+
1478
+ /* delete comments */
1479
+ ms = ms.replace(/(\{[^}]+\})+?/g, '');
1480
+
1481
+ /* delete move numbers */
1482
+ ms = ms.replace(/\d+\./g, '');
1483
+
1484
+
1485
+ /* trim and get array of moves */
1486
+ var moves = trim(ms).split(new RegExp(/\s+/));
1487
+
1488
+ /* delete empty entries */
1489
+ moves = moves.join(',').replace(/,,+/g, ',').split(',');
1490
+ var move = '';
1491
+
1492
+ for (var half_move = 0; half_move < moves.length - 1; half_move++) {
1493
+ move = get_move_obj(moves[half_move]);
1494
+
1495
+ /* move not possible! (don't clear the board to examine to show the
1496
+ * latest valid position)
1497
+ */
1498
+ if (move == null) {
1499
+ return false;
1500
+ } else {
1501
+ make_move(move);
1502
+ }
1503
+ }
1504
+
1505
+ /* examine last move */
1506
+ move = moves[moves.length - 1];
1507
+ if (POSSIBLE_RESULTS.indexOf(move) > -1) {
1508
+ if (has_keys(header) && typeof header.Result === 'undefined') {
1509
+ set_header(['Result', move]);
1510
+ }
1511
+ }
1512
+ else {
1513
+ move = get_move_obj(move);
1514
+ if (move == null) {
1515
+ return false;
1516
+ } else {
1517
+ make_move(move);
1518
+ }
1519
+ }
1520
+ return true;
1521
+ },
1522
+
1523
+ header: function() {
1524
+ return set_header(arguments);
1525
+ },
1526
+
1527
+ ascii: function() {
1528
+ return ascii();
1529
+ },
1530
+
1531
+ turn: function() {
1532
+ return turn;
1533
+ },
1534
+
1535
+ move: function(move) {
1536
+ /* The move function can be called with in the following parameters:
1537
+ *
1538
+ * .move('Nxb7') <- where 'move' is a case-sensitive SAN string
1539
+ *
1540
+ * .move({ from: 'h7', <- where the 'move' is a move object (additional
1541
+ * to :'h8', fields are ignored)
1542
+ * promotion: 'q',
1543
+ * })
1544
+ */
1545
+ var move_obj = null;
1546
+ var moves = generate_moves();
1547
+
1548
+ if (typeof move === 'string') {
1549
+ /* convert the move string to a move object */
1550
+ for (var i = 0, len = moves.length; i < len; i++) {
1551
+ if (move === move_to_san(moves[i])) {
1552
+ move_obj = moves[i];
1553
+ break;
1554
+ }
1555
+ }
1556
+ } else if (typeof move === 'object') {
1557
+ /* convert the pretty move object to an ugly move object */
1558
+ for (var i = 0, len = moves.length; i < len; i++) {
1559
+ if (move.from === algebraic(moves[i].from) &&
1560
+ move.to === algebraic(moves[i].to) &&
1561
+ (!('promotion' in moves[i]) ||
1562
+ move.promotion === moves[i].promotion)) {
1563
+ move_obj = moves[i];
1564
+ break;
1565
+ }
1566
+ }
1567
+ }
1568
+
1569
+ /* failed to find move */
1570
+ if (!move_obj) {
1571
+ return null;
1572
+ }
1573
+
1574
+ /* need to make a copy of move because we can't generate SAN after the
1575
+ * move is made
1576
+ */
1577
+ var pretty_move = make_pretty(move_obj);
1578
+
1579
+ make_move(move_obj);
1580
+
1581
+ return pretty_move;
1582
+ },
1583
+
1584
+ undo: function() {
1585
+ var move = undo_move();
1586
+ return (move) ? make_pretty(move) : null;
1587
+ },
1588
+
1589
+ clear: function() {
1590
+ return clear();
1591
+ },
1592
+
1593
+ put: function(piece, square) {
1594
+ return put(piece, square);
1595
+ },
1596
+
1597
+ get: function(square) {
1598
+ return get(square);
1599
+ },
1600
+
1601
+ remove: function(square) {
1602
+ return remove(square);
1603
+ },
1604
+
1605
+ perft: function(depth) {
1606
+ return perft(depth);
1607
+ },
1608
+
1609
+ square_color: function(square) {
1610
+ if (square in SQUARES) {
1611
+ var sq_0x88 = SQUARES[square];
1612
+ return ((rank(sq_0x88) + file(sq_0x88)) % 2 === 0) ? 'light' : 'dark';
1613
+ }
1614
+
1615
+ return null;
1616
+ },
1617
+
1618
+ history: function(options) {
1619
+ var reversed_history = [];
1620
+ var move_history = [];
1621
+ var verbose = (typeof options !== 'undefined' && 'verbose' in options &&
1622
+ options.verbose);
1623
+
1624
+ while (history.length > 0) {
1625
+ reversed_history.push(undo_move());
1626
+ }
1627
+
1628
+ while (reversed_history.length > 0) {
1629
+ var move = reversed_history.pop();
1630
+ if (verbose) {
1631
+ move_history.push(make_pretty(move));
1632
+ } else {
1633
+ move_history.push(move_to_san(move));
1634
+ }
1635
+ make_move(move);
1636
+ }
1637
+
1638
+ return move_history;
1639
+ }
1640
+
1641
+ };
1642
+ };
1643
+
1644
+ /* export Chess object if using node or any other CommonJS compatible
1645
+ * environment */
1646
+ if (typeof exports !== 'undefined') exports.Chess = Chess;