84 lines
3.3 KiB
TypeScript
84 lines
3.3 KiB
TypeScript
|
|
import { LessonTask } from "./MoveOutcome";
|
||
|
|
|
||
|
|
export type RuleId = "board" | "sowing" | "extraTurn" | "evenCapture" | "emptyPitCapture" | "endOfGame" | "clock";
|
||
|
|
|
||
|
|
// A rule shown on a board: the position, player 1 (bottom) to move, and the pit played, if any.
|
||
|
|
export interface RuleExample {
|
||
|
|
board: number[];
|
||
|
|
pitIndex?: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
// The rule taught on a prepared position (player 1, bottom, to move) with a task.
|
||
|
|
export interface Lesson {
|
||
|
|
board: number[];
|
||
|
|
task: LessonTask;
|
||
|
|
taskKey: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface Rule {
|
||
|
|
id: RuleId;
|
||
|
|
titleKey: string;
|
||
|
|
// At most three sentences.
|
||
|
|
textKeys: string[];
|
||
|
|
example?: RuleExample;
|
||
|
|
lesson?: Lesson;
|
||
|
|
}
|
||
|
|
|
||
|
|
const START = [4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0];
|
||
|
|
const SOWING = [4, 4, 1, 4, 4, 4, 1, 4, 4, 4, 4, 4, 4, 2];
|
||
|
|
const EVEN_CAPTURE = [4, 4, 4, 4, 4, 4, 0, 4, 3, 4, 4, 4, 4, 1];
|
||
|
|
const EMPTY_PIT_CAPTURE = [2, 0, 4, 4, 4, 4, 3, 4, 4, 4, 4, 5, 4, 2];
|
||
|
|
const END_OF_GAME = [0, 0, 0, 0, 0, 1, 20, 2, 3, 1, 0, 2, 1, 18];
|
||
|
|
|
||
|
|
const texts = (id: string, count: number) => Array.from({ length: count }, (_, i) => `Rule${id}Text${i + 1}`);
|
||
|
|
|
||
|
|
// The rules of the game in the order they are taught (docs/specs/tutorial). Every board here is checked against the
|
||
|
|
// engine by the tests; the boards after a move are never written by hand.
|
||
|
|
export const RULES: Rule[] = [
|
||
|
|
{
|
||
|
|
id: "board", titleKey: "RuleBoardTitle", textKeys: texts("Board", 3),
|
||
|
|
example: { board: START },
|
||
|
|
lesson: { board: START, task: "anyMove", taskKey: "LessonTaskAnyMove" },
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: "sowing", titleKey: "RuleSowingTitle", textKeys: texts("Sowing", 3),
|
||
|
|
example: { board: SOWING, pitIndex: 0 },
|
||
|
|
lesson: { board: SOWING, task: "anyMove", taskKey: "LessonTaskSowing" },
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: "extraTurn", titleKey: "RuleExtraTurnTitle", textKeys: texts("ExtraTurn", 2),
|
||
|
|
example: { board: START, pitIndex: 3 },
|
||
|
|
lesson: { board: START, task: "extraTurn", taskKey: "LessonTaskExtraTurn" },
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: "evenCapture", titleKey: "RuleEvenCaptureTitle", textKeys: texts("EvenCapture", 2),
|
||
|
|
example: { board: EVEN_CAPTURE, pitIndex: 5 },
|
||
|
|
lesson: { board: EVEN_CAPTURE, task: "evenCapture", taskKey: "LessonTaskEvenCapture" },
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: "emptyPitCapture", titleKey: "RuleEmptyPitCaptureTitle", textKeys: texts("EmptyPitCapture", 2),
|
||
|
|
example: { board: EMPTY_PIT_CAPTURE, pitIndex: 0 },
|
||
|
|
lesson: { board: EMPTY_PIT_CAPTURE, task: "emptyPitCapture", taskKey: "LessonTaskEmptyPitCapture" },
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: "endOfGame", titleKey: "RuleEndOfGameTitle", textKeys: texts("EndOfGame", 3),
|
||
|
|
example: { board: END_OF_GAME, pitIndex: 5 },
|
||
|
|
lesson: { board: END_OF_GAME, task: "endGame", taskKey: "LessonTaskEndGame" },
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: "clock", titleKey: "RuleClockTitle", textKeys: texts("Clock", 2),
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
export const LESSON_RULES: Rule[] = RULES.filter((rule) => rule.lesson);
|
||
|
|
|
||
|
|
export function getRule(id: string | undefined): Rule | undefined {
|
||
|
|
return RULES.find((rule) => rule.id === id);
|
||
|
|
}
|
||
|
|
|
||
|
|
// The lesson after the given one, or undefined after the last.
|
||
|
|
export function getNextLessonRule(id: RuleId): Rule | undefined {
|
||
|
|
const index = LESSON_RULES.findIndex((rule) => rule.id === id);
|
||
|
|
return index >= 0 ? LESSON_RULES[index + 1] : undefined;
|
||
|
|
}
|