mirror of
https://github.com/Kugelschieber/breach.git
synced 2026-01-18 03:50:24 +00:00
Manage game state through object injection everywhere, fixed filling buffer and stopping clock in Game.
This commit is contained in:
24
src/App.vue
24
src/App.vue
@@ -8,13 +8,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, provide, ref, readonly} from "vue";
|
import {defineComponent, provide, ref} from "vue";
|
||||||
import Level from "./components/Level.vue";
|
import Level from "./components/Level.vue";
|
||||||
import Timer from "./components/Timer.vue";
|
import Timer from "./components/Timer.vue";
|
||||||
import Buffer from "./components/Buffer.vue";
|
import Buffer from "./components/Buffer.vue";
|
||||||
import Matrix from "./components/Matrix.vue";
|
import Matrix from "./components/Matrix.vue";
|
||||||
import { Game } from "./game/Game";
|
import { Game } from "./game/Game";
|
||||||
import {useTimer} from "./components/timer";
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@@ -24,8 +23,7 @@
|
|||||||
Matrix
|
Matrix
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const {updateCountdown} = useTimer();
|
const game = ref(new Game({
|
||||||
const game = new Game({
|
|
||||||
matrix: [
|
matrix: [
|
||||||
"AA", "BB", "CC",
|
"AA", "BB", "CC",
|
||||||
"DD", "AA", "BB",
|
"DD", "AA", "BB",
|
||||||
@@ -36,23 +34,9 @@
|
|||||||
],
|
],
|
||||||
maxBufferLength: 3,
|
maxBufferLength: 3,
|
||||||
timeoutMilliseconds: 60_000,
|
timeoutMilliseconds: 60_000,
|
||||||
});
|
}));
|
||||||
|
provide("game", game);
|
||||||
const level = ref(1);
|
const level = ref(1);
|
||||||
const remainingMilliseconds = ref(game.remainingMilliseconds);
|
|
||||||
const timeoutMilliseconds = ref(game.timeoutMilliseconds);
|
|
||||||
const maxBufferLength = ref(game.maxBufferLength);
|
|
||||||
const buffer = ref(game.buffer);
|
|
||||||
const sequences = ref(game.sequences);
|
|
||||||
const size = ref(game.size);
|
|
||||||
const matrix = ref(game.matrix);
|
|
||||||
provide("remainingMilliseconds", readonly(remainingMilliseconds));
|
|
||||||
provide("timeoutMilliseconds", readonly(timeoutMilliseconds));
|
|
||||||
provide("maxBufferLength", readonly(maxBufferLength));
|
|
||||||
provide("buffer", readonly(buffer));
|
|
||||||
provide("sequences", readonly(sequences));
|
|
||||||
provide("size", readonly(size));
|
|
||||||
provide("matrix", readonly(matrix));
|
|
||||||
updateCountdown(game, remainingMilliseconds);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
level
|
level
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<h2>Buffer</h2>
|
<h2>Buffer</h2>
|
||||||
<div class="buffer-slots">
|
<div class="buffer-slots">
|
||||||
<div class="buffer-slots-slot buffer-slots-border" v-for="(slot, i) in maxBufferLength" :key="i">
|
<div class="buffer-slots-slot buffer-slots-border" v-for="(slot, i) in maxBufferLength" :key="i">
|
||||||
<span v-if="buffer.length > i">{{buffer[i]}}</span>
|
<span v-if="buffer.length > i">{{buffer[i].value}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Sequences />
|
<Sequences />
|
||||||
@@ -11,7 +11,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, inject} from "vue";
|
import { Game } from "@/game/Game";
|
||||||
|
import {defineComponent, inject, Ref, computed} from "vue";
|
||||||
import Sequences from "./Sequences.vue";
|
import Sequences from "./Sequences.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -19,8 +20,9 @@
|
|||||||
Sequences
|
Sequences
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const maxBufferLength = inject("maxBufferLength");
|
const game = inject("game") as Ref<Game>;
|
||||||
const buffer = inject("buffer");
|
const maxBufferLength = computed(() => game.value.maxBufferLength);
|
||||||
|
const buffer = computed(() => game.value.buffer);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
maxBufferLength,
|
maxBufferLength,
|
||||||
|
|||||||
@@ -2,24 +2,35 @@
|
|||||||
<div class="matrix">
|
<div class="matrix">
|
||||||
<h2>Code-Matrix</h2>
|
<h2>Code-Matrix</h2>
|
||||||
<div class="matrix-row" v-for="i in size" :key="i">
|
<div class="matrix-row" v-for="i in size" :key="i">
|
||||||
<div class="matrix-column" v-for="j in size" :key="j">
|
<div class="matrix-column" v-for="j in size" :key="j" v-on:click="select(i, j)">
|
||||||
{{matrix[(i - 1)*size + (j - 1)]}}
|
{{matrix[(j - 1)*size + (i - 1)]}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, inject} from "vue";
|
import { Game } from "@/game/Game";
|
||||||
|
import {defineComponent, inject, computed, Ref} from "vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const size = inject("size");
|
const game = inject("game") as Ref<Game>;
|
||||||
const matrix = inject("matrix");
|
const size = computed(() => game.value.size);
|
||||||
|
const matrix = computed(() => game.value.matrix);
|
||||||
|
|
||||||
|
function select(row: number, column: number) {
|
||||||
|
try {
|
||||||
|
game.value.pick(row-1, column-1);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("nö!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
size,
|
size,
|
||||||
matrix
|
matrix,
|
||||||
|
select
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,11 +10,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, inject} from "vue";
|
import { Game } from "@/game/Game";
|
||||||
|
import {computed, defineComponent, inject, Ref} from "vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const sequences = inject("sequences");
|
const game = inject("game") as Ref<Game>;
|
||||||
|
const sequences = computed(() => game.value.sequences);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sequences
|
sequences
|
||||||
|
|||||||
@@ -6,15 +6,31 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, computed, inject, Ref} from "vue";
|
import { Game } from "@/game/Game";
|
||||||
|
import {defineComponent, computed, inject, Ref, ref} from "vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const remainingMilliseconds = inject("remainingMilliseconds") as Ref<number>;
|
const game = inject("game") as Ref<Game>;
|
||||||
const timeoutMilliseconds = inject("timeoutMilliseconds") as Ref<number>;
|
const remainingMilliseconds = ref(game.value.remainingMilliseconds);
|
||||||
|
const timeoutMilliseconds = computed(() => game.value.timeoutMilliseconds);
|
||||||
const progress = computed(() => remainingMilliseconds.value/timeoutMilliseconds.value*100);
|
const progress = computed(() => remainingMilliseconds.value/timeoutMilliseconds.value*100);
|
||||||
const countdown = computed(() => (remainingMilliseconds.value/1000).toFixed(2));
|
const countdown = computed(() => (remainingMilliseconds.value/1000).toFixed(2));
|
||||||
|
|
||||||
|
const updateTime = () => {
|
||||||
|
remainingMilliseconds.value = game.value.remainingMilliseconds;
|
||||||
|
|
||||||
|
if (remainingMilliseconds.value > 0) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
updateTime();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
updateTime();
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
countdown,
|
countdown,
|
||||||
progress
|
progress
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import { Game } from '@/game/Game';
|
|
||||||
import { Ref } from 'vue';
|
|
||||||
|
|
||||||
interface Timer {
|
|
||||||
updateCountdown(game: Game, remainingMilliseconds: Ref<number>): void
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useTimer(): Timer {
|
|
||||||
function updateCountdown(game: Game, remainingMilliseconds: Ref<number>) {
|
|
||||||
const updateTime = () => {
|
|
||||||
remainingMilliseconds.value = game.remainingMilliseconds;
|
|
||||||
|
|
||||||
if (remainingMilliseconds.value > 0) {
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
updateTime();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
updateTime();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
updateCountdown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -53,6 +53,7 @@ describe("GameState", () => {
|
|||||||
expect(() => game.pick(0, 2)).toThrow();
|
expect(() => game.pick(0, 2)).toThrow();
|
||||||
game.pick(2, 0);
|
game.pick(2, 0);
|
||||||
expect(game.state).toEqual({ selectionMode: SelectionMode.ColumnPick, row: 2 });
|
expect(game.state).toEqual({ selectionMode: SelectionMode.ColumnPick, row: 2 });
|
||||||
|
expect(game.buffer.length).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("cannot pick cell twice", () => {
|
test("cannot pick cell twice", () => {
|
||||||
@@ -64,6 +65,7 @@ describe("GameState", () => {
|
|||||||
});
|
});
|
||||||
game.pick(0, 0);
|
game.pick(0, 0);
|
||||||
expect(() => game.pick(0, 0)).toThrow();
|
expect(() => game.pick(0, 0)).toThrow();
|
||||||
|
expect(game.buffer.length).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("picking outside of range fails", () => {
|
test("picking outside of range fails", () => {
|
||||||
@@ -95,6 +97,7 @@ describe("GameState", () => {
|
|||||||
value: "00"
|
value: "00"
|
||||||
}]);
|
}]);
|
||||||
expect(game.getSequences()).toEqual([{ sequence: simpleSequence, numberOfFulfilled: 1 }])
|
expect(game.getSequences()).toEqual([{ sequence: simpleSequence, numberOfFulfilled: 1 }])
|
||||||
|
expect(game.buffer.length).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("picking fulfills second sequence occurence", () => {
|
test("picking fulfills second sequence occurence", () => {
|
||||||
@@ -120,6 +123,7 @@ describe("GameState", () => {
|
|||||||
expect(game.getSequences()).toEqual([{ sequence: sequence, numberOfFulfilled: 2 }])
|
expect(game.getSequences()).toEqual([{ sequence: sequence, numberOfFulfilled: 2 }])
|
||||||
game.pick(0, 2);
|
game.pick(0, 2);
|
||||||
expect(game.getSequences()).toEqual([{ sequence: sequence, numberOfFulfilled: 3 }])
|
expect(game.getSequences()).toEqual([{ sequence: sequence, numberOfFulfilled: 3 }])
|
||||||
|
expect(game.buffer.length).toEqual(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -156,11 +156,9 @@ export class Game {
|
|||||||
if (this.getSequences().every(isSequenceFulfilled)) {
|
if (this.getSequences().every(isSequenceFulfilled)) {
|
||||||
this.stopClock()
|
this.stopClock()
|
||||||
this.state = EndState.Won
|
this.state = EndState.Won
|
||||||
} else {
|
} else if (this.buffer.length >= this.config.maxBufferLength) {
|
||||||
this.stopClock()
|
this.stopClock()
|
||||||
if (this.buffer.length >= this.config.maxBufferLength) {
|
this.state = EndState.Lost
|
||||||
this.state = EndState.Lost
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,15 +172,10 @@ export class Game {
|
|||||||
Lost: () => {throw new IllegalMoveError()},
|
Lost: () => {throw new IllegalMoveError()},
|
||||||
InProgress: (selectionMode) => {
|
InProgress: (selectionMode) => {
|
||||||
const cell = this.getCell(row, column)
|
const cell = this.getCell(row, column)
|
||||||
if (cell.isUsed) {
|
|
||||||
throw new IllegalMoveError()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.buffer.push({
|
if (cell.isUsed) {
|
||||||
value: cell.value,
|
throw new IllegalMoveError();
|
||||||
positionInMatrixRow: row,
|
}
|
||||||
positionInMatrixColumn: column,
|
|
||||||
})
|
|
||||||
|
|
||||||
matchSelectionState({
|
matchSelectionState({
|
||||||
Free: () => {
|
Free: () => {
|
||||||
@@ -212,6 +205,12 @@ export class Game {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})(selectionMode)
|
})(selectionMode)
|
||||||
|
|
||||||
|
this.buffer.push({
|
||||||
|
value: cell.value,
|
||||||
|
positionInMatrixRow: row,
|
||||||
|
positionInMatrixColumn: column,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})(this.state)
|
})(this.state)
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,11 @@ main {
|
|||||||
height: 32px;
|
height: 32px;
|
||||||
margin: 0 5px 5px 0;
|
margin: 0 5px 5px 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $yellow;
|
||||||
|
color: $background;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user