Files
drinkmarble/app/game/chessman.ts

95 lines
2.6 KiB
TypeScript

/** 주사위판 위를 이동하는 말. */
export class Chessman {
private index: number;
private readonly boardLength: number;
/** 1: 시계방향, -1: 반시계방향 */
private direction: 1 | -1 = 1;
/** 무인도: 남은 대기 턴, 탈출 가능 눈 */
private island: { turnsLeft: number; escapeOn: number } | null = null;
constructor(boardLength: number, startIndex = 0) {
if (boardLength <= 0) {
throw new Error('Chessman boardLength must be positive');
}
this.boardLength = boardLength;
this.index = ((startIndex % boardLength) + boardLength) % boardLength;
}
get position(): number {
return this.index;
}
get facing(): 1 | -1 {
return this.direction;
}
get isOnIsland(): boolean {
return this.island != null;
}
get islandTurnsLeft(): number {
return this.island?.turnsLeft ?? 0;
}
reverseDirection(): 1 | -1 {
this.direction = this.direction === 1 ? -1 : 1;
return this.direction;
}
enterIsland(turns: number, escapeOn = 6) {
this.island = { turnsLeft: Math.max(1, Math.trunc(turns)), escapeOn };
}
clearIsland() {
this.island = null;
}
/**
* 무인도 상태에서의 주사위 처리.
* - escape: escapeOn(기본 6)이 한 면이라도 나오면 탈출 → 이후 정상 이동
* - wait: 턴 소모 후 대기 (turnsLeft 0이면 자동 해제, 이번 턴은 이동 없음)
* - free: 이미 무인도 아님
*/
resolveIslandRoll(roll: number, faces?: number[]): 'escape' | 'wait' | 'free' {
if (!this.island) return 'free';
const values = faces && faces.length > 0 ? faces : [roll];
if (values.some((value) => value === this.island!.escapeOn)) {
this.island = null;
return 'escape';
}
this.island.turnsLeft -= 1;
if (this.island.turnsLeft <= 0) {
this.island = null;
}
return 'wait';
}
teleport(index: number): number {
this.index = ((index % this.boardLength) + this.boardLength) % this.boardLength;
return this.index;
}
moveOne(): number {
return this.move(1);
}
/** steps칸만큼 현재 방향으로 이동 */
move(steps: number): number {
const amount = Math.trunc(steps);
if (amount === 0) return this.index;
const delta = amount * this.direction;
this.index = (this.index + delta) % this.boardLength;
if (this.index < 0) this.index += this.boardLength;
return this.index;
}
reset(startIndex = 0): number {
this.index = ((startIndex % this.boardLength) + this.boardLength) % this.boardLength;
this.direction = 1;
this.island = null;
return this.index;
}
}