Skip to content

Commit

Permalink
refactor engine settings (closes #39)
Browse files Browse the repository at this point in the history
  • Loading branch information
franciscoBSalgueiro committed Oct 14, 2023
1 parent c3b1858 commit df78750
Show file tree
Hide file tree
Showing 16 changed files with 266 additions and 210 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"start": "vite",
"build": "tsc && vite build",
"build": "tsc --noEmit && vite build",
"tauri": "tauri",
"pretty": "prettier --write \"./src/**/*.{ts,tsx}\"",
"test": "vitest run",
Expand Down
113 changes: 108 additions & 5 deletions src/atoms/atoms.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { atomFamily, atomWithStorage, createJSONStorage } from "jotai/utils";
import { atomFamily, atomWithStorage, createJSONStorage, loadable } from "jotai/utils";
import { Tab, genID } from "@/utils/tabs";
import { MantineColor } from "@mantine/core";
import { Session } from "../utils/session";
Expand All @@ -8,6 +8,44 @@ import { MissingMove } from "@/utils/repertoire";
import { Card, buildFromTree } from "@/components/files/opening";
import { GameHeaders, TreeNode } from "@/utils/treeReducer";
import { AtomFamily } from "jotai/vanilla/utils/atomFamily";
import EngineSettings from "@/components/panels/analysis/EngineSettings";
import { AsyncStringStorage } from "jotai/vanilla/utils/atomWithStorage";
import { BaseDirectory, readTextFile, removeFile, writeTextFile } from "@tauri-apps/api/fs";
import { Engine } from "@/utils/engines";


const options = { dir: BaseDirectory.AppData };
const fileStorage: AsyncStringStorage = {
async getItem(key) {
try {
return await readTextFile(key, options);
} catch (error) {
return null;
}
},
async setItem(key, newValue) {
try {
await writeTextFile(key, newValue, options);
} catch (error) {
throw new Error('Unable to set item.');
}
},
async removeItem(key) {
try {
await removeFile(key, options);
} catch (error) {
throw new Error('Unable to remove item.');
}
}
}

export const enginesAtom = atomWithStorage<Engine[]>(
"engines/engines.json",
[],
createJSONStorage(() => fileStorage),
);

const loadableEnginesAtom = loadable(enginesAtom);

// Tabs

Expand Down Expand Up @@ -97,8 +135,9 @@ export const missingMovesAtom = atomWithStorage<TabMap<MissingMove[] | null>>(
createJSONStorage(() => sessionStorage)
);


function tabValue<T extends object | string | boolean>(family: AtomFamily<string, PrimitiveAtom<T>>) {
function tabValue<T extends object | string | boolean>(
family: AtomFamily<string, PrimitiveAtom<T>>
) {
return atom(
(get) => {
const tab = get(currentTabAtom);
Expand All @@ -116,7 +155,7 @@ function tabValue<T extends object | string | boolean>(family: AtomFamily<string
const atom = family(tab.value);
set(atom, nextValue);
}
)
);
}

// Board Options
Expand All @@ -133,7 +172,17 @@ const practicingFamily = atomFamily((tab: string) => atom(false));
export const currentPracticingAtom = tabValue(practicingFamily);

export const deckAtomFamily = atomFamily(
({ id, game, root, headers }: { id: string, game: number, root: TreeNode, headers: GameHeaders }) => {
({
id,
game,
root,
headers,
}: {
id: string;
game: number;
root: TreeNode;
headers: GameHeaders;
}) => {
const a = atomWithStorage<Card[]>(`deck-${id}-${game}`, []);
a.onMount = (set) => {
if (localStorage.getItem(`deck-${id}-${game}`) === null) {
Expand All @@ -149,3 +198,57 @@ export const deckAtomFamily = atomFamily(
},
(a, b) => a.id === b.id && a.game === b.game
);

export type EngineSettings = {
enabled: boolean;
maxDepth: number;
cores: number;
numberLines: number;
};

export const tabEngineSettingsFamily = atomFamily(
({ tab, engine }: { tab: string; engine: string }) =>
atom<EngineSettings>({
enabled: false,
maxDepth: 24,
cores: 2,
numberLines: 3,
}),
(a, b) => a.tab === b.tab && a.engine === b.engine
);

export const allEnabledAtom = loadable(atom(
async (get) => {
const engines = await get(enginesAtom);

const v = engines
.filter((e) => e.loaded)
.every((engine) => {
const atom = tabEngineSettingsFamily({
tab: get(activeTabAtom)!,
engine: engine.name,
});
return get(atom).enabled;
});

return v;
},
));

export const enableAllAtom = atom(
null,
(get, set, value: boolean) => {
const engines = get(loadableEnginesAtom);
if (!(engines.state === "hasData")) return;

for (const engine of engines.data.filter((e) => e.loaded)) {
const atom = tabEngineSettingsFamily({
tab: get(activeTabAtom)!,
engine: engine.name,
});
set(atom, { ...get(atom), enabled: value });
}

}

);
5 changes: 2 additions & 3 deletions src/components/boards/BoardGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { Chess, DEFAULT_POSITION } from "chess.js";
import { useContext, useEffect, useRef, useState } from "react";
import BoardLayout from "@/layouts/BoardLayout";
import { parseUci } from "@/utils/chess";
import { useEngines } from "@/utils/engines";
import { invoke } from "@/utils/invoke";
import { getNodeAtPath } from "@/utils/treeReducer";
import GameInfo from "../common/GameInfo";
Expand All @@ -32,7 +31,7 @@ import {
} from "../common/TreeStateContext";
import BoardPlay from "./BoardPlay";
import GameNotation from "./GameNotation";
import { activeTabAtom, tabsAtom } from "@/atoms/atoms";
import { activeTabAtom, enginesAtom, tabsAtom } from "@/atoms/atoms";
import { useAtom, useAtomValue } from "jotai";
import { match } from "ts-pattern";

Expand Down Expand Up @@ -147,7 +146,7 @@ function BoardGame() {

const boardRef = useRef(null);

const { engines } = useEngines();
const engines = useAtomValue(enginesAtom);
const [inputColor, setInputColor] = useState<"white" | "random" | "black">(
"white"
);
Expand Down
19 changes: 8 additions & 11 deletions src/components/engines/AddEngine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ import { useForm } from "@mantine/form";
import { IconAlertCircle, IconDatabase, IconTrophy } from "@tabler/icons-react";
import { platform } from "@tauri-apps/api/os";
import { appDataDir, join, resolve } from "@tauri-apps/api/path";
import { Dispatch, SetStateAction, useCallback, useState } from "react";
import { useCallback, useState } from "react";
import { Engine, useDefaultEngines } from "@/utils/engines";
import { formatBytes } from "@/utils/format";
import { invoke } from "@/utils/invoke";
import ProgressButton from "../common/ProgressButton";
import useSWR from "swr";
import { match } from "ts-pattern";
import EngineForm from "./EngineForm";
import { useAtom } from "jotai";
import { enginesAtom } from "@/atoms/atoms";

const useStyles = createStyles((theme) => ({
card: {
Expand All @@ -42,16 +44,13 @@ const useStyles = createStyles((theme) => ({
}));

function AddEngine({
engines,
opened,
setOpened,
setEngines,
}: {
engines: Engine[];
opened: boolean;
setOpened: (opened: boolean) => void;
setEngines: Dispatch<SetStateAction<Engine[]>>;
}) {
const [engines, setEngines] = useAtom(enginesAtom);
const { data: os } = useSWR("os", async () => {
const p = await platform();
const os = match(p)
Expand Down Expand Up @@ -107,7 +106,6 @@ function AddEngine({
engine={engine}
engineId={i}
key={i}
setEngines={setEngines}
initInstalled={engines.some((e) => e.name === engine.name)}
/>
))}
Expand All @@ -128,7 +126,7 @@ function AddEngine({
submitLabel="Add"
form={form}
onSubmit={(values: Engine) => {
setEngines((prev) => [...prev, values]);
setEngines(async (prev) => [...(await prev), values]);
setOpened(false);
}}
/>
Expand All @@ -139,17 +137,16 @@ function AddEngine({
}

function EngineCard({
setEngines,
engine,
engineId,
initInstalled,
}: {
setEngines: Dispatch<SetStateAction<Engine[]>>;
engine: Engine;
engineId: number;
initInstalled: boolean;
}) {
const [inProgress, setInProgress] = useState<boolean>(false);
const [, setEngines] = useAtom(enginesAtom);
const downloadEngine = useCallback(
async (id: number, url: string) => {
setInProgress(true);
Expand All @@ -170,8 +167,8 @@ function EngineCard({
...engine.path.split("/")
);
await invoke("set_file_as_executable", { path: enginePath });
setEngines((prev) => [
...prev,
setEngines(async (prev) => [
...(await prev),
{
...engine,
path: enginePath,
Expand Down
20 changes: 7 additions & 13 deletions src/components/engines/EditEngine.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import { Modal, ScrollArea } from "@mantine/core";
import { Modal } from "@mantine/core";
import { useForm } from "@mantine/form";
import { Dispatch, SetStateAction } from "react";
import { Engine } from "@/utils/engines";
import EngineForm from "./EngineForm";
import { enginesAtom } from "@/atoms/atoms";
import { useAtom } from "jotai";

export default function EditEngine({
initialEngine,
engines,
opened,
setOpened,
setEngines,
}: {
initialEngine: Engine;
engines: Engine[];
opened: boolean;
setOpened: (opened: boolean) => void;
setEngines: Dispatch<SetStateAction<Engine[]>>;
}) {
const [engines, setEngines] = useAtom(enginesAtom);
const form = useForm<Engine>({
initialValues: initialEngine,

Expand All @@ -33,17 +31,13 @@ export default function EditEngine({
});

return (
<Modal
opened={opened}
onClose={() => setOpened(false)}
title="Edit Engine"
>
<Modal opened={opened} onClose={() => setOpened(false)} title="Edit Engine">
<EngineForm
submitLabel="Save"
form={form}
onSubmit={(values) => {
setEngines((prev) =>
prev.map((e) => (e === initialEngine ? values : e))
setEngines(async (prev) =>
(await prev).map((e) => (e === initialEngine ? values : e))
);
setOpened(false);
}}
Expand Down
Loading

0 comments on commit df78750

Please sign in to comment.