Started on the game logic

This commit is contained in:
Erik Schilling
2020-12-16 12:23:12 +01:00
parent cae7e305a5
commit a449ab89e0
3 changed files with 174 additions and 1 deletions

View File

@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2020 Marvin Blum Copyright (c) 2020 Marvin Blum, Erik Schilling
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

48
src/game/Game.test.ts Normal file
View File

@@ -0,0 +1,48 @@
import "jest"
import {Game, SelectionMode} from "./Game"
describe("GameState", () => {
const threeByThreeMatrix = [
"00", "10", "20",
"01", "11", "21",
"02", "12", "22",
]
test("getting size works", () => {
expect((new Game([])).size).toEqual(0)
expect((new Game(["00"])).size).toEqual(1)
expect((new Game([
"00", "01",
"10", "11",
])).size).toEqual(2)
});
test("getting cell works", () => {
const game = new Game(threeByThreeMatrix);
expect(game.getCell(0, 2)).toEqual("02")
});
describe("picking", () => {
test("starts with free pick", () => {
const game = new Game([]);
expect(game.state).toEqual({selectionMode: SelectionMode.FreePick})
});
test("picking cells works", () => {
const game = new Game(threeByThreeMatrix);
game.pick(0, 0);
expect(game.state).toEqual({selectionMode: SelectionMode.RowPick, column: 0});
expect(() => game.pick(0, 2)).toThrow();
game.pick(2, 0);
expect(game.state).toEqual({selectionMode: SelectionMode.ColumnPick, row: 2});
});
test("picking outside of range fails", () => {
const game = new Game(threeByThreeMatrix);
expect(() => game.pick(-1, 0)).toThrow();
expect(() => game.pick(0, -1)).toThrow();
expect(() => game.pick(3, 0)).toThrow();
expect(() => game.pick(0, 3)).toThrow();
});
});
});

125
src/game/Game.ts Normal file
View File

@@ -0,0 +1,125 @@
export enum EndState {
Won,
Lost,
}
export enum SelectionMode {
FreePick,
RowPick,
ColumnPick,
}
type FreeSelection = {
selectionMode: SelectionMode.FreePick,
}
type RowSelection = {
selectionMode: SelectionMode.RowPick,
column: number;
}
type ColumnSelection = {
selectionMode: SelectionMode.ColumnPick,
row: number;
}
type SelectionState = FreeSelection | RowSelection | ColumnSelection;
type State = SelectionState | EndState.Won | EndState.Lost;
interface StateSpecificHandler<T> {
Won(): T
Lost(): T
InProgress(selectionState: SelectionState): T
}
function matchState<T>(h: StateSpecificHandler<T>): (a: State) => T {
return (a: State) => {
switch (a) {
case EndState.Won:
return h.Won();
case EndState.Lost:
return h.Lost();
default:
return h.InProgress(a);
}
}
}
interface SelectionStateSpecificHandler<T> {
Free(): T;
Row(row: number): T;
Column(column: number): T;
}
function matchSelectionState<T>(h: SelectionStateSpecificHandler<T>): (a: SelectionState) => T {
return (a: SelectionState) => {
switch (a.selectionMode) {
case SelectionMode.FreePick:
return h.Free();
case SelectionMode.RowPick:
return h.Row(a.column);
case SelectionMode.ColumnPick:
return h.Column(a.row);
}
}
}
class IllegalMoveError extends Error {
constructor() {
super("Illegal move!")
}
}
export class Game {
state: State = {selectionMode: SelectionMode.FreePick}
public readonly size: number
constructor(public readonly matrix: string[]) {
this.size = Math.sqrt(matrix.length)
}
getCell(row: number, column: number) {
return this.matrix[row + column * this.size]
}
pick(row: number, column: number): void {
if (row < 0 || column < 0 || row >= this.size || column >= this.size) {
throw new IllegalMoveError()
}
matchState({
Won: () => {throw new IllegalMoveError()},
Lost: () => {throw new IllegalMoveError()},
InProgress: (selectionMode) =>
matchSelectionState({
Free: () => {
this.state = {
selectionMode: SelectionMode.RowPick,
column: column,
};
},
Column: (r) => {
if (r === row) {
this.state = {
selectionMode: SelectionMode.RowPick,
column: column,
}
} else {
throw new IllegalMoveError()
}
},
Row: (c) => {
if (c === column) {
this.state = {
selectionMode: SelectionMode.ColumnPick,
row: row,
}
} else {
throw new IllegalMoveError()
}
},
})(selectionMode),
})(this.state)
}
}