/**** * Classes ****/ var ActionButton = Container.expand(function (text, color, callback) { var self = Container.call(this); var bg = self.attachAsset(color === 'green' ? 'copyButtonBg' : 'resetButtonBg', { anchorX: 0.5, anchorY: 0.5 }); var buttonText = new Text2(text, { size: 28, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); buttonText.y = -10; self.addChild(buttonText); self.callback = callback; self.down = function (x, y, obj) { if (self.callback) { self.callback(); } }; return self; }); var ChessSquare = Container.expand(function (isLight, x, y) { var self = Container.call(this); var square = self.attachAsset(isLight ? 'chessSquareLight' : 'chessSquareDark', { anchorX: 0, anchorY: 0 }); self.squareX = x; self.squareY = y; self.isLight = isLight; self.piece = null; self.pieceText = null; self.setPiece = function (pieceSymbol) { if (self.pieceText) { self.removeChild(self.pieceText); } if (pieceSymbol) { self.piece = pieceSymbol; self.pieceText = new Text2(pieceSymbol, { size: 120, fill: 0x000000 }); self.pieceText.anchor.set(0.5, 0.5); self.pieceText.x = square.width / 2; self.pieceText.y = square.height / 2; self.addChild(self.pieceText); } else { self.piece = null; self.pieceText = null; } }; return self; }); var GenerateButton = Container.expand(function (callback) { var self = Container.call(this); var bg = self.attachAsset('buttonBg', { anchorX: 0.5, anchorY: 0.5 }); var text = new Text2('Generate New Position', { size: 36, fill: 0xFFFFFF }); text.anchor.set(0.5, 0.5); self.addChild(text); self.callback = callback; self.down = function (x, y, obj) { if (self.callback) { self.callback(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2c3e50 }); /**** * Game Code ****/ var boardSquares = []; var currentPosition = ''; var positionText = null; // Chess piece symbols var pieceSymbols = { 'K': '♔', // King 'Q': '♕', // Queen 'R': '♖', // Rook 'B': '♗', // Bishop 'N': '♘' // Knight }; // Standard Chess960 pieces that need to be placed var piecesToPlace = ['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R']; function initializeBoard() { var boardSize = 1600; // 8 squares * 200px each var startX = (2048 - boardSize) / 2; var startY = 400; // Create 2 ranks: 8 and 2 (back rank and pawn rank) for (var rank = 0; rank < 2; rank++) { // Show ranks 8 and 2 for (var file = 0; file < 8; file++) { var isLight = (rank + file) % 2 === 0; var square = new ChessSquare(isLight, file, rank); square.x = startX + file * 200; square.y = startY + rank * 200; game.addChild(square); if (rank === 0) { // Only store rank 8 squares for piece placement boardSquares.push(square); } // Add pawns to rank 2 (white pawns) only if (rank === 1) { // Rank 2 - white pawns (one rank in front of pieces) square.setPiece('♙'); } } } } function generateChess960Position() { // Clear current pieces for (var i = 0; i < boardSquares.length; i++) { boardSquares[i].setPiece(null); } var position = new Array(8); var availableSquares = [0, 1, 2, 3, 4, 5, 6, 7]; // Step 1: Place bishops on opposite colored squares var lightSquares = [1, 3, 5, 7]; // Light squares var darkSquares = [0, 2, 4, 6]; // Dark squares // Place first bishop on light square var lightBishopPos = lightSquares[Math.floor(Math.random() * lightSquares.length)]; position[lightBishopPos] = 'B'; availableSquares.splice(availableSquares.indexOf(lightBishopPos), 1); // Place second bishop on dark square var darkBishopPos = darkSquares[Math.floor(Math.random() * darkSquares.length)]; position[darkBishopPos] = 'B'; availableSquares.splice(availableSquares.indexOf(darkBishopPos), 1); // Step 2: Place queen randomly in remaining squares var queenPos = availableSquares[Math.floor(Math.random() * availableSquares.length)]; position[queenPos] = 'Q'; availableSquares.splice(availableSquares.indexOf(queenPos), 1); // Step 3: Place knights randomly in remaining squares var knight1Pos = availableSquares[Math.floor(Math.random() * availableSquares.length)]; position[knight1Pos] = 'N'; availableSquares.splice(availableSquares.indexOf(knight1Pos), 1); var knight2Pos = availableSquares[Math.floor(Math.random() * availableSquares.length)]; position[knight2Pos] = 'N'; availableSquares.splice(availableSquares.indexOf(knight2Pos), 1); // Step 4: Place king and rooks (king must be between rooks) // Sort remaining squares to ensure proper rook-king-rook placement availableSquares.sort(function (a, b) { return a - b; }); position[availableSquares[0]] = 'R'; // First rook position[availableSquares[1]] = 'K'; // King position[availableSquares[2]] = 'R'; // Second rook // Update board display for (var i = 0; i < 8; i++) { boardSquares[i].setPiece(pieceSymbols[position[i]]); } // Generate position notation currentPosition = position.join(''); updatePositionText(); } function calculatePositionNumber(position) { // Calculate Chess960 position number (0-959) var n = 0; var pieces = position.split(''); // Find king position var kingPos = pieces.indexOf('K'); var leftRook = pieces.indexOf('R'); var rightRook = pieces.lastIndexOf('R'); // N1: King position relative to rooks (0-3) var rookPairs = [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [3, 4], [3, 5], [3, 6], [3, 7], [4, 5], [4, 6], [4, 7], [5, 6], [5, 7], [6, 7]]; var rookPairIndex = -1; for (var i = 0; i < rookPairs.length; i++) { if (rookPairs[i][0] === leftRook && rookPairs[i][1] === rightRook) { rookPairIndex = i; break; } } var n1 = 0; for (var pos = leftRook + 1; pos < rightRook; pos++) { if (pieces[pos] === 'K') break; n1++; } // N2: Queen position (0-5) var queenPos = pieces.indexOf('Q'); var emptySquares = []; for (var i = 0; i < 8; i++) { if (pieces[i] !== 'R' && pieces[i] !== 'K') { emptySquares.push(i); } } var n2 = emptySquares.indexOf(queenPos); // N3: Light bishop position (0-3) var lightSquares = [1, 3, 5, 7]; var lightBishopPos = -1; for (var i = 0; i < lightSquares.length; i++) { if (pieces[lightSquares[i]] === 'B') { lightBishopPos = i; break; } } var n3 = lightBishopPos; // N4: Dark bishop position (0-3) var darkSquares = [0, 2, 4, 6]; var darkBishopPos = -1; for (var i = 0; i < darkSquares.length; i++) { if (pieces[darkSquares[i]] === 'B') { darkBishopPos = i; break; } } var n4 = darkBishopPos; // N5: Knight positions (0-9) var knights = []; for (var i = 0; i < 8; i++) { if (pieces[i] === 'N') { knights.push(i); } } var availableForKnights = []; for (var i = 0; i < 8; i++) { if (pieces[i] !== 'R' && pieces[i] !== 'K' && pieces[i] !== 'Q' && pieces[i] !== 'B') { availableForKnights.push(i); } } var n5 = 0; if (availableForKnights.length >= 2) { var first = availableForKnights.indexOf(knights[0]); var second = availableForKnights.indexOf(knights[1]); if (first < second) { n5 = first * (availableForKnights.length - 1) + second - 1; } else { n5 = second * (availableForKnights.length - 1) + first; } } return n4 + 4 * n3 + 16 * n5 + 96 * n2 + 576 * n1; } function updatePositionText() { if (positionText) { positionText.setText('Position: ' + currentPosition); } if (numericalText) { var posNumber = calculatePositionNumber(currentPosition); numericalText.setText('Position #' + posNumber); } } function copyPosition() { // Flash effect to show copy action LK.effects.flashScreen(0x27ae60, 300); } function resetToStandard() { var standardPosition = ['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R']; for (var i = 0; i < 8; i++) { boardSquares[i].setPiece(pieceSymbols[standardPosition[i]]); } currentPosition = standardPosition.join(''); updatePositionText(); } // Initialize board initializeBoard(); // Create title var titleText = new Text2('Chess960 Position Generator', { size: 60, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); titleText.x = 2048 / 2; titleText.y = 100; game.addChild(titleText); // Create subtitle for ranks var rankLabel = new Text2('Ranks 8 (Black) and 2 (White)', { size: 32, fill: 0xBDC3C7 }); rankLabel.anchor.set(0.5, 1); rankLabel.x = 2048 / 2; rankLabel.y = 380; game.addChild(rankLabel); // Create position display text positionText = new Text2('Position: RNBQKBNR', { size: 48, fill: 0xFFFFFF }); positionText.anchor.set(0.5, 0); positionText.x = 2048 / 2; positionText.y = 1000; game.addChild(positionText); // Create numerical position display var numericalText = new Text2('Position #518', { size: 40, fill: 0xE8C547 }); numericalText.anchor.set(0.5, 0); numericalText.x = 2048 / 2; numericalText.y = 1060; game.addChild(numericalText); // Create generate button var generateBtn = new GenerateButton(generateChess960Position); generateBtn.x = 2048 / 2; generateBtn.y = 1200; game.addChild(generateBtn); // Create copy button var copyBtn = new ActionButton('Copy Position', 'green', copyPosition); copyBtn.x = 2048 / 2 - 200; copyBtn.y = 1400; game.addChild(copyBtn); // Create reset button var resetBtn = new ActionButton('Standard Setup', 'red', resetToStandard); resetBtn.x = 2048 / 2 + 200; resetBtn.y = 1380; game.addChild(resetBtn); // Create rules title var rulesText = new Text2('Rules', { size: 36, fill: 0xFFFFFF }); rulesText.anchor.set(0.5, 0); rulesText.x = 2048 / 2; rulesText.y = 1420; game.addChild(rulesText); // Create instructions var instructionText = new Text2('Bishops on opposite colors, King between Rooks', { size: 28, fill: 0x95A5A6 }); instructionText.anchor.set(0.5, 0); instructionText.x = 2048 / 2; instructionText.y = 1460; game.addChild(instructionText); // Set initial standard position currentPosition = 'RNBQKBNR'; resetToStandard();
/****
* Classes
****/
var ActionButton = Container.expand(function (text, color, callback) {
var self = Container.call(this);
var bg = self.attachAsset(color === 'green' ? 'copyButtonBg' : 'resetButtonBg', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(text, {
size: 28,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
buttonText.y = -10;
self.addChild(buttonText);
self.callback = callback;
self.down = function (x, y, obj) {
if (self.callback) {
self.callback();
}
};
return self;
});
var ChessSquare = Container.expand(function (isLight, x, y) {
var self = Container.call(this);
var square = self.attachAsset(isLight ? 'chessSquareLight' : 'chessSquareDark', {
anchorX: 0,
anchorY: 0
});
self.squareX = x;
self.squareY = y;
self.isLight = isLight;
self.piece = null;
self.pieceText = null;
self.setPiece = function (pieceSymbol) {
if (self.pieceText) {
self.removeChild(self.pieceText);
}
if (pieceSymbol) {
self.piece = pieceSymbol;
self.pieceText = new Text2(pieceSymbol, {
size: 120,
fill: 0x000000
});
self.pieceText.anchor.set(0.5, 0.5);
self.pieceText.x = square.width / 2;
self.pieceText.y = square.height / 2;
self.addChild(self.pieceText);
} else {
self.piece = null;
self.pieceText = null;
}
};
return self;
});
var GenerateButton = Container.expand(function (callback) {
var self = Container.call(this);
var bg = self.attachAsset('buttonBg', {
anchorX: 0.5,
anchorY: 0.5
});
var text = new Text2('Generate New Position', {
size: 36,
fill: 0xFFFFFF
});
text.anchor.set(0.5, 0.5);
self.addChild(text);
self.callback = callback;
self.down = function (x, y, obj) {
if (self.callback) {
self.callback();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
var boardSquares = [];
var currentPosition = '';
var positionText = null;
// Chess piece symbols
var pieceSymbols = {
'K': '♔',
// King
'Q': '♕',
// Queen
'R': '♖',
// Rook
'B': '♗',
// Bishop
'N': '♘' // Knight
};
// Standard Chess960 pieces that need to be placed
var piecesToPlace = ['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R'];
function initializeBoard() {
var boardSize = 1600; // 8 squares * 200px each
var startX = (2048 - boardSize) / 2;
var startY = 400;
// Create 2 ranks: 8 and 2 (back rank and pawn rank)
for (var rank = 0; rank < 2; rank++) {
// Show ranks 8 and 2
for (var file = 0; file < 8; file++) {
var isLight = (rank + file) % 2 === 0;
var square = new ChessSquare(isLight, file, rank);
square.x = startX + file * 200;
square.y = startY + rank * 200;
game.addChild(square);
if (rank === 0) {
// Only store rank 8 squares for piece placement
boardSquares.push(square);
}
// Add pawns to rank 2 (white pawns) only
if (rank === 1) {
// Rank 2 - white pawns (one rank in front of pieces)
square.setPiece('♙');
}
}
}
}
function generateChess960Position() {
// Clear current pieces
for (var i = 0; i < boardSquares.length; i++) {
boardSquares[i].setPiece(null);
}
var position = new Array(8);
var availableSquares = [0, 1, 2, 3, 4, 5, 6, 7];
// Step 1: Place bishops on opposite colored squares
var lightSquares = [1, 3, 5, 7]; // Light squares
var darkSquares = [0, 2, 4, 6]; // Dark squares
// Place first bishop on light square
var lightBishopPos = lightSquares[Math.floor(Math.random() * lightSquares.length)];
position[lightBishopPos] = 'B';
availableSquares.splice(availableSquares.indexOf(lightBishopPos), 1);
// Place second bishop on dark square
var darkBishopPos = darkSquares[Math.floor(Math.random() * darkSquares.length)];
position[darkBishopPos] = 'B';
availableSquares.splice(availableSquares.indexOf(darkBishopPos), 1);
// Step 2: Place queen randomly in remaining squares
var queenPos = availableSquares[Math.floor(Math.random() * availableSquares.length)];
position[queenPos] = 'Q';
availableSquares.splice(availableSquares.indexOf(queenPos), 1);
// Step 3: Place knights randomly in remaining squares
var knight1Pos = availableSquares[Math.floor(Math.random() * availableSquares.length)];
position[knight1Pos] = 'N';
availableSquares.splice(availableSquares.indexOf(knight1Pos), 1);
var knight2Pos = availableSquares[Math.floor(Math.random() * availableSquares.length)];
position[knight2Pos] = 'N';
availableSquares.splice(availableSquares.indexOf(knight2Pos), 1);
// Step 4: Place king and rooks (king must be between rooks)
// Sort remaining squares to ensure proper rook-king-rook placement
availableSquares.sort(function (a, b) {
return a - b;
});
position[availableSquares[0]] = 'R'; // First rook
position[availableSquares[1]] = 'K'; // King
position[availableSquares[2]] = 'R'; // Second rook
// Update board display
for (var i = 0; i < 8; i++) {
boardSquares[i].setPiece(pieceSymbols[position[i]]);
}
// Generate position notation
currentPosition = position.join('');
updatePositionText();
}
function calculatePositionNumber(position) {
// Calculate Chess960 position number (0-959)
var n = 0;
var pieces = position.split('');
// Find king position
var kingPos = pieces.indexOf('K');
var leftRook = pieces.indexOf('R');
var rightRook = pieces.lastIndexOf('R');
// N1: King position relative to rooks (0-3)
var rookPairs = [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [3, 4], [3, 5], [3, 6], [3, 7], [4, 5], [4, 6], [4, 7], [5, 6], [5, 7], [6, 7]];
var rookPairIndex = -1;
for (var i = 0; i < rookPairs.length; i++) {
if (rookPairs[i][0] === leftRook && rookPairs[i][1] === rightRook) {
rookPairIndex = i;
break;
}
}
var n1 = 0;
for (var pos = leftRook + 1; pos < rightRook; pos++) {
if (pieces[pos] === 'K') break;
n1++;
}
// N2: Queen position (0-5)
var queenPos = pieces.indexOf('Q');
var emptySquares = [];
for (var i = 0; i < 8; i++) {
if (pieces[i] !== 'R' && pieces[i] !== 'K') {
emptySquares.push(i);
}
}
var n2 = emptySquares.indexOf(queenPos);
// N3: Light bishop position (0-3)
var lightSquares = [1, 3, 5, 7];
var lightBishopPos = -1;
for (var i = 0; i < lightSquares.length; i++) {
if (pieces[lightSquares[i]] === 'B') {
lightBishopPos = i;
break;
}
}
var n3 = lightBishopPos;
// N4: Dark bishop position (0-3)
var darkSquares = [0, 2, 4, 6];
var darkBishopPos = -1;
for (var i = 0; i < darkSquares.length; i++) {
if (pieces[darkSquares[i]] === 'B') {
darkBishopPos = i;
break;
}
}
var n4 = darkBishopPos;
// N5: Knight positions (0-9)
var knights = [];
for (var i = 0; i < 8; i++) {
if (pieces[i] === 'N') {
knights.push(i);
}
}
var availableForKnights = [];
for (var i = 0; i < 8; i++) {
if (pieces[i] !== 'R' && pieces[i] !== 'K' && pieces[i] !== 'Q' && pieces[i] !== 'B') {
availableForKnights.push(i);
}
}
var n5 = 0;
if (availableForKnights.length >= 2) {
var first = availableForKnights.indexOf(knights[0]);
var second = availableForKnights.indexOf(knights[1]);
if (first < second) {
n5 = first * (availableForKnights.length - 1) + second - 1;
} else {
n5 = second * (availableForKnights.length - 1) + first;
}
}
return n4 + 4 * n3 + 16 * n5 + 96 * n2 + 576 * n1;
}
function updatePositionText() {
if (positionText) {
positionText.setText('Position: ' + currentPosition);
}
if (numericalText) {
var posNumber = calculatePositionNumber(currentPosition);
numericalText.setText('Position #' + posNumber);
}
}
function copyPosition() {
// Flash effect to show copy action
LK.effects.flashScreen(0x27ae60, 300);
}
function resetToStandard() {
var standardPosition = ['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R'];
for (var i = 0; i < 8; i++) {
boardSquares[i].setPiece(pieceSymbols[standardPosition[i]]);
}
currentPosition = standardPosition.join('');
updatePositionText();
}
// Initialize board
initializeBoard();
// Create title
var titleText = new Text2('Chess960 Position Generator', {
size: 60,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.x = 2048 / 2;
titleText.y = 100;
game.addChild(titleText);
// Create subtitle for ranks
var rankLabel = new Text2('Ranks 8 (Black) and 2 (White)', {
size: 32,
fill: 0xBDC3C7
});
rankLabel.anchor.set(0.5, 1);
rankLabel.x = 2048 / 2;
rankLabel.y = 380;
game.addChild(rankLabel);
// Create position display text
positionText = new Text2('Position: RNBQKBNR', {
size: 48,
fill: 0xFFFFFF
});
positionText.anchor.set(0.5, 0);
positionText.x = 2048 / 2;
positionText.y = 1000;
game.addChild(positionText);
// Create numerical position display
var numericalText = new Text2('Position #518', {
size: 40,
fill: 0xE8C547
});
numericalText.anchor.set(0.5, 0);
numericalText.x = 2048 / 2;
numericalText.y = 1060;
game.addChild(numericalText);
// Create generate button
var generateBtn = new GenerateButton(generateChess960Position);
generateBtn.x = 2048 / 2;
generateBtn.y = 1200;
game.addChild(generateBtn);
// Create copy button
var copyBtn = new ActionButton('Copy Position', 'green', copyPosition);
copyBtn.x = 2048 / 2 - 200;
copyBtn.y = 1400;
game.addChild(copyBtn);
// Create reset button
var resetBtn = new ActionButton('Standard Setup', 'red', resetToStandard);
resetBtn.x = 2048 / 2 + 200;
resetBtn.y = 1380;
game.addChild(resetBtn);
// Create rules title
var rulesText = new Text2('Rules', {
size: 36,
fill: 0xFFFFFF
});
rulesText.anchor.set(0.5, 0);
rulesText.x = 2048 / 2;
rulesText.y = 1420;
game.addChild(rulesText);
// Create instructions
var instructionText = new Text2('Bishops on opposite colors, King between Rooks', {
size: 28,
fill: 0x95A5A6
});
instructionText.anchor.set(0.5, 0);
instructionText.x = 2048 / 2;
instructionText.y = 1460;
game.addChild(instructionText);
// Set initial standard position
currentPosition = 'RNBQKBNR';
resetToStandard();