Don't use component library for game boards...
This commit is contained in:
parent
9fd2b4d507
commit
677221603c
|
@ -8,6 +8,7 @@
|
||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@game-algorithms/connect-four-rust": "^0.0.1",
|
||||||
"@game-algorithms/o-x-rust": "^0.1.0"
|
"@game-algorithms/o-x-rust": "^0.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -80,6 +81,12 @@
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@game-algorithms/connect-four-rust": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://git.koval.net/api/packages/cyclane/npm/%40game-algorithms%2Fconnect-four-rust/-/0.0.1/connect-four-rust-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-XFVCupzmxi6lL9jfrETenwQwo7JDvGby8PB2jOfRuRpmGBZwgx9GQvc9euw+LwlGCXUhN8zYqKIdoU7IDfd6ng==",
|
||||||
|
"license": "GNU GPLv3"
|
||||||
|
},
|
||||||
"node_modules/@game-algorithms/o-x-rust": {
|
"node_modules/@game-algorithms/o-x-rust": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://git.koval.net/api/packages/cyclane/npm/%40game-algorithms%2Fo-x-rust/-/0.1.0/o-x-rust-0.1.0.tgz",
|
"resolved": "https://git.koval.net/api/packages/cyclane/npm/%40game-algorithms%2Fo-x-rust/-/0.1.0/o-x-rust-0.1.0.tgz",
|
||||||
|
@ -2927,6 +2934,11 @@
|
||||||
"strip-json-comments": "^3.1.1"
|
"strip-json-comments": "^3.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@game-algorithms/connect-four-rust": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://git.koval.net/api/packages/cyclane/npm/%40game-algorithms%2Fconnect-four-rust/-/0.0.1/connect-four-rust-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-XFVCupzmxi6lL9jfrETenwQwo7JDvGby8PB2jOfRuRpmGBZwgx9GQvc9euw+LwlGCXUhN8zYqKIdoU7IDfd6ng=="
|
||||||
|
},
|
||||||
"@game-algorithms/o-x-rust": {
|
"@game-algorithms/o-x-rust": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://git.koval.net/api/packages/cyclane/npm/%40game-algorithms%2Fo-x-rust/-/0.1.0/o-x-rust-0.1.0.tgz",
|
"resolved": "https://git.koval.net/api/packages/cyclane/npm/%40game-algorithms%2Fo-x-rust/-/0.1.0/o-x-rust-0.1.0.tgz",
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@game-algorithms/o-x-rust": "^0.1.0"
|
"@game-algorithms/o-x-rust": "^0.1.0",
|
||||||
|
"@game-algorithms/connect-four-rust": "^0.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { type SvelteComponent, createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
|
export let board: number[];
|
||||||
|
export let iconMap: Record<number, typeof SvelteComponent>;
|
||||||
|
export let disabled = false;
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate handler function for board cell click
|
||||||
|
*
|
||||||
|
* @param idx Index of cell for event
|
||||||
|
* @returns Handler function
|
||||||
|
*/
|
||||||
|
function handleClick(idx: number) {
|
||||||
|
return () => dispatch("click", {
|
||||||
|
idx
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
{#each board as b, idx}
|
||||||
|
<button disabled={b !== 0 || disabled} class="item" on:click={handleClick(idx)}>
|
||||||
|
<svelte:component this="{iconMap[b]}"/>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
max-width: min(100%, 24rem);
|
||||||
|
border: 1px solid var(--cds-interactive-03);
|
||||||
|
}
|
||||||
|
button.item {
|
||||||
|
cursor: pointer;
|
||||||
|
aspect-ratio: 1/1;
|
||||||
|
background: none;
|
||||||
|
border: 2px solid var(--cds-interactive-03);
|
||||||
|
color: var(--cds-interactive-03);
|
||||||
|
transition: background 70ms cubic-bezier(0, 0, 0.38, 0.9),
|
||||||
|
color 70ms cubic-bezier(0, 0, 0.38, 0.9);
|
||||||
|
}
|
||||||
|
button.item:enabled:hover {
|
||||||
|
background-color: var(--cds-hover-tertiary);
|
||||||
|
color: var(--cds-inverse-01);
|
||||||
|
}
|
||||||
|
button.item:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
width: 5rem;
|
width: 100%;
|
||||||
height: 5rem;
|
height: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
h1 {
|
h1 {
|
||||||
width: 5rem;
|
width: 100%;
|
||||||
height: 5rem;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
}
|
}
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
h1 {
|
h1 {
|
||||||
width: 5rem;
|
width: 100%;
|
||||||
height: 5rem;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
}
|
}
|
|
@ -2,6 +2,10 @@
|
||||||
import { Column, Grid, Link, Row } from "carbon-components-svelte";
|
import { Column, Grid, Link, Row } from "carbon-components-svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>About</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Row>
|
<Row>
|
||||||
<Column>
|
<Column>
|
||||||
|
|
|
@ -12,14 +12,15 @@
|
||||||
UnorderedList
|
UnorderedList
|
||||||
} from "carbon-components-svelte";
|
} from "carbon-components-svelte";
|
||||||
import wasmInit, { count_empty, find_winner, get_score, predict } from "@game-algorithms/o-x-rust";
|
import wasmInit, { count_empty, find_winner, get_score, predict } from "@game-algorithms/o-x-rust";
|
||||||
import OXNoneIcon from "$lib/components/OXNoneIcon.svelte";
|
import OXBoard from "$lib/components/o-x/OXBoard.svelte";
|
||||||
import OXOIcon from "$lib/components/OXOIcon.svelte";
|
import OXNoneIcon from "$lib/components/o-x/OXNoneIcon.svelte";
|
||||||
import OXXIcon from "$lib/components/OXXIcon.svelte";
|
import OXOIcon from "$lib/components/o-x/OXOIcon.svelte";
|
||||||
|
import OXXIcon from "$lib/components/o-x/OXXIcon.svelte";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
let board: number[][] = [];
|
let board: number[] = [];
|
||||||
for (let i = 0;i < 3;i++) {
|
for (let i = 0;i < 9;i++) {
|
||||||
board.push([0, 0, 0]);
|
board.push(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let turn = -1;
|
let turn = -1;
|
||||||
|
@ -44,7 +45,7 @@
|
||||||
* Complete a turn logic
|
* Complete a turn logic
|
||||||
*/
|
*/
|
||||||
function completeTurn() {
|
function completeTurn() {
|
||||||
const convertedBoard = Uint8Array.from(board.flat());
|
const convertedBoard = Uint8Array.from(board);
|
||||||
scores = [...scores, getBoardScore(board, human, bot, algorithm)];
|
scores = [...scores, getBoardScore(board, human, bot, algorithm)];
|
||||||
const winner = find_winner(convertedBoard);
|
const winner = find_winner(convertedBoard);
|
||||||
if (winner !== 0 || count_empty(convertedBoard) === 0) {
|
if (winner !== 0 || count_empty(convertedBoard) === 0) {
|
||||||
|
@ -73,7 +74,7 @@
|
||||||
} else {
|
} else {
|
||||||
status = "Algorithm's turn!";
|
status = "Algorithm's turn!";
|
||||||
const p = predict(bot, human, bot === 1, convertedBoard, algorithm);
|
const p = predict(bot, human, bot === 1, convertedBoard, algorithm);
|
||||||
board[Math.floor(p / 3)][p % 3] = bot;
|
board[p] = bot;
|
||||||
completeTurn();
|
completeTurn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,17 +89,17 @@
|
||||||
* @returns The scores for the moves for each cell and player
|
* @returns The scores for the moves for each cell and player
|
||||||
*/
|
*/
|
||||||
function getBoardScore(
|
function getBoardScore(
|
||||||
board: number[][],
|
board: number[],
|
||||||
human: number,
|
human: number,
|
||||||
bot: number,
|
bot: number,
|
||||||
algorithm: string
|
algorithm: string
|
||||||
): [number | null, number | null][] {
|
): [number | null, number | null][] {
|
||||||
return board.flat().map((v, i) => {
|
return board.flat().map((v, i) => {
|
||||||
if (v === 0) {
|
if (v === 0) {
|
||||||
const humanCopy = board.flat();
|
const humanCopy = board.slice();
|
||||||
humanCopy[i] = human;
|
humanCopy[i] = human;
|
||||||
const hUint8Array = Uint8Array.from(humanCopy);
|
const hUint8Array = Uint8Array.from(humanCopy);
|
||||||
const botCopy = board.flat();
|
const botCopy = board.slice();
|
||||||
botCopy[i] = bot;
|
botCopy[i] = bot;
|
||||||
const bUint8Array = Uint8Array.from(humanCopy);
|
const bUint8Array = Uint8Array.from(humanCopy);
|
||||||
return [
|
return [
|
||||||
|
@ -151,6 +152,10 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Noughts & Crosses</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Row>
|
<Row>
|
||||||
<Column>
|
<Column>
|
||||||
|
@ -166,7 +171,7 @@
|
||||||
<ListItem>mm+d: minimax with depth</ListItem>
|
<ListItem>mm+d: minimax with depth</ListItem>
|
||||||
</UnorderedList>
|
</UnorderedList>
|
||||||
<div class="selection">
|
<div class="selection">
|
||||||
<RadioButtonGroup disabled={turn !== -1} legendText="Algorithms" bind:selected={algorithm}>
|
<RadioButtonGroup disabled={turn !== -1} legendText="Algorithm" bind:selected={algorithm}>
|
||||||
<RadioButton labelText="sa" value="sa" />
|
<RadioButton labelText="sa" value="sa" />
|
||||||
<RadioButton labelText="sa+rd" value="sa+rd" />
|
<RadioButton labelText="sa+rd" value="sa+rd" />
|
||||||
<RadioButton labelText="sa+r-d" value="sa+r-d" />
|
<RadioButton labelText="sa+r-d" value="sa+r-d" />
|
||||||
|
@ -183,8 +188,8 @@
|
||||||
<Button disabled={!loaded || turn !== -1} on:click={() => {
|
<Button disabled={!loaded || turn !== -1} on:click={() => {
|
||||||
board = [];
|
board = [];
|
||||||
scores = [];
|
scores = [];
|
||||||
for (let i = 0;i < 3;i++) {
|
for (let i = 0;i < 9;i++) {
|
||||||
board.push([0, 0, 0]);
|
board.push(0);
|
||||||
}
|
}
|
||||||
turn = 2;
|
turn = 2;
|
||||||
human = Number(playAs === "O") + 1;
|
human = Number(playAs === "O") + 1;
|
||||||
|
@ -199,27 +204,19 @@
|
||||||
<h2>Board</h2>
|
<h2>Board</h2>
|
||||||
<p>{status}</p>
|
<p>{status}</p>
|
||||||
{#if loaded}
|
{#if loaded}
|
||||||
<Grid>
|
<OXBoard
|
||||||
{#each board as row, r}
|
disabled={turn !== human}
|
||||||
<Row>
|
board={board}
|
||||||
{#each row as col, c}
|
iconMap={{
|
||||||
<Column sm={.1} style="border: 1px solid white;">
|
0: OXNoneIcon,
|
||||||
<Button
|
1: OXXIcon,
|
||||||
kind="tertiary"
|
2: OXOIcon
|
||||||
iconDescription={col === 0 ? "Available" : col === 1 ? "Cross" : "Nought"}
|
}}
|
||||||
icon={col === 0 ? OXNoneIcon : col === 1 ? OXXIcon : OXOIcon}
|
on:click={event => {
|
||||||
disabled={board[r][c] !== 0 || turn !== human}
|
board[event.detail.idx] = human;
|
||||||
on:click={() => {
|
|
||||||
board[r][c] = human;
|
|
||||||
completeTurn();
|
completeTurn();
|
||||||
console.log(scores);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Column>
|
|
||||||
{/each}
|
|
||||||
</Row>
|
|
||||||
{/each}
|
|
||||||
</Grid>
|
|
||||||
{:else}
|
{:else}
|
||||||
<h3>Loading...</h3>
|
<h3>Loading...</h3>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -248,7 +245,7 @@
|
||||||
key: "move",
|
key: "move",
|
||||||
value: "Move"
|
value: "Move"
|
||||||
},
|
},
|
||||||
...board.flat().map((_, c) => {
|
...board.map((_, c) => {
|
||||||
return {
|
return {
|
||||||
key: c.toString(),
|
key: c.toString(),
|
||||||
value: (c + 1).toString()
|
value: (c + 1).toString()
|
||||||
|
|
Loading…
Reference in New Issue