exporting version
This commit is contained in:
+3
-13
@@ -1,4 +1,4 @@
|
|||||||
import { useGame } from "../../src/useGame";
|
import { useGame, version } from "../../src";
|
||||||
import { useKeyBindings } from "./useKeyBindings";
|
import { useKeyBindings } from "./useKeyBindings";
|
||||||
import { useIsMobile } from "./useIsMobile";
|
import { useIsMobile } from "./useIsMobile";
|
||||||
import '../../src/game.css';
|
import '../../src/game.css';
|
||||||
@@ -10,7 +10,7 @@ function App() {
|
|||||||
|
|
||||||
const gameManager = useGame({ cols, rows }, useKeyBindings);
|
const gameManager = useGame({ cols, rows }, useKeyBindings);
|
||||||
const { containerRef, gameStatus, scoreManager } = gameManager;
|
const { containerRef, gameStatus, scoreManager } = gameManager;
|
||||||
const { timeValue, keystrokes, efficiency, optimalSteps, finalScore } = scoreManager;
|
const { timeValue, keystrokes, finalScore } = scoreManager;
|
||||||
|
|
||||||
const formatTime = (ms: number) => {
|
const formatTime = (ms: number) => {
|
||||||
const seconds = Math.floor(ms / 1000);
|
const seconds = Math.floor(ms / 1000);
|
||||||
@@ -21,7 +21,7 @@ function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-around min-h-[100vh]">
|
<div className="flex flex-col items-center justify-around min-h-[100vh]">
|
||||||
<h1 className="text-6xl">VIMazing vim-maze example</h1>
|
<h1 className="text-6xl">VIMazing vim-maze <small>v{version}</small> example</h1>
|
||||||
<div className="mx-auto my-4 w-fit bg-muted rounded-lg p-4">
|
<div className="mx-auto my-4 w-fit bg-muted rounded-lg p-4">
|
||||||
{(gameStatus === "started" || gameStatus === "has-key" || gameStatus === "game-won") && (
|
{(gameStatus === "started" || gameStatus === "has-key" || gameStatus === "game-won") && (
|
||||||
<div className="flex gap-8 text-sm font-mono items-center">
|
<div className="flex gap-8 text-sm font-mono items-center">
|
||||||
@@ -33,16 +33,6 @@ function App() {
|
|||||||
<div className="text-muted-foreground">Keystrokes</div>
|
<div className="text-muted-foreground">Keystrokes</div>
|
||||||
<div className="text-lg font-bold">{keystrokes}</div>
|
<div className="text-lg font-bold">{keystrokes}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<div className="text-muted-foreground">Efficiency</div>
|
|
||||||
<div className={`text-lg font-bold ${efficiency > 150 ? 'text-red-500' : efficiency > 100 ? 'text-yellow-500' : 'text-green-500'}`}>
|
|
||||||
{efficiency}%
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-muted-foreground">Optimal</div>
|
|
||||||
<div className="text-lg font-bold">{optimalSteps}</div>
|
|
||||||
</div>
|
|
||||||
{gameStatus === "game-won" && finalScore && (
|
{gameStatus === "game-won" && finalScore && (
|
||||||
<>
|
<>
|
||||||
<div className="w-px h-12 bg-muted-foreground/20" />
|
<div className="w-px h-12 bg-muted-foreground/20" />
|
||||||
|
|||||||
@@ -2,4 +2,6 @@ export * from './types';
|
|||||||
export * from './useGame';
|
export * from './useGame';
|
||||||
export * from './useGameStatus';
|
export * from './useGameStatus';
|
||||||
export { gameInfo } from './gameInfo';
|
export { gameInfo } from './gameInfo';
|
||||||
|
import { version } from '../package.json';
|
||||||
export type { GameInfo } from './gameInfo';
|
export type { GameInfo } from './gameInfo';
|
||||||
|
export { version };
|
||||||
|
|||||||
+122
-117
@@ -1,7 +1,6 @@
|
|||||||
import { useRef, useEffect } from 'react'
|
import { useRef, useEffect } from 'react'
|
||||||
import type { CursorMode, Coord } from './types';
|
import type { CursorMode, Coord } from './types';
|
||||||
import type { BoardManager } from '../useBoard/types';
|
import type { GameStatusManager, BoardManager, CellTag } from '../types';
|
||||||
import type { GameStatusManager } from '../useGameStatus/types';
|
|
||||||
import { useHero } from './useHero';
|
import { useHero } from './useHero';
|
||||||
import { useGameKeys } from './useGameKeys';
|
import { useGameKeys } from './useGameKeys';
|
||||||
import { useMazeNavigation } from '../useBoard';
|
import { useMazeNavigation } from '../useBoard';
|
||||||
@@ -16,6 +15,9 @@ export function useCursor(board: BoardManager, gameStatusManager: GameStatusMana
|
|||||||
const navigator = useMazeNavigation(board.mazeRef.current);
|
const navigator = useMazeNavigation(board.mazeRef.current);
|
||||||
const vimMotions = useVimMotions();
|
const vimMotions = useVimMotions();
|
||||||
|
|
||||||
|
const maze = board.mazeInstanceRef.current?.maze;
|
||||||
|
const exitPos = maze?.[0].findIndex((cell: CellTag) => cell.includes('door'));
|
||||||
|
|
||||||
// Sync cursor position with hero position
|
// Sync cursor position with hero position
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hero.heroPos) {
|
if (hero.heroPos) {
|
||||||
@@ -61,98 +63,101 @@ 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 hasKey = gameStatusManager.gameStatus === 'has-key';
|
const hasKey = gameStatusManager.gameStatus === 'has-key';
|
||||||
const anchorTarget = navigator.findAnchorTarget(currentPos, direction, hasKey);
|
const anchorTarget = navigator.findAnchorTarget(currentPos, direction, hasKey);
|
||||||
if (!anchorTarget) return;
|
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;
|
||||||
const target = navigator.findAnchorTarget(currentPos, 'left');
|
const target = navigator.findAnchorTarget(currentPos, 'left');
|
||||||
if (!target) {
|
if (!target) {
|
||||||
window.dispatchEvent(new Event("maze-invalid"));
|
window.dispatchEvent(new Event("maze-invalid"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dr = target.row > currentPos.row ? 1 : target.row < currentPos.row ? -1 : 0;
|
const dr = target.row > currentPos.row ? 1 : target.row < currentPos.row ? -1 : 0;
|
||||||
const dc = target.col > currentPos.col ? 1 : target.col < currentPos.col ? -1 : 0;
|
const dc = target.col > currentPos.col ? 1 : target.col < currentPos.col ? -1 : 0;
|
||||||
const steps = Math.abs(target.row - currentPos.row) + Math.abs(target.col - currentPos.col);
|
const steps = Math.abs(target.row - currentPos.row) + Math.abs(target.col - currentPos.col);
|
||||||
if (!navigator.validatePath(currentPos, { row: dr, col: dc }, steps)) {
|
if (!navigator.validatePath(currentPos, { row: dr, col: dc }, steps)) {
|
||||||
window.dispatchEvent(new Event("maze-invalid"));
|
window.dispatchEvent(new Event("maze-invalid"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
moveToAnchor('start');
|
moveToAnchor('start');
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveToEnd(): void {
|
function moveToEnd(): void {
|
||||||
const currentPos = position.current;
|
const currentPos = position.current;
|
||||||
const target = navigator.findAnchorTarget(currentPos, 'right');
|
const target = navigator.findAnchorTarget(currentPos, 'right');
|
||||||
if (!target) {
|
if (!target) {
|
||||||
window.dispatchEvent(new Event("maze-invalid"));
|
window.dispatchEvent(new Event("maze-invalid"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dr = target.row > currentPos.row ? 1 : target.row < currentPos.row ? -1 : 0;
|
const dr = target.row > currentPos.row ? 1 : target.row < currentPos.row ? -1 : 0;
|
||||||
const dc = target.col > currentPos.col ? 1 : target.col < currentPos.col ? -1 : 0;
|
const dc = target.col > currentPos.col ? 1 : target.col < currentPos.col ? -1 : 0;
|
||||||
const steps = Math.abs(target.row - currentPos.row) + Math.abs(target.col - currentPos.col);
|
const steps = Math.abs(target.row - currentPos.row) + Math.abs(target.col - currentPos.col);
|
||||||
if (!navigator.validatePath(currentPos, { row: dr, col: dc }, steps)) {
|
if (!navigator.validatePath(currentPos, { row: dr, col: dc }, steps)) {
|
||||||
window.dispatchEvent(new Event("maze-invalid"));
|
window.dispatchEvent(new Event("maze-invalid"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
moveToAnchor('end');
|
moveToAnchor('end');
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveToTop(): void {
|
function moveToTop(): void {
|
||||||
const currentPos = position.current;
|
const currentPos = position.current;
|
||||||
const target = navigator.findAnchorTarget(currentPos, 'top');
|
|
||||||
if (!target) {
|
|
||||||
window.dispatchEvent(new Event("maze-invalid"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const dr = target.row > currentPos.row ? 1 : target.row < currentPos.row ? -1 : 0;
|
|
||||||
const dc = target.col > currentPos.col ? 1 : target.col < currentPos.col ? -1 : 0;
|
|
||||||
const steps = Math.abs(target.row - currentPos.row) + Math.abs(target.col - currentPos.col);
|
|
||||||
if (!navigator.validatePath(currentPos, { row: dr, col: dc }, steps)) {
|
|
||||||
window.dispatchEvent(new Event("maze-invalid"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
moveToAnchor('top');
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveToBottom(): void {
|
console.log("Exit position:", exitPos, currentPos);
|
||||||
const currentPos = position.current;
|
console.log(currentPos, board);
|
||||||
const target = navigator.findAnchorTarget(currentPos, 'bottom');
|
const target = navigator.findAnchorTarget(currentPos, 'top');
|
||||||
if (!target) {
|
if (!target) {
|
||||||
window.dispatchEvent(new Event("maze-invalid"));
|
window.dispatchEvent(new Event("maze-invalid"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dr = target.row > currentPos.row ? 1 : target.row < currentPos.row ? -1 : 0;
|
const dr = target.row > currentPos.row ? 1 : target.row < currentPos.row ? -1 : 0;
|
||||||
const dc = target.col > currentPos.col ? 1 : target.col < currentPos.col ? -1 : 0;
|
const dc = target.col > currentPos.col ? 1 : target.col < currentPos.col ? -1 : 0;
|
||||||
const steps = Math.abs(target.row - currentPos.row) + Math.abs(target.col - currentPos.col);
|
const steps = Math.abs(target.row - currentPos.row) + Math.abs(target.col - currentPos.col);
|
||||||
if (!navigator.validatePath(currentPos, { row: dr, col: dc }, steps)) {
|
if (!navigator.validatePath(currentPos, { row: dr, col: dc }, steps)) {
|
||||||
window.dispatchEvent(new Event("maze-invalid"));
|
window.dispatchEvent(new Event("maze-invalid"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
moveToAnchor('bottom');
|
moveToAnchor('top');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function moveToBottom(): void {
|
||||||
|
const currentPos = position.current;
|
||||||
|
const target = navigator.findAnchorTarget(currentPos, 'bottom');
|
||||||
|
if (!target) {
|
||||||
|
window.dispatchEvent(new Event("maze-invalid"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const dr = target.row > currentPos.row ? 1 : target.row < currentPos.row ? -1 : 0;
|
||||||
|
const dc = target.col > currentPos.col ? 1 : target.col < currentPos.col ? -1 : 0;
|
||||||
|
const steps = Math.abs(target.row - currentPos.row) + Math.abs(target.col - currentPos.col);
|
||||||
|
if (!navigator.validatePath(currentPos, { row: dr, col: dc }, steps)) {
|
||||||
|
window.dispatchEvent(new Event("maze-invalid"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveToAnchor('bottom');
|
||||||
|
}
|
||||||
|
|
||||||
function repeatLastMotion(): void {
|
function repeatLastMotion(): void {
|
||||||
const motion = vimMotions.repeatLastMotion();
|
const motion = vimMotions.repeatLastMotion();
|
||||||
@@ -176,17 +181,17 @@ export function useCursor(board: BoardManager, gameStatusManager: GameStatusMana
|
|||||||
vimMotions.resetCount();
|
vimMotions.resetCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCount(): string {
|
function getCount(): string {
|
||||||
return String(vimMotions.getCount());
|
return String(vimMotions.getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasCount(): boolean {
|
function hasCount(): boolean {
|
||||||
return vimMotions.hasCount();
|
return vimMotions.hasCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCount(digit: string): void {
|
function setCount(digit: string): void {
|
||||||
vimMotions.processKey(digit);
|
vimMotions.processKey(digit);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLastKey(key: string): void {
|
function setLastKey(key: string): void {
|
||||||
lastKeyRef.current = key;
|
lastKeyRef.current = key;
|
||||||
@@ -196,27 +201,27 @@ export function useCursor(board: BoardManager, gameStatusManager: GameStatusMana
|
|||||||
return lastKeyRef.current;
|
return lastKeyRef.current;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cursor = {
|
const cursor = {
|
||||||
position: () => position.current,
|
position: () => position.current,
|
||||||
mode: () => mode.current,
|
mode: () => mode.current,
|
||||||
move,
|
move,
|
||||||
moveLeft,
|
moveLeft,
|
||||||
moveRight,
|
moveRight,
|
||||||
moveUp,
|
moveUp,
|
||||||
moveDown,
|
moveDown,
|
||||||
moveToStart,
|
moveToStart,
|
||||||
moveToEnd,
|
moveToEnd,
|
||||||
moveToTop,
|
moveToTop,
|
||||||
moveToBottom,
|
moveToBottom,
|
||||||
repeatLastMotion,
|
repeatLastMotion,
|
||||||
resetCount,
|
resetCount,
|
||||||
getCount,
|
getCount,
|
||||||
hasCount,
|
hasCount,
|
||||||
setCount,
|
setCount,
|
||||||
setLastKey,
|
setLastKey,
|
||||||
getLastKey,
|
getLastKey,
|
||||||
hero,
|
hero,
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyManager = useGameKeys({ cursor, gameStatusManager });
|
const keyManager = useGameKeys({ cursor, gameStatusManager });
|
||||||
|
|
||||||
|
|||||||
@@ -8,17 +8,24 @@ export function useTimer(): TimerManager {
|
|||||||
const startTimeRef = useRef<number | null>(null);
|
const startTimeRef = useRef<number | null>(null);
|
||||||
const frameRef = useRef<number | null>(null);
|
const frameRef = useRef<number | null>(null);
|
||||||
const runningRef = useRef(false);
|
const runningRef = useRef(false);
|
||||||
|
const lastUpdateRef = useRef(0);
|
||||||
|
const currentTimeRef = useRef(0);
|
||||||
|
|
||||||
const startTimer = () => {
|
const startTimer = () => {
|
||||||
if (runningRef.current) return;
|
if (runningRef.current) return;
|
||||||
runningRef.current = true;
|
runningRef.current = true;
|
||||||
startTimeRef.current = performance.now();
|
startTimeRef.current = performance.now();
|
||||||
|
lastUpdateRef.current = 0;
|
||||||
|
|
||||||
const tick = () => {
|
const tick = () => {
|
||||||
if (!runningRef.current) return;
|
if (!runningRef.current) return;
|
||||||
const now = performance.now();
|
const now = performance.now();
|
||||||
const elapsed = now - (startTimeRef.current ?? now);
|
const elapsed = now - (startTimeRef.current ?? now);
|
||||||
setTimeValue(elapsed);
|
currentTimeRef.current = elapsed;
|
||||||
|
if (elapsed - lastUpdateRef.current >= 1000) {
|
||||||
|
lastUpdateRef.current = elapsed;
|
||||||
|
setTimeValue(elapsed);
|
||||||
|
}
|
||||||
frameRef.current = requestAnimationFrame(tick);
|
frameRef.current = requestAnimationFrame(tick);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -26,6 +33,7 @@ export function useTimer(): TimerManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const stopTimer = () => {
|
const stopTimer = () => {
|
||||||
|
setTimeValue(currentTimeRef.current);
|
||||||
runningRef.current = false;
|
runningRef.current = false;
|
||||||
if (frameRef.current) {
|
if (frameRef.current) {
|
||||||
cancelAnimationFrame(frameRef.current);
|
cancelAnimationFrame(frameRef.current);
|
||||||
@@ -37,6 +45,7 @@ export function useTimer(): TimerManager {
|
|||||||
stopTimer();
|
stopTimer();
|
||||||
setTimeValue(0);
|
setTimeValue(0);
|
||||||
startTimeRef.current = null;
|
startTimeRef.current = null;
|
||||||
|
lastUpdateRef.current = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -45,5 +54,7 @@ export function useTimer(): TimerManager {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return { timeValue, startTimer, stopTimer, resetTimer };
|
console.log('timeValue', timeValue);
|
||||||
|
|
||||||
|
return { timeValue, startTimer, stopTimer, resetTimer };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user