slick-connections.pages.sei.../src/usePuzzleModel.ts
2025-05-29 15:59:20 -04:00

163 lines
4.5 KiB
TypeScript

import { Accessor, createEffect } from "solid-js";
import connections from "./assets/connections.json";
import { shuffleArray } from "./utils";
import { createStore } from "solid-js/store";
import useAppModel from "./useAppModel";
type Connection = (typeof connections)[number];
type Answer = Connection["answers"][number];
function fromIndex(index: number): [number, number] {
const col = index % 4;
const row = (index - col) / 4;
return [row, col];
}
type PuzzleStore = {
guesses: number;
pinnedCount: number;
selected: number[];
solvedGroups: Answer[];
puzzle: number[];
guessHistory: string;
};
const emoji = ["🟨", "🟩", "🟦", "🟪"];
export default function usePuzzleModel(id: Accessor<number>) {
const [store, setStore] = createStore<PuzzleStore>({
guesses: 0,
pinnedCount: 0,
selected: [],
solvedGroups: [],
puzzle: [],
guessHistory: "",
});
const { setSolution } = useAppModel();
createEffect(() => {
setStore({
guesses: 0,
pinnedCount: 0,
selected: [],
solvedGroups: [],
puzzle: shuffleArray(Array.from({ length: 16 }, (_, i) => i)),
guessHistory: `Connections
Puzzle #${id()}`,
});
});
const answers = (): Answer[] => {
return connections.find((x) => x.id === id())!.answers;
};
const getFromPuzzle = (index: number) => {
const puzzleIndex = store.puzzle[index];
const [groupIndex, memberIndex] = fromIndex(puzzleIndex);
const group = answers()[groupIndex];
return {
group: group.group,
level: group.level,
answer: answers()[groupIndex].members[memberIndex],
};
};
const handleGuess = () => {
setStore("guesses", (x) => x + 1);
const selected = store.puzzle.length === 4 ? [0, 1, 2, 3] : store.selected;
const selectedAnswers = selected.map((x) => getFromPuzzle(x));
const { level } = selectedAnswers[0];
const isCorrect = selectedAnswers.every((x) => x.level === level);
const guessHistoryLine = selectedAnswers
.map((x) => emoji[x.level])
.join("");
setStore({
guessHistory: `${store.guessHistory}
${guessHistoryLine}`,
});
if (!isCorrect) {
// TODO you got it wrong
alert("wrong");
return;
}
const selectedPinnedCount = selected.reduce(
(acc, cur) => acc + (cur < store.pinnedCount ? 1 : 0),
0
);
setStore({
pinnedCount: store.pinnedCount - selectedPinnedCount,
puzzle: store.puzzle.filter((x) =>
selected.every((s) => store.puzzle[s] !== x)
),
selected: [],
});
const newSolvedGroup = answers().find((x) => x.level === level);
if (newSolvedGroup != null) {
setStore("solvedGroups", (x) => x.concat(newSolvedGroup));
}
if (store.puzzle.length === 0) {
// completely solved!
setSolution(id(), store.guesses);
}
};
const handleShuffle = () => {
const pinned = store.puzzle.slice(0, store.pinnedCount);
const toShuffle = store.puzzle.slice(store.pinnedCount);
setStore({
puzzle: [...pinned, ...shuffleArray(toShuffle)],
});
};
const handleDeselect = () => {
setStore({
selected: [],
});
};
const handlePinUnpin = () => {
if (store.selected.every((x) => x < store.pinnedCount)) {
// we are unpinning
const puzzleStart = Array.from({ length: store.pinnedCount }, (_, i) => i)
.filter((x) => !store.selected.includes(x))
.map((x) => store.puzzle[x]);
const puzzleMiddle = store.selected.map((x) => store.puzzle[x]);
const puzzleEnd = store.puzzle.slice(store.pinnedCount);
const newPuzzle = [...puzzleStart, ...puzzleMiddle, ...puzzleEnd];
setStore({
pinnedCount: store.pinnedCount - store.selected.length,
selected: [],
puzzle: newPuzzle,
});
return;
}
// we are pinning
const puzzleStart = store.puzzle.slice(0, store.pinnedCount);
const puzzleMid = store.selected
.filter((x) => x >= store.pinnedCount)
.map((x) => store.puzzle[x]);
const puzzleEnd = Array.from(
{ length: store.puzzle.length - store.pinnedCount },
(_, i) => i + store.pinnedCount
)
.filter((x) => !store.selected.includes(x))
.map((x) => store.puzzle[x]);
setStore({
pinnedCount: puzzleStart.length + puzzleMid.length,
selected: [],
puzzle: [...puzzleStart, ...puzzleMid, ...puzzleEnd],
});
};
return {
connections,
store,
setStore,
handleGuess,
handlePinUnpin,
handleShuffle,
handleDeselect,
getFromPuzzle,
};
}