Skip to content

Commit

Permalink
feat: implement controls
Browse files Browse the repository at this point in the history
  • Loading branch information
Jabolol committed Jan 1, 2024
1 parent 7c6f012 commit bf28b33
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 28 deletions.
1 change: 1 addition & 0 deletions www/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"tailwindcss/": "npm:/[email protected]/",
"tailwindcss/plugin": "npm:/[email protected]/plugin.js",
"$std/": "https://deno.land/[email protected]/",
"icons/": "https://deno.land/x/[email protected]/tsx/",
"~/": "./"
},
"compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "preact" }
Expand Down
13 changes: 8 additions & 5 deletions www/hooks/useAsyncEffect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from "@preact/signals";

export function useAsyncEffect<T, K, V>(
instance: ReadonlySignal<() => () => AsyncGenerator<T, K, V>>,
instance: ReadonlySignal<() => AsyncGenerator<T, K, V>>,
context: Signal<Context>,
) {
const signal = useSignal<T>({} as T);
Expand All @@ -23,10 +23,13 @@ export function useAsyncEffect<T, K, V>(
};

useSignalEffect(() => {
if (!fired.value || context.value.change) {
fired.value = true;
context.value = { ...context.value, change: false };
runEffect(instance.value()());
switch (true) {
case context.value.change:
context.value = { ...context.value, change: false };
/* falls through */
case !fired.value:
fired.value = true;
runEffect(instance.value());
}
});

Expand Down
5 changes: 3 additions & 2 deletions www/islands/Game.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@ export default function Game() {
const context = useSignal<Context>(init(ref));
const kind = useComputed(() => context.value.step);
const fn = useComputed(() => (fnMap[context.value.step] ?? next));
const instance = useComputed(() => generate);
const instance = useComputed(() => generate());

useAsyncEffect(instance, context);

function generate() {
return async function* () {
while (kind.value !== "END") {

yield await fn.value(context);
}
};
}

return (
<div class="flex items-center justify-center flex-col gap-5">
<div class="flex items-center justify-center flex-col gap-5 m-5">
<canvas
width={WIDTH}
height={HEIGHT}
Expand Down
57 changes: 45 additions & 12 deletions www/islands/Menu.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,60 @@
import { Context } from "~/types.ts";
import { Signal, useComputed } from "@preact/signals";
import { sort } from "$std/semver/sort.ts";
import IconRepeat from "icons/repeat.tsx";
import IconPause from "icons/player-pause.tsx";
import IconPlayerPlay from "icons/player-play.tsx";

type MenuProps = {
context: Signal<Context>;
};

export default function Menu({ context }: MenuProps) {
const playing = useComputed(() => context.value.running);
const keys = useComputed(() => Object.keys(context.value.figures));

const execute = (selected: string) => {
context.value = {
...context.value,
step: "LOAD_WASM",
pause: false,
change: true,
selected,
};
};

const toggle = (pause: boolean) => {
context.value = {
...context.value,
step: pause ? "END" : "LOAD_WASM",
pause: true,
change: true,
running: false,
};
};

return (
<div class="w-full flex items-center justify-end">
<div class="w-full flex items-center justify-end gap-2">
{playing.value
? (
<IconPause
onClick={() => toggle(true)}
class="hover:cursor-pointer border-4 border-black rounded-lg h-12 w-[25%]"
/>
)
: (
<IconPlayerPlay
onClick={() => toggle(false)}
class="hover:cursor-pointer border-4 border-black rounded-lg h-12 w-[25%]"
/>
)}
<IconRepeat
onClick={() => execute(context.value.selected)}
class="hover:cursor-pointer border-4 border-black rounded-lg h-12 w-[25%]"
/>
<select
onChange={(evt) => {
context.value = {
...context.value,
step: "LOAD_WASM",
change: true,
selected: evt.currentTarget.value,
};
}}
onChange={({ currentTarget: { value } }) => execute(value)}
name="pattern"
class="border-4 border-black rounded-lg py-2 max-w-[25%] w-full"
class="hover:cursor-pointer focus:outline-none border-4 border-black rounded-lg py-2 w-[50%] h-12"
>
{keys.value.length
? (
Expand All @@ -37,7 +70,7 @@ export default function Menu({ context }: MenuProps) {
: (
<option
value="loading"
class="border-4 border-black rounded-lg py-2 max-w-[25%] w-full"
class="border-4 border-black rounded-lg py-2 max-w-[50%] w-full"
disabled
selected
>
Expand Down
3 changes: 1 addition & 2 deletions www/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import Game from "~/islands/Game.tsx";

export default function Home() {
return (
<div class="flex items-center justify-center h-screen flex-col gap-20">
<h1 class="text-4xl">Conway's Game of Life</h1>
<div class="flex items-center justify-center h-screen flex-co">
<Game />
</div>
);
Expand Down
1 change: 1 addition & 0 deletions www/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export type Context = {
selected: string;
figures: Figures;
change: boolean;
pause: boolean;
running: boolean;
canvas: RefObject<HTMLCanvasElement>;
};
23 changes: 16 additions & 7 deletions www/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,17 @@ export const initMap = (context: Signal<Context>) => {

const item = context.value.figures[context.value.selected];

context.value.exports.reset();
ctx.clearRect(0, 0, WIDTH, HEIGHT);
if (!context.value.pause) {
context.value.exports.reset();
ctx.clearRect(0, 0, WIDTH, HEIGHT);

for (const [x, y] of item) {
context.value.exports.set_cell(x, y, 1);
ctx.fillStyle = COLOR;
ctx.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
for (const [x, y] of item) {
context.value.exports.set_cell(x, y, 1);
ctx.fillStyle = COLOR;
ctx.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
}
} else {
context.value.pause = false;
}

next(context);
Expand All @@ -168,6 +172,10 @@ export const initLoop = (context: Signal<Context>) => {
}

const animation = () => {
if (!context.value.running) {
return;
}

context.value.exports.step();

ctx.clearRect(0, 0, WIDTH, HEIGHT);
Expand All @@ -186,7 +194,7 @@ export const initLoop = (context: Signal<Context>) => {
setTimeout(() => requestAnimationFrame(animation), DELAY);
};
if (!context.value.running) {
context.value = {...context.value, running: true};
context.value = { ...context.value, running: true };
requestAnimationFrame(animation);
}
next(context);
Expand All @@ -206,5 +214,6 @@ export const init = (ref: RefObject<HTMLCanvasElement>) => ({
figures: {},
change: false,
running: false,
pause: false,
selected: "koks_galaxy",
} as Context);

0 comments on commit bf28b33

Please sign in to comment.