Here are the modifications to implement the new FEN format (rotate, flip, halfMoveClock, fullMoveNumber) and the updated text output logic for ejceesoutput.
Find the corresponding sections in your code and replace them with the provided snippets.
Replace INITIAL_FEN and add the new global state variables (around line 324):
JavaScript
const INITIAL_FEN = 'rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1';
// ... existing maps ...
let currentTurn = 'w';
let halfMoveClock = 0;
let fullMoveNumber = 1;
let outputTimeout = null;
let historyFEN = {
Insert these two new functions anywhere in the DOMContentLoaded scope (e.g., right before the loadFEN function):
JavaScript
function setOutputText(text, temp = false) {
const outputElement = document.querySelector('.ejceesoutput');
if (outputElement) {
outputElement.textContent = text;
}
if (outputTimeout) {
clearTimeout(outputTimeout);
outputTimeout = null;
}
if (temp) {
outputTimeout = setTimeout(() => {
restoreOutputText();
}, moveInterval * 2000);
}
}
function restoreOutputText() {
const node = getNodeAtStep(currentStepIndex);
if (node && node.lastMove && node.move) {
setOutputText(`(${node.lastMove.startX},${node.lastMove.startY}) -> (${node.lastMove.endX},${node.lastMove.endY}) : ${node.move}`);
} else {
setOutputText('Game start');
}
}
Update the FEN parsers and generators to include the new fields.
In loadFEN:
JavaScript
function loadFEN(fenStr) {
// Cancel states
isAnimating = false;
if (pickedPieceId) setPickup(pickedPieceId, false);
pickedPieceId = null;
// Clear board DOM and Map
// ... existing clear logic ...
tileMap.clear();
piecePos.clear();
const parts = fenStr.split(' ');
const boardPart = parts[0];
currentTurn = parts[1] || 'w';
halfMoveClock = parts[4] !== undefined ? parseInt(parts[4], 10) : 0;
fullMoveNumber = parts[5] !== undefined ? parseInt(parts[5], 10) : 1;
const rows = boardPart.split('/');
// ...
In boardToFEN:
JavaScript
function boardToFEN() {
let fen = '';
for (let y = 0; y <= 9; y++) {
// ... existing loop logic ...
if (emptyCount > 0) fen += emptyCount;
if (y < 9) fen += '/';
}
fen += ` ${currentTurn}`;
fen += ` ${isRotateEnabled ? '1' : '-'}`;
fen += ` ${isFlipEnabled ? '1' : '-'}`;
fen += ` ${halfMoveClock}`;
fen += ` ${fullMoveNumber}`;
return fen;
}
To properly inherit and iterate the clock counts during deep branch generation:
JavaScript
function simulateMove(fen, derive_coords) {
const parts = fen.split(' ');
const boardPart = parts[0];
let turn = parts[1] || 'w';
const rotateFlag = parts[2] || '-';
const flipFlag = parts[3] || '-';
let halfMove = parts[4] !== undefined ? parseInt(parts[4], 10) : 0;
let fullMove = parts[5] !== undefined ? parseInt(parts[5], 10) : 1;
// 1. Initialize an empty 10x9 board
// ... (keep the existing board array setup) ...
// 3. Extract the moving piece
const piece = board[startY][startX];
const targetSquare = board[endY][endX];
if (!piece) {
console.warn(`simulateMove: No piece found at (${startX}, ${startY})`);
return fen;
}
// 4. Apply the move
board[startY][startX] = '';
board[endY][endX] = piece;
if (targetSquare !== '') {
halfMove = 0;
} else {
halfMove++;
}
if (turn === 'b') {
fullMove++;
}
// 5. Toggle the turn
turn = (turn === 'w') ? 'b' : 'w';
// 6. Generate the new FEN string
let newFenBoard = '';
// ... (keep the existing board generation loop) ...
return `${newFenBoard} ${turn} ${rotateFlag} ${flipFlag} ${halfMove} ${fullMove}`;
}
Ensure the clocks are updated upon physical movement. In movePieceWithAnimation, replace the onComplete block:
JavaScript
// Final visual position
pieceEl.setAttribute('transform', `translate(${endXpx}, ${endYpx})`);
setPickup(pieceId, false);
isAnimating = false;
if (onComplete) {
onComplete();
} else {
if (eatenId) {
halfMoveClock = 0;
} else {
halfMoveClock++;
}
if (currentTurn === 'b') {
fullMoveNumber++;
}
currentTurn = currentTurn === 'w' ? 'b' : 'w';
pushHistory(moveText, moveData);
applyLastMoveVisuals(moveData);
restoreOutputText();
}
This incorporates the coordinate tracking and invalid move notifications. Replace your entire svgElement.addEventListener('click', ...) block with this:
JavaScript
svgElement.addEventListener('click', (event) => {
if (isAnimating || isEditingComment || isExportTextMode || isRangeMode || isAutoPlaying) return;
const pt = svgElement.createSVGPoint();
pt.x = event.clientX;
pt.y = event.clientY;
const cursorPt = pt.matrixTransform(svgElement.getScreenCTM().inverse());
const clickX = Math.floor(cursorPt.x);
const clickY = Math.floor(cursorPt.y);
if (clickX < 0 || clickX >= 432 || clickY < 0 || clickY >= 480) return;
const gridX_vis = Math.floor(clickX / 48);
const gridY_vis = Math.floor(clickY / 48);
const logicalPt = getLogicalCoords(gridX_vis, gridY_vis);
const gridX = logicalPt.x;
const gridY = logicalPt.y;
const clickedId = tileMap.get(`${gridX},${gridY}`);
// Handle Edit Mode Clicks
if (isEditMode) {
handleEditBoardClick(gridX, gridY, clickedId);
return;
}
if (!pickedPieceId) {
if (clickedId) {
const isWhitePiece = clickedId[0] === clickedId[0].toUpperCase();
if ((currentTurn === 'w' && isWhitePiece) || (currentTurn === 'b' && !isWhitePiece)) {
pickedPieceId = clickedId;
setPickup(pickedPieceId, true);
setOutputText(`(${gridX},${gridY}) - ID: ${clickedId}`);
} else {
setOutputText("Not your turn.", true);
}
}
} else {
if (pickedPieceId === clickedId) {
// Unpickup
setPickup(pickedPieceId, false);
pickedPieceId = null;
restoreOutputText();
} else if (clickedId && isSameTeam(pickedPieceId, clickedId)) {
// Switch pickup
setPickup(pickedPieceId, false);
pickedPieceId = clickedId;
setPickup(pickedPieceId, true);
setOutputText(`(${gridX},${gridY}) - ID: ${clickedId}`);
} else {
// Attempt Move/Eat
const startPos = piecePos.get(pickedPieceId);
if (!checkMoveGeometry(pickedPieceId, startPos.x, startPos.y, gridX, gridY, tileMap)) {
setOutputText("Invalid move: Violates rules.", true);
setPickup(pickedPieceId, false);
pickedPieceId = null;
} else if (!isValidMove(pickedPieceId, startPos.x, startPos.y, gridX, gridY)) {
setOutputText("Invalid move: King is under attack.", true);
setPickup(pickedPieceId, false);
pickedPieceId = null;
} else {
const movingPieceId = pickedPieceId;
pickedPieceId = null;
movePieceWithAnimation(movingPieceId, gridX, gridY, clickedId);
}
}
}
});
Update jumpToStep to reflect the text layout changes:
In jumpToStep:
JavaScript
function jumpToStep(index) {
if (isEditingComment) return; // Block slider/click execution
if (isRangeMode && !isAutoPlaying) {
// ... (keep the existing range logic) ...
applyRangeBorders();
return; // Intercept jump
}
const totalDepth = getPathDepth(historyFEN, currentBranch);
if (index >= 0 && index < totalDepth) {
currentStepIndex = index;
stepSlider.value = index;
const stepData = getNodeAtStep(index);
if (stepData) {
loadFEN(stepData.fen);
applyLastMoveVisuals(stepData.lastMove);
highlightActiveStep(index);
restoreOutputText();
renderNoteUI();
}
}
}
In playNextAnimated (update the onComplete arrow function inside it):
JavaScript
// Trigger animation
movePieceWithAnimation(pieceId, moveData.endX, moveData.endY, eatenId, () => {
currentTurn = currentTurn === 'w' ? 'b' : 'w';
applyLastMoveVisuals(moveData);
currentStepIndex = nextStepIndex;
stepSlider.value = currentStepIndex;
highlightActiveStep(currentStepIndex);
renderNoteUI();
restoreOutputText();
// Chain the next animated move
setTimeout(playNextAnimated, moveInterval * 1000);
});
When initializing edits or reconstructing SVG, reset the half/full clocks so new setups begin cleanly:
In exitEditModeConfirm:
JavaScript
function exitEditModeConfirm() {
isEditMode = false;
halfMoveClock = 0;
fullMoveNumber = 1;
const finalFEN = boardToFEN();
historyFEN = {
// ...
In importExportedSVG:
JavaScript
// ... (Around line 1618, when declaring the base FEN)
let initialTurn = 'w';
if (firstMovePieceId && firstMovePieceId[0] === firstMovePieceId[0].toLowerCase()) {
initialTurn = 'b';
}
const baseFen = initialFenBoard + " " + initialTurn + " - - 0 1";