Don't use component library for game boards...
ci/woodpecker/push/tools-wasm-pack-plugin Pipeline was successful Details
ci/woodpecker/push/wasm-connect-four-rust Pipeline was successful Details
ci/woodpecker/push/wasm-o-x-rust Pipeline was successful Details
ci/woodpecker/push/frontend Pipeline was successful Details

This commit is contained in:
Gleb Koval 2022-08-09 21:10:51 +00:00
parent 9fd2b4d507
commit 677221603c
Signed by: cyclane
GPG Key ID: 15E168A8B332382C
8 changed files with 109 additions and 43 deletions

View File

@ -8,6 +8,7 @@
"name": "frontend",
"version": "0.0.1",
"dependencies": {
"@game-algorithms/connect-four-rust": "^0.0.1",
"@game-algorithms/o-x-rust": "^0.1.0"
},
"devDependencies": {
@ -80,6 +81,12 @@
"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": {
"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",
@ -2927,6 +2934,11 @@
"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": {
"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",

View File

@ -32,6 +32,7 @@
},
"type": "module",
"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"
}
}

View File

@ -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>

View File

@ -2,7 +2,7 @@
<style>
div {
width: 5rem;
height: 5rem;
width: 100%;
height: 100%;
}
</style>

View File

@ -2,8 +2,7 @@
<style>
h1 {
width: 5rem;
height: 5rem;
width: 100%;
text-align: center;
font-size: 4rem;
}

View File

@ -2,8 +2,7 @@
<style>
h1 {
width: 5rem;
height: 5rem;
width: 100%;
text-align: center;
font-size: 4rem;
}

View File

@ -2,6 +2,10 @@
import { Column, Grid, Link, Row } from "carbon-components-svelte";
</script>
<svelte:head>
<title>About</title>
</svelte:head>
<Grid>
<Row>
<Column>

View File

@ -12,14 +12,15 @@
UnorderedList
} from "carbon-components-svelte";
import wasmInit, { count_empty, find_winner, get_score, predict } from "@game-algorithms/o-x-rust";
import OXNoneIcon from "$lib/components/OXNoneIcon.svelte";
import OXOIcon from "$lib/components/OXOIcon.svelte";
import OXXIcon from "$lib/components/OXXIcon.svelte";
import OXBoard from "$lib/components/o-x/OXBoard.svelte";
import OXNoneIcon from "$lib/components/o-x/OXNoneIcon.svelte";
import OXOIcon from "$lib/components/o-x/OXOIcon.svelte";
import OXXIcon from "$lib/components/o-x/OXXIcon.svelte";
import { onMount } from "svelte";
let board: number[][] = [];
for (let i = 0;i < 3;i++) {
board.push([0, 0, 0]);
let board: number[] = [];
for (let i = 0;i < 9;i++) {
board.push(0);
}
let turn = -1;
@ -44,7 +45,7 @@
* Complete a turn logic
*/
function completeTurn() {
const convertedBoard = Uint8Array.from(board.flat());
const convertedBoard = Uint8Array.from(board);
scores = [...scores, getBoardScore(board, human, bot, algorithm)];
const winner = find_winner(convertedBoard);
if (winner !== 0 || count_empty(convertedBoard) === 0) {
@ -73,7 +74,7 @@
} else {
status = "Algorithm's turn!";
const p = predict(bot, human, bot === 1, convertedBoard, algorithm);
board[Math.floor(p / 3)][p % 3] = bot;
board[p] = bot;
completeTurn();
}
}
@ -88,17 +89,17 @@
* @returns The scores for the moves for each cell and player
*/
function getBoardScore(
board: number[][],
board: number[],
human: number,
bot: number,
algorithm: string
): [number | null, number | null][] {
return board.flat().map((v, i) => {
if (v === 0) {
const humanCopy = board.flat();
const humanCopy = board.slice();
humanCopy[i] = human;
const hUint8Array = Uint8Array.from(humanCopy);
const botCopy = board.flat();
const botCopy = board.slice();
botCopy[i] = bot;
const bUint8Array = Uint8Array.from(humanCopy);
return [
@ -151,6 +152,10 @@
}
</script>
<svelte:head>
<title>Noughts & Crosses</title>
</svelte:head>
<Grid>
<Row>
<Column>
@ -166,7 +171,7 @@
<ListItem>mm+d: minimax with depth</ListItem>
</UnorderedList>
<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+rd" value="sa+rd" />
<RadioButton labelText="sa+r-d" value="sa+r-d" />
@ -183,8 +188,8 @@
<Button disabled={!loaded || turn !== -1} on:click={() => {
board = [];
scores = [];
for (let i = 0;i < 3;i++) {
board.push([0, 0, 0]);
for (let i = 0;i < 9;i++) {
board.push(0);
}
turn = 2;
human = Number(playAs === "O") + 1;
@ -199,27 +204,19 @@
<h2>Board</h2>
<p>{status}</p>
{#if loaded}
<Grid>
{#each board as row, r}
<Row>
{#each row as col, c}
<Column sm={.1} style="border: 1px solid white;">
<Button
kind="tertiary"
iconDescription={col === 0 ? "Available" : col === 1 ? "Cross" : "Nought"}
icon={col === 0 ? OXNoneIcon : col === 1 ? OXXIcon : OXOIcon}
disabled={board[r][c] !== 0 || turn !== human}
on:click={() => {
board[r][c] = human;
completeTurn();
console.log(scores);
}}
/>
</Column>
{/each}
</Row>
{/each}
</Grid>
<OXBoard
disabled={turn !== human}
board={board}
iconMap={{
0: OXNoneIcon,
1: OXXIcon,
2: OXOIcon
}}
on:click={event => {
board[event.detail.idx] = human;
completeTurn();
}}
/>
{:else}
<h3>Loading...</h3>
{/if}
@ -248,7 +245,7 @@
key: "move",
value: "Move"
},
...board.flat().map((_, c) => {
...board.map((_, c) => {
return {
key: c.toString(),
value: (c + 1).toString()