├── .gitignore ├── .DS_Store ├── .idea ├── misc.xml ├── scopes │ └── scope_settings.xml ├── xcode.xml ├── encodings.xml ├── vcs.xml ├── modules.xml ├── ReactChess.iml ├── runConfigurations │ └── ReactChess.xml └── workspace.xml ├── ReactChess.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── clairesarsam.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcuserdata │ └── clairesarsam.xcuserdatad │ │ ├── xcschemes │ │ └── xcschememanagement.plist │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist ├── xcshareddata │ └── xcschemes │ │ └── ReactChess.xcscheme └── project.pbxproj ├── package.json ├── iOS ├── AppDelegate.h ├── main.m ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── AppDelegate.m └── Base.lproj │ └── LaunchScreen.xib ├── javascript ├── square.ios.js ├── constants.ios.js ├── piece.ios.js ├── board.ios.js └── chess.js └── index.ios.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csarsam/ReactNativeChess/HEAD/.DS_Store -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/xcode.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ReactChess.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ReactChess.xcodeproj/project.xcworkspace/xcuserdata/clairesarsam.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csarsam/ReactNativeChess/HEAD/ReactChess.xcodeproj/project.xcworkspace/xcuserdata/clairesarsam.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ReactChess", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node_modules/react-native/packager/packager.sh" 7 | }, 8 | "dependencies": { 9 | "react-native": "^0.1.0", 10 | "chess.js": "0.1.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iOS/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /iOS/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ReactChess.xcodeproj/xcuserdata/clairesarsam.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ReactChess.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 13B07F861A680F5B00A75B9A 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/ReactChess.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /iOS/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /.idea/runConfigurations/ReactChess.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ReactChess.xcodeproj/xcuserdata/clairesarsam.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /javascript/square.ios.js: -------------------------------------------------------------------------------- 1 | const React = require('react-native'); 2 | const { 3 | AppRegistry, 4 | StyleSheet, 5 | Text, 6 | TouchableHighlight, 7 | View 8 | } = React; 9 | const CONSTANTS = require('./constants.ios'); 10 | 11 | const Square = React.createClass({ 12 | render() { 13 | const color = this.props.column % 2 === 1 ? 14 | (this.props.row % 2 === 1 ? '#464646' : '#7F7E7E') : 15 | (this.props.row % 2 === 1 ? '#7F7E7E' : '#464646'); 16 | 17 | const styles = { 18 | backgroundColor: this.props.selected ? '#376060' : color, 19 | width: 375/8, 20 | height: 375/8 21 | }; 22 | 23 | const onPress = () => { 24 | this.props.onSquareSelect(this.props.row, this.props.column); 25 | }; 26 | 27 | if (this.props.selectable) { 28 | return ( 29 | 30 | 31 | 32 | 33 | ); 34 | } 35 | 36 | else 37 | return ( 38 | 39 | 40 | ); 41 | } 42 | }); 43 | 44 | module.exports = Square; 45 | -------------------------------------------------------------------------------- /iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /iOS/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import "RCTRootView.h" 13 | 14 | @implementation AppDelegate 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 17 | { 18 | NSURL *jsCodeLocation; 19 | 20 | // Loading JavaScript code - uncomment the one you want. 21 | 22 | // OPTION 1 23 | // Load from development server. Start the server from the repository root: 24 | // 25 | // $ npm start 26 | // 27 | // To run on device, change `localhost` to the IP address of your computer, and make sure your computer and 28 | // iOS device are on the same Wi-Fi network. 29 | jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle"]; 30 | 31 | // OPTION 2 32 | // Load from pre-bundled file on disk. To re-generate the static bundle, run 33 | // 34 | // $ curl http://localhost:8081/index.ios.bundle -o main.jsbundle 35 | // 36 | // and uncomment the next following line 37 | // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 38 | 39 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 40 | moduleName:@"ReactChess" 41 | launchOptions:launchOptions]; 42 | 43 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 44 | UIViewController *rootViewController = [[UIViewController alloc] init]; 45 | rootViewController.view = rootView; 46 | self.window.rootViewController = rootViewController; 47 | [self.window makeKeyAndVisible]; 48 | return YES; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /index.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var React = require('react-native'); 3 | var { 4 | AppRegistry, 5 | StyleSheet, 6 | Text, 7 | View, 8 | Animation, 9 | } = React; 10 | 11 | var Chess = require('./javascript/chess'); 12 | var Board = require('./javascript/board.ios'); 13 | var CONSTANTS = require('./javascript/constants.ios'); 14 | 15 | var game; 16 | 17 | var ReactChess = React.createClass({ 18 | getInitialState() { 19 | return { 20 | turn: CONSTANTS.WHITE 21 | }; 22 | }, 23 | 24 | render() { 25 | var history = game.history({ verbose: true }); 26 | return ( 27 | 28 | 29 | {game.game_over() ? 30 | game.in_checkmate() ? 31 | this.state.turn === CONSTANTS.WHITE ? 32 | 'White is in checkmate' : 'Black is in checkmate' : 33 | `The game has ended in ${game.in_checkmate()}` : 34 | this.state.turn === CONSTANTS.WHITE ? 'White moves' : 'Black moves'} 35 | 36 | 37 | 38 | 39 | 40 | {history.length > 0 ? 41 | `${history[history.length - 1].to} => ${history[history.length - 1].from}` : ''} 42 | 43 | 44 | ); 45 | }, 46 | 47 | turnComplete() { 48 | this.setState({ turn: game.turn() === 'b' ? CONSTANTS.BLACK : CONSTANTS.WHITE }); 49 | }, 50 | 51 | componentWillMount() { 52 | game = new Chess(); 53 | }, 54 | 55 | componentDidMount() { 56 | setTimeout(() => { 57 | Animation.startAnimation(this.refs['this'], 300, 0, 'easeInOutQuad', {opacity: 1}); 58 | }, 0); 59 | } 60 | }); 61 | 62 | var styles = StyleSheet.create({ 63 | container: { 64 | flex: 1, 65 | opacity: 0, 66 | justifyContent: 'center', 67 | alignItems: 'center', 68 | backgroundColor: '#F5FCFF', 69 | }, 70 | turn: { 71 | fontSize: 30, 72 | position: 'absolute', 73 | width: 375, 74 | textAlign: 'center', 75 | top: 50 76 | }, 77 | history: { 78 | fontSize: 20, 79 | position: 'absolute', 80 | textAlign: 'center', 81 | width: 375, 82 | top: 550 83 | } 84 | }); 85 | 86 | AppRegistry.registerComponent('ReactChess', () => ReactChess); 87 | -------------------------------------------------------------------------------- /javascript/constants.ios.js: -------------------------------------------------------------------------------- 1 | const COLUMNS = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; 2 | const ROWS = [8, 7, 6, 5, 4, 3, 2, 1]; 3 | 4 | const PIECES = ['p', 'b', 'c', 'n', 'k', 'q']; 5 | const PAWN = 'p', BISHOP = 'b', CASTLE = 'c', KNIGHT = 'n', KING = 'k', QUEEN = 'q'; 6 | const BLACK = '#000000'; 7 | const WHITE = '#FFFFFF'; 8 | 9 | const initialBoard = [ 10 | [ 11 | { piece: CASTLE, color: WHITE, key: 0 }, 12 | { piece: KNIGHT, color: WHITE, key: 1 }, 13 | { piece: BISHOP, color: WHITE, key: 2 }, 14 | { piece: QUEEN, color: WHITE, key: 3 }, 15 | { piece: KING, color: WHITE, key: 4 }, 16 | { piece: BISHOP, color: WHITE, key: 5 }, 17 | { piece: KNIGHT, color: WHITE, key: 6 }, 18 | { piece: CASTLE, color: WHITE, key: 7 } 19 | ], 20 | [ 21 | { piece: PAWN, color: WHITE, key: 8 }, 22 | { piece: PAWN, color: WHITE, key: 9 }, 23 | { piece: PAWN, color: WHITE, key: 10 }, 24 | { piece: PAWN, color: WHITE, key: 11 }, 25 | { piece: PAWN, color: WHITE, key: 12 }, 26 | { piece: PAWN, color: WHITE, key: 13 }, 27 | { piece: PAWN, color: WHITE, key: 14 }, 28 | { piece: PAWN, color: WHITE, key: 15 } 29 | ], 30 | [ 31 | null, 32 | null, 33 | null, 34 | null, 35 | null, 36 | null, 37 | null, 38 | null 39 | ], 40 | [ 41 | null, 42 | null, 43 | null, 44 | null, 45 | null, 46 | null, 47 | null, 48 | null 49 | ], 50 | [ 51 | null, 52 | null, 53 | null, 54 | null, 55 | null, 56 | null, 57 | null, 58 | null 59 | ], 60 | [ 61 | null, 62 | null, 63 | null, 64 | null, 65 | null, 66 | null, 67 | null, 68 | null 69 | ], 70 | [ 71 | { piece: PAWN, color: BLACK, key: 16 }, 72 | { piece: PAWN, color: BLACK, key: 17 }, 73 | { piece: PAWN, color: BLACK, key: 18 }, 74 | { piece: PAWN, color: BLACK, key: 19 }, 75 | { piece: PAWN, color: BLACK, key: 20 }, 76 | { piece: PAWN, color: BLACK, key: 21 }, 77 | { piece: PAWN, color: BLACK, key: 22 }, 78 | { piece: PAWN, color: BLACK, key: 23 } 79 | ], 80 | [ 81 | { piece: CASTLE, color: BLACK, key: 24 }, 82 | { piece: KNIGHT, color: BLACK, key: 25 }, 83 | { piece: BISHOP, color: BLACK, key: 26 }, 84 | { piece: QUEEN, color: BLACK, key: 27 }, 85 | { piece: KING, color: BLACK, key: 28 }, 86 | { piece: BISHOP, color: BLACK, key: 29 }, 87 | { piece: KNIGHT, color: BLACK, key: 30 }, 88 | { piece: CASTLE, color: BLACK, key: 31 } 89 | ] 90 | ]; 91 | 92 | module.exports = { 93 | PAWN: PAWN, 94 | CASTLE: CASTLE, 95 | KNIGHT: KNIGHT, 96 | QUEEN: QUEEN, 97 | KING: KING, 98 | BISHOP: BISHOP, 99 | COLUMNS: COLUMNS, 100 | ROWS: ROWS, 101 | PIECES: PIECES, 102 | BLACK: BLACK, 103 | WHITE: WHITE, 104 | initialBoard: initialBoard 105 | }; -------------------------------------------------------------------------------- /javascript/piece.ios.js: -------------------------------------------------------------------------------- 1 | const React = require('react-native'); 2 | const { 3 | AppRegistry, 4 | StyleSheet, 5 | Text, 6 | TouchableHighlight, 7 | Animation, 8 | View 9 | } = React; 10 | const CONSTANTS = require('./constants.ios'); 11 | 12 | const Piece = React.createClass({ 13 | render() { 14 | const containerStyle = { 15 | top: this.props.row * (375/8), 16 | left: this.props.column * (375/8) 17 | }; 18 | const textStyle = { 19 | color: this.props.color 20 | }; 21 | 22 | const onPress = () => { 23 | this.props.onPieceSelect(this.props.row, this.props.column, this.props.color); 24 | }; 25 | 26 | if (this.props.selectable) 27 | return ( 28 | 29 | 30 | 31 | {this.props.piece.toUpperCase()} 32 | 33 | 34 | 35 | ); 36 | else 37 | return ( 38 | 39 | 40 | {this.props.piece.toUpperCase()} 41 | 42 | 43 | ); 44 | }, 45 | 46 | componentWillUpdate(nextProps, nextState) { 47 | if (this.props.row != nextProps.row || this.props.column != nextProps.column) { 48 | const x = (375/8) * (nextProps.column + .5), 49 | y = (375/8) * (nextProps.row + .5); 50 | 51 | if (this.props.piece === CONSTANTS.KNIGHT) { 52 | const diffX = Math.abs(this.props.column - nextProps.column), 53 | diffY = Math.abs(this.props.row - nextProps.row); 54 | var midX, midY; 55 | if (Math.max(diffX, diffY) === diffX) { 56 | midX = (375/8) * (nextProps.column + .5); 57 | midY = (375/8) * (this.props.row + .5); 58 | } else { 59 | midX = (375/8) * (this.props.column + .5); 60 | midY = (375/8) * (nextProps.row + .5); 61 | } 62 | Animation.startAnimation(this.refs['this'], 300, 0, 'easeInOutCirc', {position: [midX, midY]}); 63 | setTimeout(() => { 64 | Animation.startAnimation(this.refs['this'], 150, 0, 'easeInOutCirc', {position: [x, y]}); 65 | }, 300); 66 | } else { 67 | Animation.startAnimation(this.refs['this'], 500, 0, 'easeInOutCirc', {position: [x, y]}); 68 | } 69 | } 70 | }, 71 | 72 | componentDidMount() { 73 | setTimeout(() => { 74 | Animation.startAnimation(this.refs['this'], 500, 0, 'easeInQuad', {opacity: 1}); 75 | }, (150 * (7 - Math.abs(this.props.column - 3.5)))); 76 | // setTimeout(() => { 77 | // Animation.startAnimation(this.refs['this'], 500, 0, 'easeInQuad', {opacity: 1}); 78 | // }, (100 * Math.sqrt(Math.pow(this.props.column - 3, 2)))); 79 | } 80 | }); 81 | 82 | const styles = StyleSheet.create({ 83 | container: { 84 | position: 'absolute', 85 | width: 375/8, 86 | height: 375/8, 87 | backgroundColor: 'rgba(0, 0, 0, 0)', 88 | opacity: 0 89 | }, 90 | text: { 91 | fontSize: 40, 92 | textAlign: 'center', 93 | fontWeight: 'bold' 94 | } 95 | }); 96 | 97 | module.exports = Piece; 98 | -------------------------------------------------------------------------------- /ReactChess.xcodeproj/xcshareddata/xcschemes/ReactChess.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /iOS/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /javascript/board.ios.js: -------------------------------------------------------------------------------- 1 | const React = require('react-native'); 2 | const { 3 | AppRegistry, 4 | StyleSheet, 5 | Text, 6 | View 7 | } = React; 8 | const Square = require('./square.ios'); 9 | const Piece = require('./piece.ios'); 10 | const CONSTANTS = require('./constants.ios'); 11 | 12 | var keys = [ 13 | [0, 1, 2, 3, 4, 5, 6, 7], 14 | [8, 9, 10, 11, 12, 13, 14, 15], 15 | [null, null, null, null, null, null, null, null], 16 | [null, null, null, null, null, null, null, null], 17 | [null, null, null, null, null, null, null, null], 18 | [null, null, null, null, null, null, null, null], 19 | [16, 17, 18, 19, 20, 21, 22, 23], 20 | [24, 25, 26, 27, 28, 29, 30, 31] 21 | ]; 22 | 23 | const Board = React.createClass({ 24 | getInitialState() { 25 | return { 26 | selectedPiece: null 27 | }; 28 | }, 29 | 30 | render() { 31 | var squares = [], pieces = []; 32 | var moves = []; 33 | 34 | const gameState = this.props.game.fen().split(' ')[0].split('/'); 35 | 36 | if (this.state.selectedPiece !== null) { 37 | const currentSquare = { square: 38 | CONSTANTS.COLUMNS[this.state.selectedPiece.column] + CONSTANTS.ROWS[this.state.selectedPiece.row], 39 | verbose: true 40 | }; 41 | moves = []; 42 | this.props.game.moves(currentSquare).map((move) => { 43 | moves.push(move.to); 44 | }); 45 | } 46 | 47 | gameState.map((row, rowIndex) => { 48 | var column = 0; 49 | for (var i = 0; i < row.length; i++) { 50 | if (row.charAt(i).match(/\d/)) { 51 | for (var j = 0; j < parseInt(row.charAt(i)); j++) { 52 | squares.push( 53 | 63 | ); 64 | column++; 65 | } 66 | } 67 | else if (row.charAt(i).match(/[A-Za-z]/)) { 68 | squares.push( 69 | 79 | ); 80 | color = row.charAt(i).match(/[A-Z]/) ? CONSTANTS.WHITE : CONSTANTS.BLACK; 81 | key = color + row.charAt(i).toLowerCase(); 82 | pieces.push( 83 | 92 | ); 93 | column++; 94 | } 95 | } 96 | }); 97 | 98 | return ( 99 | 100 | {squares.concat(pieces)} 101 | 102 | ); 103 | }, 104 | 105 | onPieceSelected(row, column, color) { 106 | if (this.state.selectedPiece !== null) { 107 | if (this.props.turn !== color) { 108 | this.onSquareSelected(row, column); 109 | } else { 110 | if (this.state.selectedPiece.row !== row || this.state.selectedPiece.column !== column) 111 | this.setState({ selectedPiece: {row: row, column: column} }); 112 | else 113 | this.setState({ selectedPiece: null }); 114 | } 115 | } else { 116 | this.setState({ selectedPiece: {row: row, column: column} }); 117 | } 118 | }, 119 | 120 | onSquareSelected(row, column) { 121 | if (this.state.selectedPiece === null) { 122 | return; 123 | } 124 | const move = { 125 | from: CONSTANTS.COLUMNS[this.state.selectedPiece.column] + CONSTANTS.ROWS[this.state.selectedPiece.row], 126 | to: CONSTANTS.COLUMNS[column] + CONSTANTS.ROWS[row] 127 | }; 128 | if (this.props.game.move(move)) { 129 | keys[row][column] = keys[this.state.selectedPiece.row][this.state.selectedPiece.column]; 130 | keys[this.state.selectedPiece.row][this.state.selectedPiece.column] = null; 131 | } 132 | this.props.turnComplete(); 133 | } 134 | }); 135 | 136 | const styles = StyleSheet.create({ 137 | container: { 138 | width: 375, 139 | height: 375, 140 | backgroundColor: '#F5FCFF', 141 | flexDirection: 'row', 142 | flexWrap: 'wrap', 143 | } 144 | }); 145 | 146 | module.exports = Board; -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | true 27 | 28 | 29 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 65 | 66 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 1427402432863 177 | 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 | 206 | 207 | 208 | 209 | 212 | 215 | 216 | 217 | 219 | 220 | 223 | 224 | 225 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /ReactChess.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00481BE61AC0C7FA00671115 /* libRCTWebSocketDebugger.a */; }; 11 | 00481BEA1AC0C89D00671115 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 00481BE91AC0C89D00671115 /* libicucore.dylib */; }; 12 | 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; 13 | 00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302B41ABCB8E700DB3ED1 /* libRCTAdSupport.a */; }; 14 | 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; 15 | 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; 16 | 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; 17 | 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; 18 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 19 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 20 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 21 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 22 | 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 23 | 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 24 | AF1765C01AC604C600AE3E79 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AF1765BF1AC604B500AE3E79 /* libRCTAnimation.a */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | 00481BE51AC0C7FA00671115 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */; 31 | proxyType = 2; 32 | remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; 33 | remoteInfo = RCTWebSocketDebugger; 34 | }; 35 | 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; 38 | proxyType = 2; 39 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 40 | remoteInfo = RCTActionSheet; 41 | }; 42 | 00C302B31ABCB8E700DB3ED1 /* PBXContainerItemProxy */ = { 43 | isa = PBXContainerItemProxy; 44 | containerPortal = 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */; 45 | proxyType = 2; 46 | remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; 47 | remoteInfo = RCTAdSupport; 48 | }; 49 | 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { 50 | isa = PBXContainerItemProxy; 51 | containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; 52 | proxyType = 2; 53 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 54 | remoteInfo = RCTGeolocation; 55 | }; 56 | 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { 57 | isa = PBXContainerItemProxy; 58 | containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; 59 | proxyType = 2; 60 | remoteGlobalIDString = 58B5115D1A9E6B3D00147676; 61 | remoteInfo = RCTImage; 62 | }; 63 | 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { 64 | isa = PBXContainerItemProxy; 65 | containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; 66 | proxyType = 2; 67 | remoteGlobalIDString = 58B511DB1A9E6C8500147676; 68 | remoteInfo = RCTNetwork; 69 | }; 70 | 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { 71 | isa = PBXContainerItemProxy; 72 | containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; 73 | proxyType = 2; 74 | remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; 75 | remoteInfo = RCTVibration; 76 | }; 77 | 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { 78 | isa = PBXContainerItemProxy; 79 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 80 | proxyType = 2; 81 | remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; 82 | remoteInfo = React; 83 | }; 84 | 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { 85 | isa = PBXContainerItemProxy; 86 | containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; 87 | proxyType = 2; 88 | remoteGlobalIDString = 58B5119B1A9E6C1200147676; 89 | remoteInfo = RCTText; 90 | }; 91 | AF1765BE1AC604B500AE3E79 /* PBXContainerItemProxy */ = { 92 | isa = PBXContainerItemProxy; 93 | containerPortal = AF1765BA1AC604B500AE3E79 /* RCTAnimation.xcodeproj */; 94 | proxyType = 2; 95 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 96 | remoteInfo = RCTAnimation; 97 | }; 98 | /* End PBXContainerItemProxy section */ 99 | 100 | /* Begin PBXFileReference section */ 101 | 00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocketDebugger.xcodeproj; path = "node_modules/react-native/Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj"; sourceTree = ""; }; 102 | 00481BE91AC0C89D00671115 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; }; 103 | 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; 104 | 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = "node_modules/react-native/Libraries/AdSupport/RCTAdSupport.xcodeproj"; sourceTree = ""; }; 105 | 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; 106 | 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; 107 | 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; 108 | 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; 109 | 13B07F961A680F5B00A75B9A /* ReactChess.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReactChess.app; sourceTree = BUILT_PRODUCTS_DIR; }; 110 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = iOS/AppDelegate.h; sourceTree = ""; }; 111 | 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = iOS/AppDelegate.m; sourceTree = ""; }; 112 | 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 113 | 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = iOS/Images.xcassets; sourceTree = ""; }; 114 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = iOS/Info.plist; sourceTree = ""; }; 115 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = iOS/main.m; sourceTree = ""; }; 116 | 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; 117 | 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; 118 | AF1765BA1AC604B500AE3E79 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "node_modules/react-native/Libraries/Animation/RCTAnimation.xcodeproj"; sourceTree = ""; }; 119 | /* End PBXFileReference section */ 120 | 121 | /* Begin PBXFrameworksBuildPhase section */ 122 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { 123 | isa = PBXFrameworksBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | AF1765C01AC604C600AE3E79 /* libRCTAnimation.a in Frameworks */, 127 | 00481BEA1AC0C89D00671115 /* libicucore.dylib in Frameworks */, 128 | 146834051AC3E58100842450 /* libReact.a in Frameworks */, 129 | 00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */, 130 | 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, 131 | 00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */, 132 | 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, 133 | 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, 134 | 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, 135 | 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, 136 | 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, 137 | ); 138 | runOnlyForDeploymentPostprocessing = 0; 139 | }; 140 | /* End PBXFrameworksBuildPhase section */ 141 | 142 | /* Begin PBXGroup section */ 143 | 00481BDC1AC0C7FA00671115 /* Products */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 00481BE61AC0C7FA00671115 /* libRCTWebSocketDebugger.a */, 147 | ); 148 | name = Products; 149 | sourceTree = ""; 150 | }; 151 | 00C302A81ABCB8CE00DB3ED1 /* Products */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, 155 | ); 156 | name = Products; 157 | sourceTree = ""; 158 | }; 159 | 00C302B01ABCB8E700DB3ED1 /* Products */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 00C302B41ABCB8E700DB3ED1 /* libRCTAdSupport.a */, 163 | ); 164 | name = Products; 165 | sourceTree = ""; 166 | }; 167 | 00C302B61ABCB90400DB3ED1 /* Products */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, 171 | ); 172 | name = Products; 173 | sourceTree = ""; 174 | }; 175 | 00C302BC1ABCB91800DB3ED1 /* Products */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, 179 | ); 180 | name = Products; 181 | sourceTree = ""; 182 | }; 183 | 00C302D41ABCB9D200DB3ED1 /* Products */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, 187 | ); 188 | name = Products; 189 | sourceTree = ""; 190 | }; 191 | 00C302E01ABCB9EE00DB3ED1 /* Products */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, 195 | ); 196 | name = Products; 197 | sourceTree = ""; 198 | }; 199 | 13B07FAE1A68108700A75B9A /* ReactChess */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 203 | 13B07FB01A68108700A75B9A /* AppDelegate.m */, 204 | 13B07FB51A68108700A75B9A /* Images.xcassets */, 205 | 13B07FB61A68108700A75B9A /* Info.plist */, 206 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 207 | 13B07FB71A68108700A75B9A /* main.m */, 208 | ); 209 | name = ReactChess; 210 | sourceTree = ""; 211 | }; 212 | 146834001AC3E56700842450 /* Products */ = { 213 | isa = PBXGroup; 214 | children = ( 215 | 146834041AC3E56700842450 /* libReact.a */, 216 | ); 217 | name = Products; 218 | sourceTree = ""; 219 | }; 220 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = { 221 | isa = PBXGroup; 222 | children = ( 223 | AF1765BA1AC604B500AE3E79 /* RCTAnimation.xcodeproj */, 224 | 146833FF1AC3E56700842450 /* React.xcodeproj */, 225 | 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, 226 | 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, 227 | 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */, 228 | 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, 229 | 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, 230 | 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, 231 | 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 232 | 00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */, 233 | 00481BE91AC0C89D00671115 /* libicucore.dylib */, 234 | ); 235 | name = Libraries; 236 | sourceTree = ""; 237 | }; 238 | 832341B11AAA6A8300B99B32 /* Products */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | 832341B51AAA6A8300B99B32 /* libRCTText.a */, 242 | ); 243 | name = Products; 244 | sourceTree = ""; 245 | }; 246 | 83CBB9F61A601CBA00E9B192 = { 247 | isa = PBXGroup; 248 | children = ( 249 | 13B07FAE1A68108700A75B9A /* ReactChess */, 250 | 832341AE1AAA6A7D00B99B32 /* Libraries */, 251 | 83CBBA001A601CBA00E9B192 /* Products */, 252 | ); 253 | sourceTree = ""; 254 | }; 255 | 83CBBA001A601CBA00E9B192 /* Products */ = { 256 | isa = PBXGroup; 257 | children = ( 258 | 13B07F961A680F5B00A75B9A /* ReactChess.app */, 259 | ); 260 | name = Products; 261 | sourceTree = ""; 262 | }; 263 | AF1765BB1AC604B500AE3E79 /* Products */ = { 264 | isa = PBXGroup; 265 | children = ( 266 | AF1765BF1AC604B500AE3E79 /* libRCTAnimation.a */, 267 | ); 268 | name = Products; 269 | sourceTree = ""; 270 | }; 271 | /* End PBXGroup section */ 272 | 273 | /* Begin PBXNativeTarget section */ 274 | 13B07F861A680F5B00A75B9A /* ReactChess */ = { 275 | isa = PBXNativeTarget; 276 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactChess" */; 277 | buildPhases = ( 278 | 13B07F871A680F5B00A75B9A /* Sources */, 279 | 13B07F8C1A680F5B00A75B9A /* Frameworks */, 280 | 13B07F8E1A680F5B00A75B9A /* Resources */, 281 | ); 282 | buildRules = ( 283 | ); 284 | dependencies = ( 285 | ); 286 | name = ReactChess; 287 | productName = "Hello World"; 288 | productReference = 13B07F961A680F5B00A75B9A /* ReactChess.app */; 289 | productType = "com.apple.product-type.application"; 290 | }; 291 | /* End PBXNativeTarget section */ 292 | 293 | /* Begin PBXProject section */ 294 | 83CBB9F71A601CBA00E9B192 /* Project object */ = { 295 | isa = PBXProject; 296 | attributes = { 297 | LastUpgradeCheck = 0610; 298 | ORGANIZATIONNAME = Facebook; 299 | }; 300 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "ReactChess" */; 301 | compatibilityVersion = "Xcode 3.2"; 302 | developmentRegion = English; 303 | hasScannedForEncodings = 0; 304 | knownRegions = ( 305 | en, 306 | Base, 307 | ); 308 | mainGroup = 83CBB9F61A601CBA00E9B192; 309 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; 310 | projectDirPath = ""; 311 | projectReferences = ( 312 | { 313 | ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; 314 | ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; 315 | }, 316 | { 317 | ProductGroup = 00C302B01ABCB8E700DB3ED1 /* Products */; 318 | ProjectRef = 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */; 319 | }, 320 | { 321 | ProductGroup = AF1765BB1AC604B500AE3E79 /* Products */; 322 | ProjectRef = AF1765BA1AC604B500AE3E79 /* RCTAnimation.xcodeproj */; 323 | }, 324 | { 325 | ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; 326 | ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; 327 | }, 328 | { 329 | ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; 330 | ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; 331 | }, 332 | { 333 | ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; 334 | ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; 335 | }, 336 | { 337 | ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; 338 | ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; 339 | }, 340 | { 341 | ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; 342 | ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; 343 | }, 344 | { 345 | ProductGroup = 00481BDC1AC0C7FA00671115 /* Products */; 346 | ProjectRef = 00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */; 347 | }, 348 | { 349 | ProductGroup = 146834001AC3E56700842450 /* Products */; 350 | ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; 351 | }, 352 | ); 353 | projectRoot = ""; 354 | targets = ( 355 | 13B07F861A680F5B00A75B9A /* ReactChess */, 356 | ); 357 | }; 358 | /* End PBXProject section */ 359 | 360 | /* Begin PBXReferenceProxy section */ 361 | 00481BE61AC0C7FA00671115 /* libRCTWebSocketDebugger.a */ = { 362 | isa = PBXReferenceProxy; 363 | fileType = archive.ar; 364 | path = libRCTWebSocketDebugger.a; 365 | remoteRef = 00481BE51AC0C7FA00671115 /* PBXContainerItemProxy */; 366 | sourceTree = BUILT_PRODUCTS_DIR; 367 | }; 368 | 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { 369 | isa = PBXReferenceProxy; 370 | fileType = archive.ar; 371 | path = libRCTActionSheet.a; 372 | remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; 373 | sourceTree = BUILT_PRODUCTS_DIR; 374 | }; 375 | 00C302B41ABCB8E700DB3ED1 /* libRCTAdSupport.a */ = { 376 | isa = PBXReferenceProxy; 377 | fileType = archive.ar; 378 | path = libRCTAdSupport.a; 379 | remoteRef = 00C302B31ABCB8E700DB3ED1 /* PBXContainerItemProxy */; 380 | sourceTree = BUILT_PRODUCTS_DIR; 381 | }; 382 | 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { 383 | isa = PBXReferenceProxy; 384 | fileType = archive.ar; 385 | path = libRCTGeolocation.a; 386 | remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; 387 | sourceTree = BUILT_PRODUCTS_DIR; 388 | }; 389 | 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { 390 | isa = PBXReferenceProxy; 391 | fileType = archive.ar; 392 | path = libRCTImage.a; 393 | remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; 394 | sourceTree = BUILT_PRODUCTS_DIR; 395 | }; 396 | 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { 397 | isa = PBXReferenceProxy; 398 | fileType = archive.ar; 399 | path = libRCTNetwork.a; 400 | remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; 401 | sourceTree = BUILT_PRODUCTS_DIR; 402 | }; 403 | 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { 404 | isa = PBXReferenceProxy; 405 | fileType = archive.ar; 406 | path = libRCTVibration.a; 407 | remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; 408 | sourceTree = BUILT_PRODUCTS_DIR; 409 | }; 410 | 146834041AC3E56700842450 /* libReact.a */ = { 411 | isa = PBXReferenceProxy; 412 | fileType = archive.ar; 413 | path = libReact.a; 414 | remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; 415 | sourceTree = BUILT_PRODUCTS_DIR; 416 | }; 417 | 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { 418 | isa = PBXReferenceProxy; 419 | fileType = archive.ar; 420 | path = libRCTText.a; 421 | remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; 422 | sourceTree = BUILT_PRODUCTS_DIR; 423 | }; 424 | AF1765BF1AC604B500AE3E79 /* libRCTAnimation.a */ = { 425 | isa = PBXReferenceProxy; 426 | fileType = archive.ar; 427 | path = libRCTAnimation.a; 428 | remoteRef = AF1765BE1AC604B500AE3E79 /* PBXContainerItemProxy */; 429 | sourceTree = BUILT_PRODUCTS_DIR; 430 | }; 431 | /* End PBXReferenceProxy section */ 432 | 433 | /* Begin PBXResourcesBuildPhase section */ 434 | 13B07F8E1A680F5B00A75B9A /* Resources */ = { 435 | isa = PBXResourcesBuildPhase; 436 | buildActionMask = 2147483647; 437 | files = ( 438 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 439 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, 440 | ); 441 | runOnlyForDeploymentPostprocessing = 0; 442 | }; 443 | /* End PBXResourcesBuildPhase section */ 444 | 445 | /* Begin PBXSourcesBuildPhase section */ 446 | 13B07F871A680F5B00A75B9A /* Sources */ = { 447 | isa = PBXSourcesBuildPhase; 448 | buildActionMask = 2147483647; 449 | files = ( 450 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 451 | 13B07FC11A68108700A75B9A /* main.m in Sources */, 452 | ); 453 | runOnlyForDeploymentPostprocessing = 0; 454 | }; 455 | /* End PBXSourcesBuildPhase section */ 456 | 457 | /* Begin PBXVariantGroup section */ 458 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { 459 | isa = PBXVariantGroup; 460 | children = ( 461 | 13B07FB21A68108700A75B9A /* Base */, 462 | ); 463 | name = LaunchScreen.xib; 464 | path = iOS; 465 | sourceTree = ""; 466 | }; 467 | /* End PBXVariantGroup section */ 468 | 469 | /* Begin XCBuildConfiguration section */ 470 | 13B07F941A680F5B00A75B9A /* Debug */ = { 471 | isa = XCBuildConfiguration; 472 | buildSettings = { 473 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 474 | FRAMEWORK_SEARCH_PATHS = ( 475 | "$(inherited)", 476 | "$(DEVELOPER_FRAMEWORKS_DIR)", 477 | ); 478 | HEADER_SEARCH_PATHS = ( 479 | "$(inherited)", 480 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 481 | "$(SRCROOT)/node_modules/react-native/React/**", 482 | ); 483 | INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; 484 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 485 | OTHER_LDFLAGS = "-ObjC"; 486 | PRODUCT_NAME = ReactChess; 487 | }; 488 | name = Debug; 489 | }; 490 | 13B07F951A680F5B00A75B9A /* Release */ = { 491 | isa = XCBuildConfiguration; 492 | buildSettings = { 493 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 494 | FRAMEWORK_SEARCH_PATHS = ( 495 | "$(inherited)", 496 | "$(DEVELOPER_FRAMEWORKS_DIR)", 497 | ); 498 | HEADER_SEARCH_PATHS = ( 499 | "$(inherited)", 500 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 501 | "$(SRCROOT)/node_modules/react-native/React/**", 502 | ); 503 | INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; 504 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 505 | OTHER_LDFLAGS = "-ObjC"; 506 | PRODUCT_NAME = ReactChess; 507 | }; 508 | name = Release; 509 | }; 510 | 83CBBA201A601CBA00E9B192 /* Debug */ = { 511 | isa = XCBuildConfiguration; 512 | buildSettings = { 513 | ALWAYS_SEARCH_USER_PATHS = NO; 514 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 515 | CLANG_CXX_LIBRARY = "libc++"; 516 | CLANG_ENABLE_MODULES = YES; 517 | CLANG_ENABLE_OBJC_ARC = YES; 518 | CLANG_WARN_BOOL_CONVERSION = YES; 519 | CLANG_WARN_CONSTANT_CONVERSION = YES; 520 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 521 | CLANG_WARN_EMPTY_BODY = YES; 522 | CLANG_WARN_ENUM_CONVERSION = YES; 523 | CLANG_WARN_INT_CONVERSION = YES; 524 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 525 | CLANG_WARN_UNREACHABLE_CODE = YES; 526 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 527 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 528 | COPY_PHASE_STRIP = NO; 529 | ENABLE_STRICT_OBJC_MSGSEND = YES; 530 | GCC_C_LANGUAGE_STANDARD = gnu99; 531 | GCC_DYNAMIC_NO_PIC = NO; 532 | GCC_OPTIMIZATION_LEVEL = 0; 533 | GCC_PREPROCESSOR_DEFINITIONS = ( 534 | "DEBUG=1", 535 | "$(inherited)", 536 | ); 537 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 538 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 539 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 540 | GCC_WARN_UNDECLARED_SELECTOR = YES; 541 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 542 | GCC_WARN_UNUSED_FUNCTION = YES; 543 | GCC_WARN_UNUSED_VARIABLE = YES; 544 | HEADER_SEARCH_PATHS = ( 545 | "$(inherited)", 546 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 547 | "$(SRCROOT)/node_modules/react-native/React/**", 548 | ); 549 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 550 | MTL_ENABLE_DEBUG_INFO = YES; 551 | ONLY_ACTIVE_ARCH = YES; 552 | SDKROOT = iphoneos; 553 | }; 554 | name = Debug; 555 | }; 556 | 83CBBA211A601CBA00E9B192 /* Release */ = { 557 | isa = XCBuildConfiguration; 558 | buildSettings = { 559 | ALWAYS_SEARCH_USER_PATHS = NO; 560 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 561 | CLANG_CXX_LIBRARY = "libc++"; 562 | CLANG_ENABLE_MODULES = YES; 563 | CLANG_ENABLE_OBJC_ARC = YES; 564 | CLANG_WARN_BOOL_CONVERSION = YES; 565 | CLANG_WARN_CONSTANT_CONVERSION = YES; 566 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 567 | CLANG_WARN_EMPTY_BODY = YES; 568 | CLANG_WARN_ENUM_CONVERSION = YES; 569 | CLANG_WARN_INT_CONVERSION = YES; 570 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 571 | CLANG_WARN_UNREACHABLE_CODE = YES; 572 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 573 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 574 | COPY_PHASE_STRIP = YES; 575 | ENABLE_NS_ASSERTIONS = NO; 576 | ENABLE_STRICT_OBJC_MSGSEND = YES; 577 | GCC_C_LANGUAGE_STANDARD = gnu99; 578 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 579 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 580 | GCC_WARN_UNDECLARED_SELECTOR = YES; 581 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 582 | GCC_WARN_UNUSED_FUNCTION = YES; 583 | GCC_WARN_UNUSED_VARIABLE = YES; 584 | HEADER_SEARCH_PATHS = ( 585 | "$(inherited)", 586 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 587 | "$(SRCROOT)/node_modules/react-native/React/**", 588 | ); 589 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 590 | MTL_ENABLE_DEBUG_INFO = NO; 591 | SDKROOT = iphoneos; 592 | VALIDATE_PRODUCT = YES; 593 | }; 594 | name = Release; 595 | }; 596 | /* End XCBuildConfiguration section */ 597 | 598 | /* Begin XCConfigurationList section */ 599 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactChess" */ = { 600 | isa = XCConfigurationList; 601 | buildConfigurations = ( 602 | 13B07F941A680F5B00A75B9A /* Debug */, 603 | 13B07F951A680F5B00A75B9A /* Release */, 604 | ); 605 | defaultConfigurationIsVisible = 0; 606 | defaultConfigurationName = Release; 607 | }; 608 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "ReactChess" */ = { 609 | isa = XCConfigurationList; 610 | buildConfigurations = ( 611 | 83CBBA201A601CBA00E9B192 /* Debug */, 612 | 83CBBA211A601CBA00E9B192 /* Release */, 613 | ); 614 | defaultConfigurationIsVisible = 0; 615 | defaultConfigurationName = Release; 616 | }; 617 | /* End XCConfigurationList section */ 618 | }; 619 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; 620 | } 621 | -------------------------------------------------------------------------------- /javascript/chess.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Copyright (c) 2014, Jeff Hlywa (jhlywa@gmail.com) 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | * POSSIBILITY OF SUCH DAMAGE. 26 | * 27 | *----------------------------------------------------------------------------*/ 28 | 29 | /* minified license below */ 30 | 31 | /*! Copyright (c) 2014, Jeff Hlywa (jhlywa@gmail.com) 32 | * Released under the BSD license 33 | * https://github.com/jhlywa/chess.js/blob/master/LICENSE 34 | */ 35 | 36 | var Chess = function(fen) { 37 | 38 | /* jshint indent: false */ 39 | 40 | var BLACK = 'b'; 41 | var WHITE = 'w'; 42 | 43 | var EMPTY = -1; 44 | 45 | var PAWN = 'p'; 46 | var KNIGHT = 'n'; 47 | var BISHOP = 'b'; 48 | var ROOK = 'r'; 49 | var QUEEN = 'q'; 50 | var KING = 'k'; 51 | 52 | var SYMBOLS = 'pnbrqkPNBRQK'; 53 | 54 | var DEFAULT_POSITION = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'; 55 | 56 | var POSSIBLE_RESULTS = ['1-0', '0-1', '1/2-1/2', '*']; 57 | 58 | var PAWN_OFFSETS = { 59 | b: [16, 32, 17, 15], 60 | w: [-16, -32, -17, -15] 61 | }; 62 | 63 | var PIECE_OFFSETS = { 64 | n: [-18, -33, -31, -14, 18, 33, 31, 14], 65 | b: [-17, -15, 17, 15], 66 | r: [-16, 1, 16, -1], 67 | q: [-17, -16, -15, 1, 17, 16, 15, -1], 68 | k: [-17, -16, -15, 1, 17, 16, 15, -1] 69 | }; 70 | 71 | var ATTACKS = [ 72 | 20, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0,20, 0, 73 | 0,20, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0,20, 0, 0, 74 | 0, 0,20, 0, 0, 0, 0, 24, 0, 0, 0, 0,20, 0, 0, 0, 75 | 0, 0, 0,20, 0, 0, 0, 24, 0, 0, 0,20, 0, 0, 0, 0, 76 | 0, 0, 0, 0,20, 0, 0, 24, 0, 0,20, 0, 0, 0, 0, 0, 77 | 0, 0, 0, 0, 0,20, 2, 24, 2,20, 0, 0, 0, 0, 0, 0, 78 | 0, 0, 0, 0, 0, 2,53, 56, 53, 2, 0, 0, 0, 0, 0, 0, 79 | 24,24,24,24,24,24,56, 0, 56,24,24,24,24,24,24, 0, 80 | 0, 0, 0, 0, 0, 2,53, 56, 53, 2, 0, 0, 0, 0, 0, 0, 81 | 0, 0, 0, 0, 0,20, 2, 24, 2,20, 0, 0, 0, 0, 0, 0, 82 | 0, 0, 0, 0,20, 0, 0, 24, 0, 0,20, 0, 0, 0, 0, 0, 83 | 0, 0, 0,20, 0, 0, 0, 24, 0, 0, 0,20, 0, 0, 0, 0, 84 | 0, 0,20, 0, 0, 0, 0, 24, 0, 0, 0, 0,20, 0, 0, 0, 85 | 0,20, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0,20, 0, 0, 86 | 20, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0,20 87 | ]; 88 | 89 | var RAYS = [ 90 | 17, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 15, 0, 91 | 0, 17, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 15, 0, 0, 92 | 0, 0, 17, 0, 0, 0, 0, 16, 0, 0, 0, 0, 15, 0, 0, 0, 93 | 0, 0, 0, 17, 0, 0, 0, 16, 0, 0, 0, 15, 0, 0, 0, 0, 94 | 0, 0, 0, 0, 17, 0, 0, 16, 0, 0, 15, 0, 0, 0, 0, 0, 95 | 0, 0, 0, 0, 0, 17, 0, 16, 0, 15, 0, 0, 0, 0, 0, 0, 96 | 0, 0, 0, 0, 0, 0, 17, 16, 15, 0, 0, 0, 0, 0, 0, 0, 97 | 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1,-1, -1, -1, -1, 0, 98 | 0, 0, 0, 0, 0, 0,-15,-16,-17, 0, 0, 0, 0, 0, 0, 0, 99 | 0, 0, 0, 0, 0,-15, 0,-16, 0,-17, 0, 0, 0, 0, 0, 0, 100 | 0, 0, 0, 0,-15, 0, 0,-16, 0, 0,-17, 0, 0, 0, 0, 0, 101 | 0, 0, 0,-15, 0, 0, 0,-16, 0, 0, 0,-17, 0, 0, 0, 0, 102 | 0, 0,-15, 0, 0, 0, 0,-16, 0, 0, 0, 0,-17, 0, 0, 0, 103 | 0,-15, 0, 0, 0, 0, 0,-16, 0, 0, 0, 0, 0,-17, 0, 0, 104 | -15, 0, 0, 0, 0, 0, 0,-16, 0, 0, 0, 0, 0, 0,-17 105 | ]; 106 | 107 | var SHIFTS = { p: 0, n: 1, b: 2, r: 3, q: 4, k: 5 }; 108 | 109 | var FLAGS = { 110 | NORMAL: 'n', 111 | CAPTURE: 'c', 112 | BIG_PAWN: 'b', 113 | EP_CAPTURE: 'e', 114 | PROMOTION: 'p', 115 | KSIDE_CASTLE: 'k', 116 | QSIDE_CASTLE: 'q' 117 | }; 118 | 119 | var BITS = { 120 | NORMAL: 1, 121 | CAPTURE: 2, 122 | BIG_PAWN: 4, 123 | EP_CAPTURE: 8, 124 | PROMOTION: 16, 125 | KSIDE_CASTLE: 32, 126 | QSIDE_CASTLE: 64 127 | }; 128 | 129 | var RANK_1 = 7; 130 | var RANK_2 = 6; 131 | var RANK_3 = 5; 132 | var RANK_4 = 4; 133 | var RANK_5 = 3; 134 | var RANK_6 = 2; 135 | var RANK_7 = 1; 136 | var RANK_8 = 0; 137 | 138 | var SQUARES = { 139 | a8: 0, b8: 1, c8: 2, d8: 3, e8: 4, f8: 5, g8: 6, h8: 7, 140 | a7: 16, b7: 17, c7: 18, d7: 19, e7: 20, f7: 21, g7: 22, h7: 23, 141 | a6: 32, b6: 33, c6: 34, d6: 35, e6: 36, f6: 37, g6: 38, h6: 39, 142 | a5: 48, b5: 49, c5: 50, d5: 51, e5: 52, f5: 53, g5: 54, h5: 55, 143 | a4: 64, b4: 65, c4: 66, d4: 67, e4: 68, f4: 69, g4: 70, h4: 71, 144 | a3: 80, b3: 81, c3: 82, d3: 83, e3: 84, f3: 85, g3: 86, h3: 87, 145 | a2: 96, b2: 97, c2: 98, d2: 99, e2: 100, f2: 101, g2: 102, h2: 103, 146 | a1: 112, b1: 113, c1: 114, d1: 115, e1: 116, f1: 117, g1: 118, h1: 119 147 | }; 148 | 149 | var ROOKS = { 150 | w: [{square: SQUARES.a1, flag: BITS.QSIDE_CASTLE}, 151 | {square: SQUARES.h1, flag: BITS.KSIDE_CASTLE}], 152 | b: [{square: SQUARES.a8, flag: BITS.QSIDE_CASTLE}, 153 | {square: SQUARES.h8, flag: BITS.KSIDE_CASTLE}] 154 | }; 155 | 156 | var board = new Array(128); 157 | var kings = {w: EMPTY, b: EMPTY}; 158 | var turn = WHITE; 159 | var castling = {w: 0, b: 0}; 160 | var ep_square = EMPTY; 161 | var half_moves = 0; 162 | var move_number = 1; 163 | var history = []; 164 | var header = {}; 165 | 166 | /* if the user passes in a fen string, load it, else default to 167 | * starting position 168 | */ 169 | if (typeof fen === 'undefined') { 170 | load(DEFAULT_POSITION); 171 | } else { 172 | load(fen); 173 | } 174 | 175 | function clear() { 176 | board = new Array(128); 177 | kings = {w: EMPTY, b: EMPTY}; 178 | turn = WHITE; 179 | castling = {w: 0, b: 0}; 180 | ep_square = EMPTY; 181 | half_moves = 0; 182 | move_number = 1; 183 | history = []; 184 | header = {}; 185 | update_setup(generate_fen()); 186 | } 187 | 188 | function reset() { 189 | load(DEFAULT_POSITION); 190 | } 191 | 192 | function load(fen) { 193 | var tokens = fen.split(/\s+/); 194 | var position = tokens[0]; 195 | var square = 0; 196 | var valid = SYMBOLS + '12345678/'; 197 | 198 | if (!validate_fen(fen).valid) { 199 | return false; 200 | } 201 | 202 | clear(); 203 | 204 | for (var i = 0; i < position.length; i++) { 205 | var piece = position.charAt(i); 206 | 207 | if (piece === '/') { 208 | square += 8; 209 | } else if (is_digit(piece)) { 210 | square += parseInt(piece, 10); 211 | } else { 212 | var color = (piece < 'a') ? WHITE : BLACK; 213 | put({type: piece.toLowerCase(), color: color}, algebraic(square)); 214 | square++; 215 | } 216 | } 217 | 218 | turn = tokens[1]; 219 | 220 | if (tokens[2].indexOf('K') > -1) { 221 | castling.w |= BITS.KSIDE_CASTLE; 222 | } 223 | if (tokens[2].indexOf('Q') > -1) { 224 | castling.w |= BITS.QSIDE_CASTLE; 225 | } 226 | if (tokens[2].indexOf('k') > -1) { 227 | castling.b |= BITS.KSIDE_CASTLE; 228 | } 229 | if (tokens[2].indexOf('q') > -1) { 230 | castling.b |= BITS.QSIDE_CASTLE; 231 | } 232 | 233 | ep_square = (tokens[3] === '-') ? EMPTY : SQUARES[tokens[3]]; 234 | half_moves = parseInt(tokens[4], 10); 235 | move_number = parseInt(tokens[5], 10); 236 | 237 | update_setup(generate_fen()); 238 | 239 | return true; 240 | } 241 | 242 | function validate_fen(fen) { 243 | var errors = { 244 | 0: 'No errors.', 245 | 1: 'FEN string must contain six space-delimited fields.', 246 | 2: '6th field (move number) must be a positive integer.', 247 | 3: '5th field (half move counter) must be a non-negative integer.', 248 | 4: '4th field (en-passant square) is invalid.', 249 | 5: '3rd field (castling availability) is invalid.', 250 | 6: '2nd field (side to move) is invalid.', 251 | 7: '1st field (piece positions) does not contain 8 \'/\'-delimited rows.', 252 | 8: '1st field (piece positions) is invalid [consecutive numbers].', 253 | 9: '1st field (piece positions) is invalid [invalid piece].', 254 | 10: '1st field (piece positions) is invalid [row too large].', 255 | }; 256 | 257 | /* 1st criterion: 6 space-seperated fields? */ 258 | var tokens = fen.split(/\s+/); 259 | if (tokens.length !== 6) { 260 | return {valid: false, error_number: 1, error: errors[1]}; 261 | } 262 | 263 | /* 2nd criterion: move number field is a integer value > 0? */ 264 | if (isNaN(tokens[5]) || (parseInt(tokens[5], 10) <= 0)) { 265 | return {valid: false, error_number: 2, error: errors[2]}; 266 | } 267 | 268 | /* 3rd criterion: half move counter is an integer >= 0? */ 269 | if (isNaN(tokens[4]) || (parseInt(tokens[4], 10) < 0)) { 270 | return {valid: false, error_number: 3, error: errors[3]}; 271 | } 272 | 273 | /* 4th criterion: 4th field is a valid e.p.-string? */ 274 | if (!/^(-|[abcdefgh][36])$/.test(tokens[3])) { 275 | return {valid: false, error_number: 4, error: errors[4]}; 276 | } 277 | 278 | /* 5th criterion: 3th field is a valid castle-string? */ 279 | if( !/^(KQ?k?q?|Qk?q?|kq?|q|-)$/.test(tokens[2])) { 280 | return {valid: false, error_number: 5, error: errors[5]}; 281 | } 282 | 283 | /* 6th criterion: 2nd field is "w" (white) or "b" (black)? */ 284 | if (!/^(w|b)$/.test(tokens[1])) { 285 | return {valid: false, error_number: 6, error: errors[6]}; 286 | } 287 | 288 | /* 7th criterion: 1st field contains 8 rows? */ 289 | var rows = tokens[0].split('/'); 290 | if (rows.length !== 8) { 291 | return {valid: false, error_number: 7, error: errors[7]}; 292 | } 293 | 294 | /* 8th criterion: every row is valid? */ 295 | for (var i = 0; i < rows.length; i++) { 296 | /* check for right sum of fields AND not two numbers in succession */ 297 | var sum_fields = 0; 298 | var previous_was_number = false; 299 | 300 | for (var k = 0; k < rows[i].length; k++) { 301 | if (!isNaN(rows[i][k])) { 302 | if (previous_was_number) { 303 | return {valid: false, error_number: 8, error: errors[8]}; 304 | } 305 | sum_fields += parseInt(rows[i][k], 10); 306 | previous_was_number = true; 307 | } else { 308 | if (!/^[prnbqkPRNBQK]$/.test(rows[i][k])) { 309 | return {valid: false, error_number: 9, error: errors[9]}; 310 | } 311 | sum_fields += 1; 312 | previous_was_number = false; 313 | } 314 | } 315 | if (sum_fields !== 8) { 316 | return {valid: false, error_number: 10, error: errors[10]}; 317 | } 318 | } 319 | 320 | /* everything's okay! */ 321 | return {valid: true, error_number: 0, error: errors[0]}; 322 | } 323 | 324 | function generate_fen() { 325 | var empty = 0; 326 | var fen = ''; 327 | 328 | for (var i = SQUARES.a8; i <= SQUARES.h1; i++) { 329 | if (board[i] == null) { 330 | empty++; 331 | } else { 332 | if (empty > 0) { 333 | fen += empty; 334 | empty = 0; 335 | } 336 | var color = board[i].color; 337 | var piece = board[i].type; 338 | 339 | fen += (color === WHITE) ? 340 | piece.toUpperCase() : piece.toLowerCase(); 341 | } 342 | 343 | if ((i + 1) & 0x88) { 344 | if (empty > 0) { 345 | fen += empty; 346 | } 347 | 348 | if (i !== SQUARES.h1) { 349 | fen += '/'; 350 | } 351 | 352 | empty = 0; 353 | i += 8; 354 | } 355 | } 356 | 357 | var cflags = ''; 358 | if (castling[WHITE] & BITS.KSIDE_CASTLE) { cflags += 'K'; } 359 | if (castling[WHITE] & BITS.QSIDE_CASTLE) { cflags += 'Q'; } 360 | if (castling[BLACK] & BITS.KSIDE_CASTLE) { cflags += 'k'; } 361 | if (castling[BLACK] & BITS.QSIDE_CASTLE) { cflags += 'q'; } 362 | 363 | /* do we have an empty castling flag? */ 364 | cflags = cflags || '-'; 365 | var epflags = (ep_square === EMPTY) ? '-' : algebraic(ep_square); 366 | 367 | return [fen, turn, cflags, epflags, half_moves, move_number].join(' '); 368 | } 369 | 370 | function set_header(args) { 371 | for (var i = 0; i < args.length; i += 2) { 372 | if (typeof args[i] === 'string' && 373 | typeof args[i + 1] === 'string') { 374 | header[args[i]] = args[i + 1]; 375 | } 376 | } 377 | return header; 378 | } 379 | 380 | /* called when the initial board setup is changed with put() or remove(). 381 | * modifies the SetUp and FEN properties of the header object. if the FEN is 382 | * equal to the default position, the SetUp and FEN are deleted 383 | * the setup is only updated if history.length is zero, ie moves haven't been 384 | * made. 385 | */ 386 | function update_setup(fen) { 387 | if (history.length > 0) return; 388 | 389 | if (fen !== DEFAULT_POSITION) { 390 | header['SetUp'] = '1'; 391 | header['FEN'] = fen; 392 | } else { 393 | delete header['SetUp']; 394 | delete header['FEN']; 395 | } 396 | } 397 | 398 | function get(square) { 399 | var piece = board[SQUARES[square]]; 400 | return (piece) ? {type: piece.type, color: piece.color} : null; 401 | } 402 | 403 | function put(piece, square) { 404 | /* check for valid piece object */ 405 | if (!('type' in piece && 'color' in piece)) { 406 | return false; 407 | } 408 | 409 | /* check for piece */ 410 | if (SYMBOLS.indexOf(piece.type.toLowerCase()) === -1) { 411 | return false; 412 | } 413 | 414 | /* check for valid square */ 415 | if (!(square in SQUARES)) { 416 | return false; 417 | } 418 | 419 | var sq = SQUARES[square]; 420 | 421 | /* don't let the user place more than one king */ 422 | if (piece.type == KING && 423 | !(kings[piece.color] == EMPTY || kings[piece.color] == sq)) { 424 | return false; 425 | } 426 | 427 | board[sq] = {type: piece.type, color: piece.color}; 428 | if (piece.type === KING) { 429 | kings[piece.color] = sq; 430 | } 431 | 432 | update_setup(generate_fen()); 433 | 434 | return true; 435 | } 436 | 437 | function remove(square) { 438 | var piece = get(square); 439 | board[SQUARES[square]] = null; 440 | if (piece && piece.type === KING) { 441 | kings[piece.color] = EMPTY; 442 | } 443 | 444 | update_setup(generate_fen()); 445 | 446 | return piece; 447 | } 448 | 449 | function build_move(board, from, to, flags, promotion) { 450 | var move = { 451 | color: turn, 452 | from: from, 453 | to: to, 454 | flags: flags, 455 | piece: board[from].type 456 | }; 457 | 458 | if (promotion) { 459 | move.flags |= BITS.PROMOTION; 460 | move.promotion = promotion; 461 | } 462 | 463 | if (board[to]) { 464 | move.captured = board[to].type; 465 | } else if (flags & BITS.EP_CAPTURE) { 466 | move.captured = PAWN; 467 | } 468 | return move; 469 | } 470 | 471 | function generate_moves(options) { 472 | function add_move(board, moves, from, to, flags) { 473 | /* if pawn promotion */ 474 | if (board[from].type === PAWN && 475 | (rank(to) === RANK_8 || rank(to) === RANK_1)) { 476 | var pieces = [QUEEN, ROOK, BISHOP, KNIGHT]; 477 | for (var i = 0, len = pieces.length; i < len; i++) { 478 | moves.push(build_move(board, from, to, flags, pieces[i])); 479 | } 480 | } else { 481 | moves.push(build_move(board, from, to, flags)); 482 | } 483 | } 484 | 485 | var moves = []; 486 | var us = turn; 487 | var them = swap_color(us); 488 | var second_rank = {b: RANK_7, w: RANK_2}; 489 | 490 | var first_sq = SQUARES.a8; 491 | var last_sq = SQUARES.h1; 492 | var single_square = false; 493 | 494 | /* do we want legal moves? */ 495 | var legal = (typeof options !== 'undefined' && 'legal' in options) ? 496 | options.legal : true; 497 | 498 | /* are we generating moves for a single square? */ 499 | if (typeof options !== 'undefined' && 'square' in options) { 500 | if (options.square in SQUARES) { 501 | first_sq = last_sq = SQUARES[options.square]; 502 | single_square = true; 503 | } else { 504 | /* invalid square */ 505 | return []; 506 | } 507 | } 508 | 509 | for (var i = first_sq; i <= last_sq; i++) { 510 | /* did we run off the end of the board */ 511 | if (i & 0x88) { i += 7; continue; } 512 | 513 | var piece = board[i]; 514 | if (piece == null || piece.color !== us) { 515 | continue; 516 | } 517 | 518 | if (piece.type === PAWN) { 519 | /* single square, non-capturing */ 520 | var square = i + PAWN_OFFSETS[us][0]; 521 | if (board[square] == null) { 522 | add_move(board, moves, i, square, BITS.NORMAL); 523 | 524 | /* double square */ 525 | var square = i + PAWN_OFFSETS[us][1]; 526 | if (second_rank[us] === rank(i) && board[square] == null) { 527 | add_move(board, moves, i, square, BITS.BIG_PAWN); 528 | } 529 | } 530 | 531 | /* pawn captures */ 532 | for (j = 2; j < 4; j++) { 533 | var square = i + PAWN_OFFSETS[us][j]; 534 | if (square & 0x88) continue; 535 | 536 | if (board[square] != null && 537 | board[square].color === them) { 538 | add_move(board, moves, i, square, BITS.CAPTURE); 539 | } else if (square === ep_square) { 540 | add_move(board, moves, i, ep_square, BITS.EP_CAPTURE); 541 | } 542 | } 543 | } else { 544 | for (var j = 0, len = PIECE_OFFSETS[piece.type].length; j < len; j++) { 545 | var offset = PIECE_OFFSETS[piece.type][j]; 546 | var square = i; 547 | 548 | while (true) { 549 | square += offset; 550 | if (square & 0x88) break; 551 | 552 | if (board[square] == null) { 553 | add_move(board, moves, i, square, BITS.NORMAL); 554 | } else { 555 | if (board[square].color === us) break; 556 | add_move(board, moves, i, square, BITS.CAPTURE); 557 | break; 558 | } 559 | 560 | /* break, if knight or king */ 561 | if (piece.type === 'n' || piece.type === 'k') break; 562 | } 563 | } 564 | } 565 | } 566 | 567 | /* check for castling if: a) we're generating all moves, or b) we're doing 568 | * single square move generation on the king's square 569 | */ 570 | if ((!single_square) || last_sq === kings[us]) { 571 | /* king-side castling */ 572 | if (castling[us] & BITS.KSIDE_CASTLE) { 573 | var castling_from = kings[us]; 574 | var castling_to = castling_from + 2; 575 | 576 | if (board[castling_from + 1] == null && 577 | board[castling_to] == null && 578 | !attacked(them, kings[us]) && 579 | !attacked(them, castling_from + 1) && 580 | !attacked(them, castling_to)) { 581 | add_move(board, moves, kings[us] , castling_to, 582 | BITS.KSIDE_CASTLE); 583 | } 584 | } 585 | 586 | /* queen-side castling */ 587 | if (castling[us] & BITS.QSIDE_CASTLE) { 588 | var castling_from = kings[us]; 589 | var castling_to = castling_from - 2; 590 | 591 | if (board[castling_from - 1] == null && 592 | board[castling_from - 2] == null && 593 | board[castling_from - 3] == null && 594 | !attacked(them, kings[us]) && 595 | !attacked(them, castling_from - 1) && 596 | !attacked(them, castling_to)) { 597 | add_move(board, moves, kings[us], castling_to, 598 | BITS.QSIDE_CASTLE); 599 | } 600 | } 601 | } 602 | 603 | /* return all pseudo-legal moves (this includes moves that allow the king 604 | * to be captured) 605 | */ 606 | if (!legal) { 607 | return moves; 608 | } 609 | 610 | /* filter out illegal moves */ 611 | var legal_moves = []; 612 | for (var i = 0, len = moves.length; i < len; i++) { 613 | make_move(moves[i]); 614 | if (!king_attacked(us)) { 615 | legal_moves.push(moves[i]); 616 | } 617 | undo_move(); 618 | } 619 | 620 | return legal_moves; 621 | } 622 | 623 | /* convert a move from 0x88 coordinates to Standard Algebraic Notation 624 | * (SAN) 625 | */ 626 | function move_to_san(move) { 627 | var output = ''; 628 | 629 | if (move.flags & BITS.KSIDE_CASTLE) { 630 | output = 'O-O'; 631 | } else if (move.flags & BITS.QSIDE_CASTLE) { 632 | output = 'O-O-O'; 633 | } else { 634 | var disambiguator = get_disambiguator(move); 635 | 636 | if (move.piece !== PAWN) { 637 | output += move.piece.toUpperCase() + disambiguator; 638 | } 639 | 640 | if (move.flags & (BITS.CAPTURE | BITS.EP_CAPTURE)) { 641 | if (move.piece === PAWN) { 642 | output += algebraic(move.from)[0]; 643 | } 644 | output += 'x'; 645 | } 646 | 647 | output += algebraic(move.to); 648 | 649 | if (move.flags & BITS.PROMOTION) { 650 | output += '=' + move.promotion.toUpperCase(); 651 | } 652 | } 653 | 654 | make_move(move); 655 | if (in_check()) { 656 | if (in_checkmate()) { 657 | output += '#'; 658 | } else { 659 | output += '+'; 660 | } 661 | } 662 | undo_move(); 663 | 664 | return output; 665 | } 666 | 667 | function attacked(color, square) { 668 | for (var i = SQUARES.a8; i <= SQUARES.h1; i++) { 669 | /* did we run off the end of the board */ 670 | if (i & 0x88) { i += 7; continue; } 671 | 672 | /* if empty square or wrong color */ 673 | if (board[i] == null || board[i].color !== color) continue; 674 | 675 | var piece = board[i]; 676 | var difference = i - square; 677 | var index = difference + 119; 678 | 679 | if (ATTACKS[index] & (1 << SHIFTS[piece.type])) { 680 | if (piece.type === PAWN) { 681 | if (difference > 0) { 682 | if (piece.color === WHITE) return true; 683 | } else { 684 | if (piece.color === BLACK) return true; 685 | } 686 | continue; 687 | } 688 | 689 | /* if the piece is a knight or a king */ 690 | if (piece.type === 'n' || piece.type === 'k') return true; 691 | 692 | var offset = RAYS[index]; 693 | var j = i + offset; 694 | 695 | var blocked = false; 696 | while (j !== square) { 697 | if (board[j] != null) { blocked = true; break; } 698 | j += offset; 699 | } 700 | 701 | if (!blocked) return true; 702 | } 703 | } 704 | 705 | return false; 706 | } 707 | 708 | function king_attacked(color) { 709 | return attacked(swap_color(color), kings[color]); 710 | } 711 | 712 | function in_check() { 713 | return king_attacked(turn); 714 | } 715 | 716 | function in_checkmate() { 717 | return in_check() && generate_moves().length === 0; 718 | } 719 | 720 | function in_stalemate() { 721 | return !in_check() && generate_moves().length === 0; 722 | } 723 | 724 | function insufficient_material() { 725 | var pieces = {}; 726 | var bishops = []; 727 | var num_pieces = 0; 728 | var sq_color = 0; 729 | 730 | for (var i = SQUARES.a8; i<= SQUARES.h1; i++) { 731 | sq_color = (sq_color + 1) % 2; 732 | if (i & 0x88) { i += 7; continue; } 733 | 734 | var piece = board[i]; 735 | if (piece) { 736 | pieces[piece.type] = (piece.type in pieces) ? 737 | pieces[piece.type] + 1 : 1; 738 | if (piece.type === BISHOP) { 739 | bishops.push(sq_color); 740 | } 741 | num_pieces++; 742 | } 743 | } 744 | 745 | /* k vs. k */ 746 | if (num_pieces === 2) { return true; } 747 | 748 | /* k vs. kn .... or .... k vs. kb */ 749 | else if (num_pieces === 3 && (pieces[BISHOP] === 1 || 750 | pieces[KNIGHT] === 1)) { return true; } 751 | 752 | /* kb vs. kb where any number of bishops are all on the same color */ 753 | else if (num_pieces === pieces[BISHOP] + 2) { 754 | var sum = 0; 755 | var len = bishops.length; 756 | for (var i = 0; i < len; i++) { 757 | sum += bishops[i]; 758 | } 759 | if (sum === 0 || sum === len) { return true; } 760 | } 761 | 762 | return false; 763 | } 764 | 765 | function in_threefold_repetition() { 766 | /* TODO: while this function is fine for casual use, a better 767 | * implementation would use a Zobrist key (instead of FEN). the 768 | * Zobrist key would be maintained in the make_move/undo_move functions, 769 | * avoiding the costly that we do below. 770 | */ 771 | var moves = []; 772 | var positions = {}; 773 | var repetition = false; 774 | 775 | while (true) { 776 | var move = undo_move(); 777 | if (!move) break; 778 | moves.push(move); 779 | } 780 | 781 | while (true) { 782 | /* remove the last two fields in the FEN string, they're not needed 783 | * when checking for draw by rep */ 784 | var fen = generate_fen().split(' ').slice(0,4).join(' '); 785 | 786 | /* has the position occurred three or move times */ 787 | positions[fen] = (fen in positions) ? positions[fen] + 1 : 1; 788 | if (positions[fen] >= 3) { 789 | repetition = true; 790 | } 791 | 792 | if (!moves.length) { 793 | break; 794 | } 795 | make_move(moves.pop()); 796 | } 797 | 798 | return repetition; 799 | } 800 | 801 | function push(move) { 802 | history.push({ 803 | move: move, 804 | kings: {b: kings.b, w: kings.w}, 805 | turn: turn, 806 | castling: {b: castling.b, w: castling.w}, 807 | ep_square: ep_square, 808 | half_moves: half_moves, 809 | move_number: move_number 810 | }); 811 | } 812 | 813 | function make_move(move) { 814 | var us = turn; 815 | var them = swap_color(us); 816 | push(move); 817 | 818 | board[move.to] = board[move.from]; 819 | board[move.from] = null; 820 | 821 | /* if ep capture, remove the captured pawn */ 822 | if (move.flags & BITS.EP_CAPTURE) { 823 | if (turn === BLACK) { 824 | board[move.to - 16] = null; 825 | } else { 826 | board[move.to + 16] = null; 827 | } 828 | } 829 | 830 | /* if pawn promotion, replace with new piece */ 831 | if (move.flags & BITS.PROMOTION) { 832 | board[move.to] = {type: move.promotion, color: us}; 833 | } 834 | 835 | /* if we moved the king */ 836 | if (board[move.to].type === KING) { 837 | kings[board[move.to].color] = move.to; 838 | 839 | /* if we castled, move the rook next to the king */ 840 | if (move.flags & BITS.KSIDE_CASTLE) { 841 | var castling_to = move.to - 1; 842 | var castling_from = move.to + 1; 843 | board[castling_to] = board[castling_from]; 844 | board[castling_from] = null; 845 | } else if (move.flags & BITS.QSIDE_CASTLE) { 846 | var castling_to = move.to + 1; 847 | var castling_from = move.to - 2; 848 | board[castling_to] = board[castling_from]; 849 | board[castling_from] = null; 850 | } 851 | 852 | /* turn off castling */ 853 | castling[us] = ''; 854 | } 855 | 856 | /* turn off castling if we move a rook */ 857 | if (castling[us]) { 858 | for (var i = 0, len = ROOKS[us].length; i < len; i++) { 859 | if (move.from === ROOKS[us][i].square && 860 | castling[us] & ROOKS[us][i].flag) { 861 | castling[us] ^= ROOKS[us][i].flag; 862 | break; 863 | } 864 | } 865 | } 866 | 867 | /* turn off castling if we capture a rook */ 868 | if (castling[them]) { 869 | for (var i = 0, len = ROOKS[them].length; i < len; i++) { 870 | if (move.to === ROOKS[them][i].square && 871 | castling[them] & ROOKS[them][i].flag) { 872 | castling[them] ^= ROOKS[them][i].flag; 873 | break; 874 | } 875 | } 876 | } 877 | 878 | /* if big pawn move, update the en passant square */ 879 | if (move.flags & BITS.BIG_PAWN) { 880 | if (turn === 'b') { 881 | ep_square = move.to - 16; 882 | } else { 883 | ep_square = move.to + 16; 884 | } 885 | } else { 886 | ep_square = EMPTY; 887 | } 888 | 889 | /* reset the 50 move counter if a pawn is moved or a piece is captured */ 890 | if (move.piece === PAWN) { 891 | half_moves = 0; 892 | } else if (move.flags & (BITS.CAPTURE | BITS.EP_CAPTURE)) { 893 | half_moves = 0; 894 | } else { 895 | half_moves++; 896 | } 897 | 898 | if (turn === BLACK) { 899 | move_number++; 900 | } 901 | turn = swap_color(turn); 902 | } 903 | 904 | function undo_move() { 905 | var old = history.pop(); 906 | if (old == null) { return null; } 907 | 908 | var move = old.move; 909 | kings = old.kings; 910 | turn = old.turn; 911 | castling = old.castling; 912 | ep_square = old.ep_square; 913 | half_moves = old.half_moves; 914 | move_number = old.move_number; 915 | 916 | var us = turn; 917 | var them = swap_color(turn); 918 | 919 | board[move.from] = board[move.to]; 920 | board[move.from].type = move.piece; // to undo any promotions 921 | board[move.to] = null; 922 | 923 | if (move.flags & BITS.CAPTURE) { 924 | board[move.to] = {type: move.captured, color: them}; 925 | } else if (move.flags & BITS.EP_CAPTURE) { 926 | var index; 927 | if (us === BLACK) { 928 | index = move.to - 16; 929 | } else { 930 | index = move.to + 16; 931 | } 932 | board[index] = {type: PAWN, color: them}; 933 | } 934 | 935 | 936 | if (move.flags & (BITS.KSIDE_CASTLE | BITS.QSIDE_CASTLE)) { 937 | var castling_to, castling_from; 938 | if (move.flags & BITS.KSIDE_CASTLE) { 939 | castling_to = move.to + 1; 940 | castling_from = move.to - 1; 941 | } else if (move.flags & BITS.QSIDE_CASTLE) { 942 | castling_to = move.to - 2; 943 | castling_from = move.to + 1; 944 | } 945 | 946 | board[castling_to] = board[castling_from]; 947 | board[castling_from] = null; 948 | } 949 | 950 | return move; 951 | } 952 | 953 | /* this function is used to uniquely identify ambiguous moves */ 954 | function get_disambiguator(move) { 955 | var moves = generate_moves(); 956 | 957 | var from = move.from; 958 | var to = move.to; 959 | var piece = move.piece; 960 | 961 | var ambiguities = 0; 962 | var same_rank = 0; 963 | var same_file = 0; 964 | 965 | for (var i = 0, len = moves.length; i < len; i++) { 966 | var ambig_from = moves[i].from; 967 | var ambig_to = moves[i].to; 968 | var ambig_piece = moves[i].piece; 969 | 970 | /* if a move of the same piece type ends on the same to square, we'll 971 | * need to add a disambiguator to the algebraic notation 972 | */ 973 | if (piece === ambig_piece && from !== ambig_from && to === ambig_to) { 974 | ambiguities++; 975 | 976 | if (rank(from) === rank(ambig_from)) { 977 | same_rank++; 978 | } 979 | 980 | if (file(from) === file(ambig_from)) { 981 | same_file++; 982 | } 983 | } 984 | } 985 | 986 | if (ambiguities > 0) { 987 | /* if there exists a similar moving piece on the same rank and file as 988 | * the move in question, use the square as the disambiguator 989 | */ 990 | if (same_rank > 0 && same_file > 0) { 991 | return algebraic(from); 992 | } 993 | /* if the moving piece rests on the same file, use the rank symbol as the 994 | * disambiguator 995 | */ 996 | else if (same_file > 0) { 997 | return algebraic(from).charAt(1); 998 | } 999 | /* else use the file symbol */ 1000 | else { 1001 | return algebraic(from).charAt(0); 1002 | } 1003 | } 1004 | 1005 | return ''; 1006 | } 1007 | 1008 | function ascii() { 1009 | var s = ' +------------------------+\n'; 1010 | for (var i = SQUARES.a8; i <= SQUARES.h1; i++) { 1011 | /* display the rank */ 1012 | if (file(i) === 0) { 1013 | s += ' ' + '87654321'[rank(i)] + ' |'; 1014 | } 1015 | 1016 | /* empty piece */ 1017 | if (board[i] == null) { 1018 | s += ' . '; 1019 | } else { 1020 | var piece = board[i].type; 1021 | var color = board[i].color; 1022 | var symbol = (color === WHITE) ? 1023 | piece.toUpperCase() : piece.toLowerCase(); 1024 | s += ' ' + symbol + ' '; 1025 | } 1026 | 1027 | if ((i + 1) & 0x88) { 1028 | s += '|\n'; 1029 | i += 8; 1030 | } 1031 | } 1032 | s += ' +------------------------+\n'; 1033 | s += ' a b c d e f g h\n'; 1034 | 1035 | return s; 1036 | } 1037 | 1038 | /***************************************************************************** 1039 | * UTILITY FUNCTIONS 1040 | ****************************************************************************/ 1041 | function rank(i) { 1042 | return i >> 4; 1043 | } 1044 | 1045 | function file(i) { 1046 | return i & 15; 1047 | } 1048 | 1049 | function algebraic(i){ 1050 | var f = file(i), r = rank(i); 1051 | return 'abcdefgh'.substring(f,f+1) + '87654321'.substring(r,r+1); 1052 | } 1053 | 1054 | function swap_color(c) { 1055 | return c === WHITE ? BLACK : WHITE; 1056 | } 1057 | 1058 | function is_digit(c) { 1059 | return '0123456789'.indexOf(c) !== -1; 1060 | } 1061 | 1062 | /* pretty = external move object */ 1063 | function make_pretty(ugly_move) { 1064 | var move = clone(ugly_move); 1065 | move.san = move_to_san(move); 1066 | move.to = algebraic(move.to); 1067 | move.from = algebraic(move.from); 1068 | 1069 | var flags = ''; 1070 | 1071 | for (var flag in BITS) { 1072 | if (BITS[flag] & move.flags) { 1073 | flags += FLAGS[flag]; 1074 | } 1075 | } 1076 | move.flags = flags; 1077 | 1078 | return move; 1079 | } 1080 | 1081 | function clone(obj) { 1082 | var dupe = (obj instanceof Array) ? [] : {}; 1083 | 1084 | for (var property in obj) { 1085 | if (typeof property === 'object') { 1086 | dupe[property] = clone(obj[property]); 1087 | } else { 1088 | dupe[property] = obj[property]; 1089 | } 1090 | } 1091 | 1092 | return dupe; 1093 | } 1094 | 1095 | function trim(str) { 1096 | return str.replace(/^\s+|\s+$/g, ''); 1097 | } 1098 | 1099 | /***************************************************************************** 1100 | * DEBUGGING UTILITIES 1101 | ****************************************************************************/ 1102 | function perft(depth) { 1103 | var moves = generate_moves({legal: false}); 1104 | var nodes = 0; 1105 | var color = turn; 1106 | 1107 | for (var i = 0, len = moves.length; i < len; i++) { 1108 | make_move(moves[i]); 1109 | if (!king_attacked(color)) { 1110 | if (depth - 1 > 0) { 1111 | var child_nodes = perft(depth - 1); 1112 | nodes += child_nodes; 1113 | } else { 1114 | nodes++; 1115 | } 1116 | } 1117 | undo_move(); 1118 | } 1119 | 1120 | return nodes; 1121 | } 1122 | 1123 | return { 1124 | /*************************************************************************** 1125 | * PUBLIC CONSTANTS (is there a better way to do this?) 1126 | **************************************************************************/ 1127 | WHITE: WHITE, 1128 | BLACK: BLACK, 1129 | PAWN: PAWN, 1130 | KNIGHT: KNIGHT, 1131 | BISHOP: BISHOP, 1132 | ROOK: ROOK, 1133 | QUEEN: QUEEN, 1134 | KING: KING, 1135 | SQUARES: (function() { 1136 | /* from the ECMA-262 spec (section 12.6.4): 1137 | * "The mechanics of enumerating the properties ... is 1138 | * implementation dependent" 1139 | * so: for (var sq in SQUARES) { keys.push(sq); } might not be 1140 | * ordered correctly 1141 | */ 1142 | var keys = []; 1143 | for (var i = SQUARES.a8; i <= SQUARES.h1; i++) { 1144 | if (i & 0x88) { i += 7; continue; } 1145 | keys.push(algebraic(i)); 1146 | } 1147 | return keys; 1148 | })(), 1149 | FLAGS: FLAGS, 1150 | 1151 | /*************************************************************************** 1152 | * PUBLIC API 1153 | **************************************************************************/ 1154 | load: function(fen) { 1155 | return load(fen); 1156 | }, 1157 | 1158 | reset: function() { 1159 | return reset(); 1160 | }, 1161 | 1162 | moves: function(options) { 1163 | /* The internal representation of a chess move is in 0x88 format, and 1164 | * not meant to be human-readable. The code below converts the 0x88 1165 | * square coordinates to algebraic coordinates. It also prunes an 1166 | * unnecessary move keys resulting from a verbose call. 1167 | */ 1168 | 1169 | var ugly_moves = generate_moves(options); 1170 | var moves = []; 1171 | 1172 | for (var i = 0, len = ugly_moves.length; i < len; i++) { 1173 | 1174 | /* does the user want a full move object (most likely not), or just 1175 | * SAN 1176 | */ 1177 | if (typeof options !== 'undefined' && 'verbose' in options && 1178 | options.verbose) { 1179 | moves.push(make_pretty(ugly_moves[i])); 1180 | } else { 1181 | moves.push(move_to_san(ugly_moves[i])); 1182 | } 1183 | } 1184 | 1185 | return moves; 1186 | }, 1187 | 1188 | in_check: function() { 1189 | return in_check(); 1190 | }, 1191 | 1192 | in_checkmate: function() { 1193 | return in_checkmate(); 1194 | }, 1195 | 1196 | in_stalemate: function() { 1197 | return in_stalemate(); 1198 | }, 1199 | 1200 | in_draw: function() { 1201 | return half_moves >= 100 || 1202 | in_stalemate() || 1203 | insufficient_material() || 1204 | in_threefold_repetition(); 1205 | }, 1206 | 1207 | insufficient_material: function() { 1208 | return insufficient_material(); 1209 | }, 1210 | 1211 | in_threefold_repetition: function() { 1212 | return in_threefold_repetition(); 1213 | }, 1214 | 1215 | game_over: function() { 1216 | return half_moves >= 100 || 1217 | in_checkmate() || 1218 | in_stalemate() || 1219 | insufficient_material() || 1220 | in_threefold_repetition(); 1221 | }, 1222 | 1223 | validate_fen: function(fen) { 1224 | return validate_fen(fen); 1225 | }, 1226 | 1227 | fen: function() { 1228 | return generate_fen(); 1229 | }, 1230 | 1231 | pgn: function(options) { 1232 | /* using the specification from http://www.chessclub.com/help/PGN-spec 1233 | * example for html usage: .pgn({ max_width: 72, newline_char: "
" }) 1234 | */ 1235 | var newline = (typeof options === 'object' && 1236 | typeof options.newline_char === 'string') ? 1237 | options.newline_char : '\n'; 1238 | var max_width = (typeof options === 'object' && 1239 | typeof options.max_width === 'number') ? 1240 | options.max_width : 0; 1241 | var result = []; 1242 | var header_exists = false; 1243 | 1244 | /* add the PGN header headerrmation */ 1245 | for (var i in header) { 1246 | /* TODO: order of enumerated properties in header object is not 1247 | * guaranteed, see ECMA-262 spec (section 12.6.4) 1248 | */ 1249 | result.push('[' + i + ' \"' + header[i] + '\"]' + newline); 1250 | header_exists = true; 1251 | } 1252 | 1253 | if (header_exists && history.length) { 1254 | result.push(newline); 1255 | } 1256 | 1257 | /* pop all of history onto reversed_history */ 1258 | var reversed_history = []; 1259 | while (history.length > 0) { 1260 | reversed_history.push(undo_move()); 1261 | } 1262 | 1263 | var moves = []; 1264 | var move_string = ''; 1265 | var pgn_move_number = 1; 1266 | 1267 | /* build the list of moves. a move_string looks like: "3. e3 e6" */ 1268 | while (reversed_history.length > 0) { 1269 | var move = reversed_history.pop(); 1270 | 1271 | /* if the position started with black to move, start PGN with 1. ... */ 1272 | if (pgn_move_number === 1 && move.color === 'b') { 1273 | move_string = '1. ...'; 1274 | pgn_move_number++; 1275 | } else if (move.color === 'w') { 1276 | /* store the previous generated move_string if we have one */ 1277 | if (move_string.length) { 1278 | moves.push(move_string); 1279 | } 1280 | move_string = pgn_move_number + '.'; 1281 | pgn_move_number++; 1282 | } 1283 | 1284 | move_string = move_string + ' ' + move_to_san(move); 1285 | make_move(move); 1286 | } 1287 | 1288 | /* are there any other leftover moves? */ 1289 | if (move_string.length) { 1290 | moves.push(move_string); 1291 | } 1292 | 1293 | /* is there a result? */ 1294 | if (typeof header.Result !== 'undefined') { 1295 | moves.push(header.Result); 1296 | } 1297 | 1298 | /* history should be back to what is was before we started generating PGN, 1299 | * so join together moves 1300 | */ 1301 | if (max_width === 0) { 1302 | return result.join('') + moves.join(' '); 1303 | } 1304 | 1305 | /* wrap the PGN output at max_width */ 1306 | var current_width = 0; 1307 | for (var i = 0; i < moves.length; i++) { 1308 | /* if the current move will push past max_width */ 1309 | if (current_width + moves[i].length > max_width && i !== 0) { 1310 | 1311 | /* don't end the line with whitespace */ 1312 | if (result[result.length - 1] === ' ') { 1313 | result.pop(); 1314 | } 1315 | 1316 | result.push(newline); 1317 | current_width = 0; 1318 | } else if (i !== 0) { 1319 | result.push(' '); 1320 | current_width++; 1321 | } 1322 | result.push(moves[i]); 1323 | current_width += moves[i].length; 1324 | } 1325 | 1326 | return result.join(''); 1327 | }, 1328 | 1329 | load_pgn: function(pgn, options) { 1330 | function mask(str) { 1331 | return str.replace(/\\/g, '\\'); 1332 | } 1333 | 1334 | /* convert a move from Standard Algebraic Notation (SAN) to 0x88 1335 | * coordinates 1336 | */ 1337 | function move_from_san(move) { 1338 | var moves = generate_moves(); 1339 | for (var i = 0, len = moves.length; i < len; i++) { 1340 | /* strip off any trailing move decorations: e.g Nf3+?! */ 1341 | if (move.replace(/[+#?!=]+$/,'') == 1342 | move_to_san(moves[i]).replace(/[+#?!=]+$/,'')) { 1343 | return moves[i]; 1344 | } 1345 | } 1346 | return null; 1347 | } 1348 | 1349 | function get_move_obj(move) { 1350 | return move_from_san(trim(move)); 1351 | } 1352 | 1353 | function has_keys(object) { 1354 | var has_keys = false; 1355 | for (var key in object) { 1356 | has_keys = true; 1357 | } 1358 | return has_keys; 1359 | } 1360 | 1361 | function parse_pgn_header(header, options) { 1362 | var newline_char = (typeof options === 'object' && 1363 | typeof options.newline_char === 'string') ? 1364 | options.newline_char : '\r?\n'; 1365 | var header_obj = {}; 1366 | var headers = header.split(new RegExp(mask(newline_char))); 1367 | var key = ''; 1368 | var value = ''; 1369 | 1370 | for (var i = 0; i < headers.length; i++) { 1371 | key = headers[i].replace(/^\[([A-Z][A-Za-z]*)\s.*\]$/, '$1'); 1372 | value = headers[i].replace(/^\[[A-Za-z]+\s"(.*)"\]$/, '$1'); 1373 | if (trim(key).length > 0) { 1374 | header_obj[key] = value; 1375 | } 1376 | } 1377 | 1378 | return header_obj; 1379 | } 1380 | 1381 | var newline_char = (typeof options === 'object' && 1382 | typeof options.newline_char === 'string') ? 1383 | options.newline_char : '\r?\n'; 1384 | var regex = new RegExp('^(\\[(.|' + mask(newline_char) + ')*\\])' + 1385 | '(' + mask(newline_char) + ')*' + 1386 | '1.(' + mask(newline_char) + '|.)*$', 'g'); 1387 | 1388 | /* get header part of the PGN file */ 1389 | var header_string = pgn.replace(regex, '$1'); 1390 | 1391 | /* no info part given, begins with moves */ 1392 | if (header_string[0] !== '[') { 1393 | header_string = ''; 1394 | } 1395 | 1396 | reset(); 1397 | 1398 | /* parse PGN header */ 1399 | var headers = parse_pgn_header(header_string, options); 1400 | for (var key in headers) { 1401 | set_header([key, headers[key]]); 1402 | } 1403 | 1404 | /* delete header to get the moves */ 1405 | var ms = pgn.replace(header_string, '').replace(new RegExp(mask(newline_char), 'g'), ' '); 1406 | 1407 | /* delete comments */ 1408 | ms = ms.replace(/(\{[^}]+\})+?/g, ''); 1409 | 1410 | /* delete move numbers */ 1411 | ms = ms.replace(/\d+\./g, ''); 1412 | 1413 | 1414 | /* trim and get array of moves */ 1415 | var moves = trim(ms).split(new RegExp(/\s+/)); 1416 | 1417 | /* delete empty entries */ 1418 | moves = moves.join(',').replace(/,,+/g, ',').split(','); 1419 | var move = ''; 1420 | 1421 | for (var half_move = 0; half_move < moves.length - 1; half_move++) { 1422 | move = get_move_obj(moves[half_move]); 1423 | 1424 | /* move not possible! (don't clear the board to examine to show the 1425 | * latest valid position) 1426 | */ 1427 | if (move == null) { 1428 | return false; 1429 | } else { 1430 | make_move(move); 1431 | } 1432 | } 1433 | 1434 | /* examine last move */ 1435 | move = moves[moves.length - 1]; 1436 | if (POSSIBLE_RESULTS.indexOf(move) > -1) { 1437 | if (has_keys(header) && typeof header.Result === 'undefined') { 1438 | set_header(['Result', move]); 1439 | } 1440 | } 1441 | else { 1442 | move = get_move_obj(move); 1443 | if (move == null) { 1444 | return false; 1445 | } else { 1446 | make_move(move); 1447 | } 1448 | } 1449 | return true; 1450 | }, 1451 | 1452 | header: function() { 1453 | return set_header(arguments); 1454 | }, 1455 | 1456 | ascii: function() { 1457 | return ascii(); 1458 | }, 1459 | 1460 | turn: function() { 1461 | return turn; 1462 | }, 1463 | 1464 | move: function(move) { 1465 | /* The move function can be called with in the following parameters: 1466 | * 1467 | * .move('Nxb7') <- where 'move' is a case-sensitive SAN string 1468 | * 1469 | * .move({ from: 'h7', <- where the 'move' is a move object (additional 1470 | * to :'h8', fields are ignored) 1471 | * promotion: 'q', 1472 | * }) 1473 | */ 1474 | var move_obj = null; 1475 | var moves = generate_moves(); 1476 | 1477 | if (typeof move === 'string') { 1478 | /* convert the move string to a move object */ 1479 | for (var i = 0, len = moves.length; i < len; i++) { 1480 | if (move === move_to_san(moves[i])) { 1481 | move_obj = moves[i]; 1482 | break; 1483 | } 1484 | } 1485 | } else if (typeof move === 'object') { 1486 | /* convert the pretty move object to an ugly move object */ 1487 | for (var i = 0, len = moves.length; i < len; i++) { 1488 | if (move.from === algebraic(moves[i].from) && 1489 | move.to === algebraic(moves[i].to) && 1490 | (!('promotion' in moves[i]) || 1491 | move.promotion === moves[i].promotion)) { 1492 | move_obj = moves[i]; 1493 | break; 1494 | } 1495 | } 1496 | } 1497 | 1498 | /* failed to find move */ 1499 | if (!move_obj) { 1500 | return null; 1501 | } 1502 | 1503 | /* need to make a copy of move because we can't generate SAN after the 1504 | * move is made 1505 | */ 1506 | var pretty_move = make_pretty(move_obj); 1507 | 1508 | make_move(move_obj); 1509 | 1510 | return pretty_move; 1511 | }, 1512 | 1513 | undo: function() { 1514 | var move = undo_move(); 1515 | return (move) ? make_pretty(move) : null; 1516 | }, 1517 | 1518 | clear: function() { 1519 | return clear(); 1520 | }, 1521 | 1522 | put: function(piece, square) { 1523 | return put(piece, square); 1524 | }, 1525 | 1526 | get: function(square) { 1527 | return get(square); 1528 | }, 1529 | 1530 | remove: function(square) { 1531 | return remove(square); 1532 | }, 1533 | 1534 | perft: function(depth) { 1535 | return perft(depth); 1536 | }, 1537 | 1538 | square_color: function(square) { 1539 | if (square in SQUARES) { 1540 | var sq_0x88 = SQUARES[square]; 1541 | return ((rank(sq_0x88) + file(sq_0x88)) % 2 === 0) ? 'light' : 'dark'; 1542 | } 1543 | 1544 | return null; 1545 | }, 1546 | 1547 | history: function(options) { 1548 | var reversed_history = []; 1549 | var move_history = []; 1550 | var verbose = (typeof options !== 'undefined' && 'verbose' in options && 1551 | options.verbose); 1552 | 1553 | while (history.length > 0) { 1554 | reversed_history.push(undo_move()); 1555 | } 1556 | 1557 | while (reversed_history.length > 0) { 1558 | var move = reversed_history.pop(); 1559 | if (verbose) { 1560 | move_history.push(make_pretty(move)); 1561 | } else { 1562 | move_history.push(move_to_san(move)); 1563 | } 1564 | make_move(move); 1565 | } 1566 | 1567 | return move_history; 1568 | } 1569 | 1570 | }; 1571 | }; 1572 | 1573 | module.exports = Chess; --------------------------------------------------------------------------------