diff --git a/src/App.css b/src/App.css index 373a9e0..9694611 100644 --- a/src/App.css +++ b/src/App.css @@ -1,4 +1,9 @@ +*, :before, :after { + box-sizing: inherit; +} + :root { + box-sizing: border-box; font-family: Inter, Avenir, Helvetica, Arial, sans-serif; font-size: 16px; line-height: 24px; @@ -76,6 +81,22 @@ margin: 0 auto; } +.puzzle-header { + display: flex; + flex-direction: row; + > h1 { + flex-grow: 1; + text-align: left; + margin: 0; + } +} + +.puzzle-header-actions { + display: flex; + flex-direction: row; + gap: calc(1 * var(--unit)); +} + .puzzle-row { display: flex; justify-content: center; @@ -149,6 +170,13 @@ min-width: calc(20 * var(--unit)); } +select { + font-size: 0.8em; + line-height: 1; + margin: 0; + padding: 1em; +} + a { font-weight: 500; color: #646cff; diff --git a/src/App.tsx b/src/App.tsx index b07defe..8df8ac6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,118 +1,58 @@ import { For } from "solid-js"; -import connections from "./assets/connections.json"; import "./App.css"; -import { shuffleArray } from "./utils"; -import { createStore } from "solid-js/store"; - -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]; -} +import useAppModel from "./useAppModel"; function App() { - const [store, setStore] = createStore({ - puzzleIndex: 0, - pinnedCount: 3, - selected: [] as number[], - solvedGroups: [] as Answer[], - puzzle: shuffleArray(Array.from({ length: 16 }, (_, i) => i)), - get answers() { - return connections[store.puzzleIndex].answers; - }, - }); - - const getFromPuzzle = (index: number) => { - const puzzleIndex = store.puzzle[index]; - const [groupIndex, memberIndex] = fromIndex(puzzleIndex); - const group = store.answers[groupIndex]; - return { - group: group.group, - level: group.level, - answer: store.answers[groupIndex].members[memberIndex], - }; - }; - - const handleGuess = () => { - const selectedAnswers = store.selected.map((x) => getFromPuzzle(x)); - const { level } = selectedAnswers[0]; - const isCorrect = selectedAnswers.every((x) => x.level === level); - if (!isCorrect) { - // TODO you got it wrong - alert("wrong"); - return; - } - const selectedPinnedCount = store.selected.reduce( - (acc, cur) => acc + (cur < store.pinnedCount ? 1 : 0), - 0 - ); - setStore({ - pinnedCount: store.pinnedCount - selectedPinnedCount, - puzzle: store.puzzle.filter((x) => - store.selected.every((s) => store.puzzle[s] !== x) - ), - selected: [], - }); - const newSolvedGroup = store.answers.find((x) => x.level === level); - if (newSolvedGroup != null) { - setStore({ - solvedGroups: [...store.solvedGroups, newSolvedGroup], - }); - } - if (store.puzzle.length === 0) { - // completely solved! - } - }; - - const handleShuffle = () => { - const pinned = store.puzzle.slice(0, store.pinnedCount); - const toShuffle = store.puzzle.slice(store.pinnedCount); - setStore({ - puzzle: [...pinned, ...shuffleArray(toShuffle)], - }); - }; - - 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: 16 - 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] - }) - }; + const { + connections, + store, + setStore, + handleGuess, + handlePinUnpin, + handleSelectGame, + handleShuffle, + getFromPuzzle, + } = useAppModel(); return (
-

Slick Connections

+
+

Slick Connections

+
+ + + +
+
{({ group, level, members }) => (
diff --git a/src/useAppModel.ts b/src/useAppModel.ts new file mode 100644 index 0000000..56724e6 --- /dev/null +++ b/src/useAppModel.ts @@ -0,0 +1,130 @@ +import connections from "./assets/connections.json"; +import { shuffleArray } from "./utils"; +import { createStore } from "solid-js/store"; + +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]; +} + +export default function useAppModel() { + const [store, setStore] = createStore({ + puzzleId: 1, + pinnedCount: 0, + selected: [] as number[], + solvedGroups: [] as Answer[], + puzzle: shuffleArray(Array.from({ length: 16 }, (_, i) => i)), + get answers() { + return connections.find((x) => x.id === store.puzzleId)!.answers; + }, + }); + + const handleSelectGame = (newId: number) => { + setStore({ + puzzleId: newId, + selected: [], + pinnedCount: 0, + puzzle: shuffleArray(Array.from({ length: 16 }, (_, i) => i)), + solvedGroups: [], + }); + }; + + const getFromPuzzle = (index: number) => { + const puzzleIndex = store.puzzle[index]; + const [groupIndex, memberIndex] = fromIndex(puzzleIndex); + const group = store.answers[groupIndex]; + return { + group: group.group, + level: group.level, + answer: store.answers[groupIndex].members[memberIndex], + }; + }; + + const handleGuess = () => { + const selectedAnswers = store.selected.map((x) => getFromPuzzle(x)); + const { level } = selectedAnswers[0]; + const isCorrect = selectedAnswers.every((x) => x.level === level); + if (!isCorrect) { + // TODO you got it wrong + alert("wrong"); + return; + } + const selectedPinnedCount = store.selected.reduce( + (acc, cur) => acc + (cur < store.pinnedCount ? 1 : 0), + 0 + ); + setStore({ + pinnedCount: store.pinnedCount - selectedPinnedCount, + puzzle: store.puzzle.filter((x) => + store.selected.every((s) => store.puzzle[s] !== x) + ), + selected: [], + }); + const newSolvedGroup = store.answers.find((x) => x.level === level); + if (newSolvedGroup != null) { + setStore({ + solvedGroups: [...store.solvedGroups, newSolvedGroup], + }); + } + if (store.puzzle.length === 0) { + // completely solved! + } + }; + + const handleShuffle = () => { + const pinned = store.puzzle.slice(0, store.pinnedCount); + const toShuffle = store.puzzle.slice(store.pinnedCount); + setStore({ + puzzle: [...pinned, ...shuffleArray(toShuffle)], + }); + }; + + 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: 16 - 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, + handleSelectGame, + handleShuffle, + getFromPuzzle, + }; +}