Re: 將製作ejcees(中國象棋打譜程式)
发表于 : 2026年 2月 21日 15:55
根據盤面及走法,得到所移動的棋子起始和結束坐標:
代码: 全选
/**
* Advanced: Derives coordinates from FEN and English notation (supports handling ambiguity for multiple pawns)
* Supported formats: C2=5, +2=3, =P+1, 3P=9, etc.
*/
function deriveCoordsFromEN(fen, moveEN) {
if (!moveEN) return null;
// 1. Parse FEN to get the board array and the current turn
const parts = fen.split(' ');
const isRed = (parts[1] === 'w');
const board = parseFENToBoard(parts[0]); // Convert to 10x9 array
// 2. Parse English move string
// Format parsing: [prefix?][piece/col?][action][target value]
const match = moveEN.match(/^([+\-=1-9])?([a-zA-Z1-9])?([+\-=])(\d)$/);
if (!match) return null;
let prefix = match[1]; // +, -, =, 1-9 (representing front, back, middle, or ordinal number)
let indicator = match[2]; // Piece code (e.g., P) or starting column (e.g., 2)
const action = match[3]; // +, -, =
const targetVal = parseInt(match[4], 10);
// 3. Collect all pieces of the same type belonging to the current side on the board
let searchChar = (indicator && /[a-zA-Z]/.test(indicator)) ?
(isRed ? indicator.toUpperCase() : indicator.toLowerCase()) :
(isRed ? 'P' : 'p'); // Default to pawn
let candidates = [];
for (let y = 0; y < 10; y++) {
for (let x = 0; x < 9; x++) {
if (board[y][x] === searchChar) {
candidates.push({ x, y, col: isRed ? (9 - x) : (x + 1) });
}
}
}
// 4. Filter starting piece (startX, startY) based on ambiguity rules
let piece = null;
// Case A: Starting column specified (e.g., "2" in C2=5 or +2=3)
if (indicator && /[1-9]/.test(indicator)) {
const startCol = parseInt(indicator, 10);
let colPieces = candidates.filter(p => p.col === startCol);
if (prefix) {
// Handle cases like "+2=3" (front-2-move-to-3)
sortPiecesFrontToBack(colPieces, isRed);
const idx = parsePrefixToIndex(prefix, colPieces.length);
piece = colPieces[idx];
} else {
// Handle cases like "C2=5"
piece = colPieces[0];
}
}
// Case B: Special prefix used (e.g., =P+1 or 3P=9)
else if (prefix) {
// First, identify columns with multiple pieces
let multiCols = [];
for(let c=1; c<=9; c++) {
if (candidates.filter(p => p.col === c).length > 1) multiCols.push(c);
}
if (multiCols.length > 1 && prefix !== '=') {
// This is the "two-column pawn" rule: prefix represents "front/back", indicator represents "which column"
// If indicator is P, we need to search all candidates
sortPiecesFrontToBack(candidates, isRed);
const idx = parsePrefixToIndex(prefix, candidates.length);
piece = candidates[idx];
} else {
// Single-column, multiple-piece rule (front, middle, back)
sortPiecesFrontToBack(candidates, isRed);
const idx = parsePrefixToIndex(prefix, candidates.length);
piece = candidates[idx];
}
}
if (!piece) return null;
// 5. Calculate target coordinates (endX, endY)
const startX = piece.x;
const startY = piece.y;
let endX = startX, endY = startY;
if (action === '=') {
endX = isRed ? (9 - targetVal) : (targetVal - 1);
} else {
const direction = isRed ? -1 : 1; // Red moves decrease Y, Black moves increase Y
const sign = (action === '+') ? 1 : -1;
const isJumping = ['N','B','A'].includes(searchChar.toUpperCase());
if (isJumping) {
endX = isRed ? (9 - targetVal) : (targetVal - 1);
const dx = Math.abs(endX - startX);
const dy = (searchChar.toUpperCase() === 'N') ? (dx === 1 ? 2 : 1) : (dx === 2 ? 2 : 1);
endY = startY + (direction * sign * dy);
} else {
endY = startY + (direction * sign * targetVal);
}
}
return { startX, startY, endX, endY };
}
/** Helper: Parse FEN into an array */
function parseFENToBoard(fenPart) {
return fenPart.split('/').map(row => {
let line = [];
for (let char of row) {
if (/\d/.test(char)) line.push(...Array(parseInt(char)).fill(null));
else line.push(char);
}
return line;
});
}
/** Helper: Sort pieces based on Xiangqi rules (from front to back) */
function sortPiecesFrontToBack(pieces, isRed) {
// Red: Smaller Y is closer to the front
// Black: Larger Y is closer to the front
if (isRed) pieces.sort((a, b) => a.y - b.y);
else pieces.sort((a, b) => b.y - a.y);
}
/** Helper: Convert prefix symbol to array index */
function parsePrefixToIndex(prefix, total) {
if (prefix === '+') return 0; // Front
if (prefix === '-') return total - 1; // Back
if (prefix === '=') return 1; // Middle (assuming 3 pieces)
if (/[1-9]/.test(prefix)) return parseInt(prefix, 10) - 1; // 1-9 ordinal
return 0;
}