fix: allow anchor moves to reach exit with key

Update findAnchorTarget to accept optional hasKey parameter. When player has the key and moving to an anchor (gg, G, ^, or $), if an exit is positioned at the edge, allow movement to reach it. This matches behavior of counted moves like 5k that can reach edge-positioned exits.
This commit is contained in:
2025-10-23 20:45:44 +01:00
parent f79d9d1c65
commit 87da1f999a
3 changed files with 84 additions and 63 deletions
+1 -1
View File
@@ -27,6 +27,6 @@ export type BoardManager = {
export type MazeNavigator = { export type MazeNavigator = {
isValidMove: (from: Coord, to: Coord) => boolean; isValidMove: (from: Coord, to: Coord) => boolean;
findAnchorTarget: (from: Coord, direction: 'left' | 'right' | 'top' | 'bottom') => Coord | null; findAnchorTarget: (from: Coord, direction: 'left' | 'right' | 'top' | 'bottom', hasKey?: boolean) => Coord | null;
validatePath: (from: Coord, direction: Coord, steps: number) => boolean; validatePath: (from: Coord, direction: Coord, steps: number) => boolean;
}; };
+62 -42
View File
@@ -10,57 +10,77 @@ export function useMazeNavigation(maze: MazeCell[][]): MazeNavigator {
return true; return true;
} }
function findAnchorTarget(from: Coord, direction: 'left'|'right'|'top'|'bottom'): Coord | null { function findAnchorTarget(from: Coord, direction: 'left'|'right'|'top'|'bottom', hasKey: boolean = false): Coord | null {
if (!maze.length) return null; if (!maze.length) return null;
const rowsCount = maze.length; const rowsCount = maze.length;
const colsCount = maze[0]?.length ?? 0; const colsCount = maze[0]?.length ?? 0;
const { row: r, col: c } = from; const { row: r, col: c } = from;
if (!maze[r] || !maze[r][c] || maze[r][c].includes('wall')) return null; if (!maze[r] || !maze[r][c] || maze[r][c].includes('wall')) return null;
if (direction === 'left' || direction === 'right') { if (direction === 'left' || direction === 'right') {
const step = direction === 'left' ? -1 : 1; const step = direction === 'left' ? -1 : 1;
let cc = c; let cc = c;
let edgeCell: Coord | null = null;
while (true) { while (true) {
const next = cc + step; const next = cc + step;
if (next < 0 || next >= colsCount) return null; if (next < 0 || next >= colsCount) return null;
if (maze[r][next].includes('wall')) { if (maze[r][next].includes('wall')) {
const wallIdx = next; const wallIdx = next;
const expectedWallIdx = direction === 'left' ? 0 : colsCount - 1; const expectedWallIdx = direction === 'left' ? 0 : colsCount - 1;
const expectedCellIdx = direction === 'left' ? 1 : colsCount - 2; const expectedCellIdx = direction === 'left' ? 1 : colsCount - 2;
if (wallIdx !== expectedWallIdx) return null; if (wallIdx !== expectedWallIdx) return null;
if (cc !== expectedCellIdx) return null; if (cc !== expectedCellIdx) return null;
if (cc === c) return null; if (cc === c) return null;
return { row: r, col: cc }; edgeCell = { row: r, col: cc };
}
cc = next; if (hasKey && wallIdx === expectedWallIdx) {
} const wallPos = { row: r, col: wallIdx };
} else { if (maze[wallPos.row]?.[wallPos.col]?.includes('exit')) {
const step = direction === 'top' ? -1 : 1; return wallPos;
let rr = r; }
}
return edgeCell;
}
cc = next;
}
} else {
const step = direction === 'top' ? -1 : 1;
let rr = r;
let edgeCell: Coord | null = null;
while (true) { while (true) {
const next = rr + step; const next = rr + step;
if (next < 0 || next >= rowsCount) return null; if (next < 0 || next >= rowsCount) return null;
if (maze[next][c].includes('wall')) { if (maze[next][c].includes('wall')) {
const wallIdx = next; const wallIdx = next;
const expectedWallIdx = direction === 'top' ? 0 : rowsCount - 1; const expectedWallIdx = direction === 'top' ? 0 : rowsCount - 1;
const expectedCellIdx = direction === 'top' ? 1 : rowsCount - 2; const expectedCellIdx = direction === 'top' ? 1 : rowsCount - 2;
if (wallIdx !== expectedWallIdx) return null; if (wallIdx !== expectedWallIdx) return null;
if (rr !== expectedCellIdx) return null; if (rr !== expectedCellIdx) return null;
if (rr === r) return null; if (rr === r) return null;
return { row: rr, col: c }; edgeCell = { row: rr, col: c };
}
rr = next; if (hasKey && wallIdx === expectedWallIdx) {
} const wallPos = { row: wallIdx, col: c };
} if (maze[wallPos.row]?.[wallPos.col]?.includes('exit')) {
} return wallPos;
}
}
return edgeCell;
}
rr = next;
}
}
}
function validatePath(from: Coord, direction: Coord, steps: number): boolean { function validatePath(from: Coord, direction: Coord, steps: number): boolean {
if (!maze.length) return false; if (!maze.length) return false;
+21 -20
View File
@@ -61,29 +61,30 @@ export function useCursor(board: BoardManager, gameStatusManager: GameStatusMana
move(0, 1, count); move(0, 1, count);
} }
function moveToAnchor(target: 'start' | 'end' | 'top' | 'bottom'): void { function moveToAnchor(target: 'start' | 'end' | 'top' | 'bottom'): void {
const currentPos = position.current; const currentPos = position.current;
const direction = target === 'start' ? 'left' : const direction = target === 'start' ? 'left' :
target === 'end' ? 'right' : target === 'end' ? 'right' :
target === 'top' ? 'top' : 'bottom'; target === 'top' ? 'top' : 'bottom';
const anchorTarget = navigator.findAnchorTarget(currentPos, direction); const hasKey = gameStatusManager.gameStatus === 'has-key';
if (!anchorTarget) return; const anchorTarget = navigator.findAnchorTarget(currentPos, direction, hasKey);
if (!anchorTarget) return;
const dr = anchorTarget.row > currentPos.row ? 1 : const dr = anchorTarget.row > currentPos.row ? 1 :
anchorTarget.row < currentPos.row ? -1 : 0; anchorTarget.row < currentPos.row ? -1 : 0;
const dc = anchorTarget.col > currentPos.col ? 1 : const dc = anchorTarget.col > currentPos.col ? 1 :
anchorTarget.col < currentPos.col ? -1 : 0; anchorTarget.col < currentPos.col ? -1 : 0;
const steps = Math.abs(anchorTarget.row - currentPos.row) + Math.abs(anchorTarget.col - currentPos.col); const steps = Math.abs(anchorTarget.row - currentPos.row) + Math.abs(anchorTarget.col - currentPos.col);
position.current = anchorTarget; position.current = anchorTarget;
if (hero && hero.heroPos) { if (hero && hero.heroPos) {
hero.moveTo(anchorTarget); hero.moveTo(anchorTarget);
if (steps > 0) { if (steps > 0) {
vimMotions.setLastMotion({ dr, dc, steps }); vimMotions.setLastMotion({ dr, dc, steps });
} }
} }
} }
function moveToStart(): void { function moveToStart(): void {
const currentPos = position.current; const currentPos = position.current;