Merge pull request #23 from Kugelschieber/ui-gamestate

Game State visualization
This commit is contained in:
Marvin Blum
2020-12-21 21:34:19 +01:00
committed by GitHub
14 changed files with 192 additions and 142 deletions

70
package-lock.json generated
View File

@@ -2178,6 +2178,16 @@
} }
} }
}, },
"json5": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
"integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
"dev": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
},
"jsonfile": { "jsonfile": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
@@ -2187,6 +2197,18 @@
"graceful-fs": "^4.1.6" "graceful-fs": "^4.1.6"
} }
}, },
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"locate-path": { "locate-path": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -2342,6 +2364,18 @@
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true "dev": true
}, },
"vue-loader-v16": {
"version": "npm:vue-loader@16.1.2",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.1.2.tgz",
"integrity": "sha512-8QTxh+Fd+HB6fiL52iEVLKqE9N1JSlMXLR92Ijm6g8PZrwIxckgpqjPDWRP5TWxdiPaHR+alUWsnu1ShQOwt+Q==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
},
"yallist": { "yallist": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@@ -15067,42 +15101,6 @@
} }
} }
}, },
"vue-loader-v16": {
"version": "npm:vue-loader@16.1.2",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.1.2.tgz",
"integrity": "sha512-8QTxh+Fd+HB6fiL52iEVLKqE9N1JSlMXLR92Ijm6g8PZrwIxckgpqjPDWRP5TWxdiPaHR+alUWsnu1ShQOwt+Q==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"json5": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
"integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
"dev": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
}
}
},
"vue-style-loader": { "vue-style-loader": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz", "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz",

View File

@@ -1,18 +1,19 @@
<template> <template>
<main> <main>
<Level :level="level" /> <Level :level="level" />
<Timer :time="time" /> <Timer />
<Buffer :slots="slots" :codes="codes" /> <Buffer />
<Matrix :size="size" :matrix="matrix" /> <Matrix />
</main> </main>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent} 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";
export default defineComponent({ export default defineComponent({
components: { components: {
@@ -22,31 +23,23 @@
Matrix Matrix
}, },
setup() { setup() {
const time = new Date(); const game = ref(new Game({
time.setSeconds(time.getSeconds()+60); matrix: [
"AA", "BB", "CC",
"DD", "AA", "BB",
"CC", "DD", "AA",
],
sequences: [
["AA", "CC", "DD"],
],
maxBufferLength: 3,
timeoutMilliseconds: 60_000,
}));
provide("game", game);
const level = ref(1);
return { return {
level: 5, level
time,
slots: ["99", "E7", "AD", ""],
codes: [
{
code: ["", "E7", "AD", "BD"],
points: 100
},
{
code: ["99", "E7", "AD"],
points: 50
}
],
size: 5,
matrix: [
"99", "E7", "AD", "99", "BD",
"99", "BD", "99", "E7", "AD",
"AD", "E7", "BD", "AD", "99",
"99", "99", "BD", "E7", "AD",
"E7", "AD", "99", "BD", "99"
]
} }
} }
}); });

View File

@@ -2,31 +2,32 @@
<div class="buffer"> <div class="buffer">
<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 slots" :key="i"> <div class="buffer-slots-slot buffer-slots-border" v-for="(slot, i) in maxBufferLength" :key="i">
{{slot}} <span v-if="buffer.length > i">{{buffer[i].value}}</span>
</div>
</div>
<h2>Sequences</h2>
<div class="buffer-codes" v-for="(code, i) in codes" :key="i">
<div class="buffer-slots">
<div class="buffer-slots-slot" v-for="(digit, j) in code.code" :key="j">
{{digit}}
</div>
</div>
<div class="buffer-points">
{{code.points}} Pts
</div> </div>
</div> </div>
<Sequences />
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent} from "vue"; import { Game } from "@/game/Game";
import {defineComponent, inject, Ref, computed} from "vue";
import Sequences from "./Sequences.vue";
export default defineComponent({ export default defineComponent({
props: { components: {
slots: {type: Array, required: true}, Sequences
codes: {type: Array, required: true} },
setup() {
const game = inject("game") as Ref<Game>;
const maxBufferLength = computed(() => game.value.maxBufferLength);
const buffer = computed(() => game.value.buffer);
return {
maxBufferLength,
buffer
}
} }
}); });
</script> </script>

View File

@@ -2,20 +2,36 @@
<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)*5 + (j - 1)]}} {{matrix[(j - 1)*size + (i - 1)]}}
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent} from "vue"; import { Game } from "@/game/Game";
import {defineComponent, inject, computed, Ref} from "vue";
export default defineComponent({ export default defineComponent({
props: { setup() {
size: {type: Number, required: true}, const game = inject("game") as Ref<Game>;
matrix: {type: Array, required: true}, 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 {
size,
matrix,
select
}
} }
}); });
</script> </script>

View File

@@ -0,0 +1,26 @@
<template>
<h2>Sequences</h2>
<div class="buffer-codes" v-for="(sequence, i) in sequences" :key="i">
<div class="buffer-slots">
<div class="buffer-slots-slot" v-for="(digit, j) in sequence" :key="j">
{{digit}}
</div>
</div>
</div>
</template>
<script lang="ts">
import { Game } from "@/game/Game";
import {computed, defineComponent, inject, Ref} from "vue";
export default defineComponent({
setup() {
const game = inject("game") as Ref<Game>;
const sequences = computed(() => game.value.sequences);
return {
sequences
}
}
});
</script>

View File

@@ -6,20 +6,21 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent, ref, computed} from "vue"; import { Game } from "@/game/Game";
import {defineComponent, computed, inject, Ref, ref} from "vue";
export default defineComponent({ export default defineComponent({
props: { setup() {
time: {type: Object, required: true} const game = inject("game") as Ref<Game>;
}, const remainingMilliseconds = ref(game.value.remainingMilliseconds);
setup(props) { const timeoutMilliseconds = computed(() => game.value.timeoutMilliseconds);
const remainingTime = ref(60) const progress = computed(() => remainingMilliseconds.value/timeoutMilliseconds.value*100);
const progress = computed(() => remainingTime.value/60*100); const countdown = computed(() => (remainingMilliseconds.value/1000).toFixed(2));
const countdown = computed(() => remainingTime.value.toFixed(2));
const updateTime = () => { const updateTime = () => {
remainingTime.value = Math.max(0, (props.time.getTime() - new Date().getTime()) / 1000); remainingMilliseconds.value = game.value.remainingMilliseconds;
if (remainingTime.value > 0) {
if (remainingMilliseconds.value > 0) {
requestAnimationFrame(() => { requestAnimationFrame(() => {
updateTime(); updateTime();
}); });

View File

@@ -12,7 +12,7 @@ describe("GameState", () => {
const unlimitedTime = 999 * 1000 const unlimitedTime = 999 * 1000
test("getting size works", () => { test("getting size works", () => {
expect((new Game({ matrix: ["00"], sequences: [["AA"]], maxBufferLength: unrestrictedBuffer, timeout: unlimitedTime })).size).toEqual(1) expect((new Game({ matrix: ["00"], sequences: [["AA"]], maxBufferLength: unrestrictedBuffer, timeoutMilliseconds: unlimitedTime })).size).toEqual(1)
expect((new Game({ expect((new Game({
matrix: [ matrix: [
"00", "01", "00", "01",
@@ -20,7 +20,7 @@ describe("GameState", () => {
], ],
sequences: [], sequences: [],
maxBufferLength: unrestrictedBuffer, maxBufferLength: unrestrictedBuffer,
timeout: unlimitedTime, timeoutMilliseconds: unlimitedTime,
})).size).toEqual(2) })).size).toEqual(2)
}); });
@@ -29,14 +29,14 @@ describe("GameState", () => {
matrix: threeByThreeMatrix, matrix: threeByThreeMatrix,
sequences: [["AA"]], sequences: [["AA"]],
maxBufferLength: unrestrictedBuffer, maxBufferLength: unrestrictedBuffer,
timeout: unlimitedTime, timeoutMilliseconds: unlimitedTime,
}); });
expect(game.getCell(0, 2)).toEqual({ value: "02", isUsed: false }) expect(game.getCell(0, 2)).toEqual({ value: "02", isUsed: false })
}); });
describe("picking", () => { describe("picking", () => {
test("starts with free pick", () => { test("starts with free pick", () => {
const game = new Game({ matrix: [], sequences: [["AA"]], maxBufferLength: 1, timeout: unlimitedTime }); const game = new Game({ matrix: [], sequences: [["AA"]], maxBufferLength: 1, timeoutMilliseconds: unlimitedTime });
expect(game.state).toEqual({ selectionMode: SelectionMode.FreePick }) expect(game.state).toEqual({ selectionMode: SelectionMode.FreePick })
}); });
@@ -45,7 +45,7 @@ describe("GameState", () => {
matrix: threeByThreeMatrix, matrix: threeByThreeMatrix,
sequences: [["AA"]], sequences: [["AA"]],
maxBufferLength: unrestrictedBuffer, maxBufferLength: unrestrictedBuffer,
timeout: unlimitedTime, timeoutMilliseconds: unlimitedTime,
}); });
game.pick(0, 0); game.pick(0, 0);
expect(game.state).toEqual({ selectionMode: SelectionMode.RowPick, column: 0 }); expect(game.state).toEqual({ selectionMode: SelectionMode.RowPick, column: 0 });
@@ -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", () => {
@@ -60,10 +61,11 @@ describe("GameState", () => {
matrix: threeByThreeMatrix, matrix: threeByThreeMatrix,
sequences: [["AA"]], sequences: [["AA"]],
maxBufferLength: unrestrictedBuffer, maxBufferLength: unrestrictedBuffer,
timeout: unlimitedTime, timeoutMilliseconds: unlimitedTime,
}); });
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", () => {
@@ -71,7 +73,7 @@ describe("GameState", () => {
matrix: threeByThreeMatrix, matrix: threeByThreeMatrix,
sequences: [], sequences: [],
maxBufferLength: unrestrictedBuffer, maxBufferLength: unrestrictedBuffer,
timeout: unlimitedTime, timeoutMilliseconds: unlimitedTime,
}); });
expect(() => game.pick(-1, 0)).toThrow(); expect(() => game.pick(-1, 0)).toThrow();
expect(() => game.pick(0, -1)).toThrow(); expect(() => game.pick(0, -1)).toThrow();
@@ -85,7 +87,7 @@ describe("GameState", () => {
matrix: threeByThreeMatrix, matrix: threeByThreeMatrix,
sequences: [simpleSequence], sequences: [simpleSequence],
maxBufferLength: unrestrictedBuffer, maxBufferLength: unrestrictedBuffer,
timeout: unlimitedTime, timeoutMilliseconds: unlimitedTime,
}); });
expect(game.getSequences()).toEqual([{ sequence: simpleSequence, numberOfFulfilled: 0 }]) expect(game.getSequences()).toEqual([{ sequence: simpleSequence, numberOfFulfilled: 0 }])
game.pick(0, 0); game.pick(0, 0);
@@ -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", () => {
@@ -107,7 +110,7 @@ describe("GameState", () => {
], ],
sequences: [sequence], sequences: [sequence],
maxBufferLength: unrestrictedBuffer, maxBufferLength: unrestrictedBuffer,
timeout: unlimitedTime, timeoutMilliseconds: unlimitedTime,
}); });
expect(game.getSequences()).toEqual([{ sequence: sequence, numberOfFulfilled: 0 }]) expect(game.getSequences()).toEqual([{ sequence: sequence, numberOfFulfilled: 0 }])
game.pick(0, 0); game.pick(0, 0);
@@ -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);
}); });
}); });
@@ -133,7 +137,7 @@ describe("GameState", () => {
], ],
sequences: [["AA", "BB", "CC"]], sequences: [["AA", "BB", "CC"]],
maxBufferLength: 3, maxBufferLength: 3,
timeout: unlimitedTime, timeoutMilliseconds: unlimitedTime,
}) })
game.pick(0, 0) game.pick(0, 0)
game.pick(2, 0) game.pick(2, 0)
@@ -151,7 +155,7 @@ describe("GameState", () => {
], ],
sequences: [["AA", "BB", "CC"]], sequences: [["AA", "BB", "CC"]],
maxBufferLength: 3, maxBufferLength: 3,
timeout: unlimitedTime, timeoutMilliseconds: unlimitedTime,
}) })
game.pick(0, 0) game.pick(0, 0)
game.pick(1, 0) game.pick(1, 0)
@@ -186,7 +190,7 @@ describe("GameState", () => {
], ],
sequences: [["AA"]], sequences: [["AA"]],
maxBufferLength: 3, maxBufferLength: 3,
timeout: 10_000, timeoutMilliseconds: 10_000,
}) })
fakeTimeProgress(1_000) fakeTimeProgress(1_000)
expect(game.remainingMilliseconds).toEqual(9_000) expect(game.remainingMilliseconds).toEqual(9_000)
@@ -208,7 +212,7 @@ describe("GameState", () => {
], ],
sequences: [["AA"]], sequences: [["AA"]],
maxBufferLength: unrestrictedBuffer, maxBufferLength: unrestrictedBuffer,
timeout: 10_000, timeoutMilliseconds: 10_000,
} }
) )
fakeTimeProgress(1_000) fakeTimeProgress(1_000)

View File

@@ -102,16 +102,15 @@ export class Game {
this.timeoutInterval = setTimeout(() => { this.timeoutInterval = setTimeout(() => {
this.state = EndState.Lost this.state = EndState.Lost
this.stopClock() this.stopClock()
}, }, this.config.timeoutMilliseconds)
this.config.timeout)
this.startTimeTimeStamp = Date.now() this.startTimeTimeStamp = Date.now()
} }
get remainingMilliseconds(): number { get remainingMilliseconds(): number {
if (this.endTimestamp) { if (this.endTimestamp) {
return this.config.timeout - (this.endTimestamp - this.startTimeTimeStamp) return this.config.timeoutMilliseconds - (this.endTimestamp - this.startTimeTimeStamp)
} }
return this.config.timeout - (Date.now() - this.startTimeTimeStamp) return this.config.timeoutMilliseconds - (Date.now() - this.startTimeTimeStamp)
} }
getCell(row: number, column: number): Cell { getCell(row: number, column: number): Cell {
@@ -157,13 +156,11 @@ 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
} }
} }
}
pick(row: number, column: number): void { pick(row: number, column: number): void {
if (row < 0 || column < 0 || row >= this.size || column >= this.size) { if (row < 0 || column < 0 || row >= this.size || column >= this.size) {
@@ -175,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: () => {
@@ -213,9 +205,31 @@ export class Game {
} }
}, },
})(selectionMode) })(selectionMode)
this.buffer.push({
value: cell.value,
positionInMatrixRow: row,
positionInMatrixColumn: column,
})
} }
})(this.state) })(this.state)
this.checkEndGame(); this.checkEndGame();
} }
get maxBufferLength(): number {
return this.config.maxBufferLength
}
get sequences(): string[][] {
return this.config.sequences
}
get matrix(): string[] {
return this.config.matrix
}
get timeoutMilliseconds(): number {
return this.config.timeoutMilliseconds
}
} }

View File

@@ -2,5 +2,5 @@ export default interface GameConfiguration {
matrix: string[] matrix: string[]
sequences: string[][] sequences: string[][]
maxBufferLength: number maxBufferLength: number
timeout: number timeoutMilliseconds: number
} }

View File

@@ -19,6 +19,6 @@ describe("MatrixGen", () => {
expect(gameConfig.maxBufferLength).not.toBeNull; expect(gameConfig.maxBufferLength).not.toBeNull;
expect(gameConfig.sequences).not.toBeNull; expect(gameConfig.sequences).not.toBeNull;
console.log(gameConfig.sequences); console.log(gameConfig.sequences);
expect(gameConfig.timeout).not.toBeNull; expect(gameConfig.timeoutMilliseconds).not.toBeNull;
}); });
}); });

View File

@@ -54,7 +54,7 @@ export function generateGameConfig(level: number, seed: string): GameConfigurati
matrix: matrix, matrix: matrix,
sequences: sequences, sequences: sequences,
maxBufferLength: maxBufferLength, maxBufferLength: maxBufferLength,
timeout: defaultTimeout timeoutMilliseconds: defaultTimeout
}; };
return result; return result;

View File

@@ -4,7 +4,6 @@ import { saveGameKey, saveGame, loadGame } from './SaveGame';
describe("SaveGame", () => { describe("SaveGame", () => {
const state = { const state = {
level: 42, level: 42,
score: 89,
}; };
beforeEach(() => { beforeEach(() => {

View File

@@ -2,7 +2,6 @@ export const saveGameKey = "save_game";
export interface SaveGame { export interface SaveGame {
level: number level: number
score: number
} }
export function saveGame(saveGame: SaveGame): void { export function saveGame(saveGame: SaveGame): void {

View File

@@ -5,6 +5,7 @@ $yellow: #d0ee58;
* { * {
box-sizing: border-box; box-sizing: border-box;
user-select: none;
} }
html, body { html, body {
@@ -84,14 +85,6 @@ main {
margin: 0 5px 0 0; margin: 0 5px 0 0;
} }
&-points {
display: flex;
justify-content: center;
align-items: center;
width: 64px;
height: 32px;
}
&-border { &-border {
border: 1px solid $yellow; border: 1px solid $yellow;
} }
@@ -113,6 +106,12 @@ main {
width: 32px; width: 32px;
height: 32px; height: 32px;
margin: 0 5px 5px 0; margin: 0 5px 5px 0;
cursor: pointer;
&:hover {
background: $yellow;
color: $background;
}
} }
} }