FEN and Output Updates

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.

1. Update Global Variables

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 = {

2. Add Output Text Handlers

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');
           }
       }

3. Update loadFEN and boardToFEN

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;
       }

4. Update simulateMove

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}`;
       }

5. Update movePieceWithAnimation

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();
                   }

6. Replace SVG Click Listener

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);
                   }
               }
           }
       });

7. Clean up Step Navigation and Auto Play

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);
           });

8. Modify Edit & SVG Resets

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";