Skip to content

Commit

Permalink
big refacotr, add typescript.tsv
Browse files Browse the repository at this point in the history
  • Loading branch information
kuboon committed Dec 11, 2024
1 parent 4495fc5 commit c1741b2
Show file tree
Hide file tree
Showing 11 changed files with 110 additions and 86 deletions.
2 changes: 1 addition & 1 deletion _config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const site = lume({
site.use(esbuild({
extensions: [".ts", ".tsx"],
options: {
target: "es2020",
target: "es2022",
minify: false,
keepNames: true,
plugins: [yamlPlugin],
Expand Down
7 changes: 3 additions & 4 deletions src/_components/GameMain.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import RomajiField from "./RomajiField.tsx";
import { GameSettings } from "./_lib.ts";
import { GameSettings, QA } from "../_lib/types.ts";
import { Signal, useEffect, useSignal } from "../_deps.ts";

type QandA = { q: string; a: string };
type GameMainState = "ready" | "playing";

function Timer({ timer }: { timer: Signal<number> }) {
Expand Down Expand Up @@ -46,7 +45,7 @@ function addScoreGetAnimation(pt: number) {
}

export default function GameMain(
{ problems, settings }: { problems: QandA[]; settings: GameSettings },
{ problems, settings }: { problems: QA[]; settings: GameSettings },
) {
const currentNum = useSignal(0);
const state = useSignal<GameMainState>("ready");
Expand Down Expand Up @@ -150,7 +149,7 @@ export default function GameMain(
<>
<div class="question">{question}</div>
<RomajiField
answer={current.a}
answer={current.a || current.q}
voice={settings.voice}
/>
</>
Expand Down
4 changes: 2 additions & 2 deletions src/_components/RomajiField.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import RomajiYaml_ from "../_data/romaji.yaml" with { type: "json" };
import { loadRomajiDict, matchInput } from "./_engine.ts";
import { Hankaku } from "./_lib.ts";
import { loadRomajiDict, matchInput } from "../_lib/engine.ts";
import { Hankaku } from "../_lib/types.ts";
import { signal, useEffect } from "../_deps.ts";
import { hint } from "./Keyboard.tsx";

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
loadRomajiDict,
matchInput,
test
} from "./_engine.ts";
} from "./engine.ts";
import { assertEquals } from "@std/assert";
import { parse } from "@std/yaml/parse";

Expand Down
61 changes: 61 additions & 0 deletions src/_lib/fetchProblems.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { GameSettings, QA } from "./types.ts";
import { PCG } from "./pcg.ts";

function parseCsv(csv: string, separator = ","): QA[] {
return csv.split("\n")
.map((x) => x.split(separator))
.map(([q, a]) => ({ q, a }));
}
function parseSettings(problems: QA[]) {
const settings: GameSettings = {
title: "",
timelimit: 60,
shuffle: true,
voice: false,
};
problems.forEach(({ q, a }) => {
if (!q.startsWith(":")) return;
if(a === null) return;
switch (q) {
case ":title":
settings.title = a;
break;
case ":timelimit":
settings.timelimit = parseInt(a);
break;
case ":shuffle":
settings.shuffle = a !== "false";
break;
case ":voice":
settings.voice = a === "true";
break;
}
});
return settings;
}
function shuffleArray(array: unknown[], rng: PCG) {
for (let i = array.length - 1; i > 1; i--) {
const j = rng.nextInt(i);
[array[i], array[j]] = [array[j], array[i]];
}
}

export async function fetchProblems(url: string) {
const errors: string[] = [];
const separator = url.endsWith(".tsv") ? "\t" : ",";
const response = await fetch(url)
if (!response.ok) {
return { ok: false as const, errors: [`Failed to fetch ${url}: ${response.statusText}`] };
}
const data = parseCsv(await response.text(), separator);
const settings = parseSettings(data);
const problems = data.filter(({ q }) => !q.startsWith(":"));

if (settings.shuffle) {
const today = Math.floor(Date.now() / 86400000);
const rng = new PCG(BigInt(today));
shuffleArray(problems, rng);
}
if (errors.length === 0) return { ok: true as const, problems, settings }
return { ok: false as const, errors };
}
1 change: 1 addition & 0 deletions src/_components/_lib.ts → src/_lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export type GameSettings = {
shuffle: boolean;
voice: boolean;
};
export type QA = { q: string; a: string | null };
12 changes: 0 additions & 12 deletions src/csv/typescript.csv

This file was deleted.

13 changes: 13 additions & 0 deletions src/csv/typescript.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
:title typescript
let isDone=false
Record<string,number>
addEventListener('click',()=>{})
const list:number[]=[]
interface Person{name:string;age:number}
type StringOrNumber=string|number
return`Hello, ${name}`
enum Direction{Up,Down,Left,Right}
const tuple:[string,number]=['hello',10]
class Animal{constructor(public name:string){}}
const readOnlyArray:ReadonlyArray<number>=[1,2,3]
function greet(name:string):string{
2 changes: 1 addition & 1 deletion src/inst.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ layout: layout.vto
- [都道府県](/#/csv/todoufuken.csv)
- [県庁所在地](/#/csv/kenchou.csv)
- [百人一首](/#/csv/hyakunin.csv)
- [typescript](/#/csv/typescript.csv)
- [typescript](/#csv=/csv/typescript.tsv)

# 問題の作り方

Expand Down
92 changes: 27 additions & 65 deletions src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,34 @@
import GameMain from "./_components/GameMain.tsx";
import Keyboard from "./_components/Keyboard.tsx";
import { GameSettings } from "./_components/_lib.ts";
import { fetchProblems } from "./_lib/fetchProblems.ts";
import { render } from "./_deps.ts";
import { PCG } from "./_lib/pcg.ts";

function parseCsv(csv: string) {
return csv.split("\n")
.map((x) => x.split(","))
.map(([q, a]) => ({ q, a: a ? a.trim() : q }));
}

(async () => {
const hash = location.hash.slice(1);
let problems_;
if (hash.length > 0) {
problems_ = await fetch(hash).then((x) => x.text()).then(parseCsv).catch(
console.error,
);
}
problems_ = problems_ ||
await fetch("/csv/default.csv").then((x) => x.text()).then(parseCsv);

const settings: GameSettings = {
title: "",
timelimit: 60,
shuffle: true,
voice: false,
};
problems_.forEach(({ q, a }) => {
if (!q.startsWith(":")) return;
switch (q) {
case ":title":
settings.title = a;
break;
case ":timelimit":
settings.timelimit = parseInt(a);
break;
case ":shuffle":
settings.shuffle = a !== "false";
break;
case ":voice":
settings.voice = a === "true";
break;
}
});

function shuffleArray(array: unknown[], rng: PCG) {
for (let i = array.length - 1; i > 1; i--) {
const j = rng.nextInt(i);
[array[i], array[j]] = [array[j], array[i]];
}
const hash = location.hash.slice(1);
let csv = "/csv/default.csv"
if (hash.length > 0) {
if (!hash.includes("csv=")) {
csv = hash
} else {
const params = new URLSearchParams(hash);
csv = params.get("csv")!
}
}

const problems = problems_.filter(({ q }) => !q.startsWith(":"));
if (settings.shuffle) {
const today = Math.floor(Date.now() / 86400000);
const rng = new PCG(BigInt(today));
shuffleArray(problems, rng);
}
const App = (
<>
<GameMain problems={problems} settings={settings} />
<details open>
<summary>きーぼーど</summary>
<Keyboard />
</details>
</>
);
const appElem = document.getElementById("app")!;
const probsSets = await fetchProblems(csv)
if (!probsSets.ok) {
appElem.insertAdjacentHTML("beforeend", probsSets.errors.join("<br>"));
throw new Error('failed to init');
}

render(App, document.getElementById("app")!);
})();
const { problems, settings } = probsSets;
const App = (
<>
<GameMain problems={problems} settings={settings} />
<details open>
<summary>きーぼーど</summary>
<Keyboard />
</details>
</>
);
render(App, appElem);

0 comments on commit c1741b2

Please sign in to comment.