tangled
alpha
login
or
join now
emarref.net
/
ticks
0
fork
atom
A daily game
0
fork
atom
overview
issues
pulls
pipelines
Display hints when right or close
Malcolm Fell
5 months ago
e58aa313
280c2c2c
+88
-49
4 changed files
expand all
collapse all
unified
split
src
components
game
variant-header.tsx
variant-hints.tsx
lib
hooks.ts
state.ts
+27
-17
src/components/game/variant-header.tsx
···
1
-
import { useBoardSubset } from "@/lib/hooks";
0
2
import { cva } from "class-variance-authority";
0
3
4
-
const variantHeader = cva("flex gap-2 text-sm text-main font-bold", {
5
variants: {
6
orientation: {
7
horizontal: "",
8
vertical: "flex-col items-center",
0
0
0
0
0
9
},
10
},
11
});
12
13
type Props = {
0
0
14
variant: number;
15
};
16
17
-
type RowProps = {
18
-
row: number;
19
-
} & Props;
0
0
0
0
20
21
-
type ColProps = {
22
-
col: number;
23
-
} & Props;
0
0
0
0
24
25
-
export function VariantHeader({ variant, ...props }: RowProps | ColProps) {
26
-
const orientation = "row" in props ? "horizontal" : "vertical";
27
-
const block = useBoardSubset(
28
-
"row" in props ? "row" : "col",
29
-
"row" in props ? props.row : props.col,
30
-
variant,
31
-
);
32
return (
33
-
<div className={variantHeader({ orientation })}>
34
-
{block.map((count, i) => (
35
<p key={i}>{count}</p>
36
))}
37
</div>
···
1
+
import { groupByValue } from "@/lib/game";
2
+
import { useBoardSubset, useSessionSubset } from "@/lib/hooks";
3
import { cva } from "class-variance-authority";
4
+
import { useMemo } from "react";
5
6
+
const variantHeader = cva("flex gap-2 text-sm font-bold", {
7
variants: {
8
orientation: {
9
horizontal: "",
10
vertical: "flex-col items-center",
11
+
},
12
+
result: {
13
+
correct: "text-lime-400",
14
+
almost: "text-amber-300",
15
+
inProgress: "text-main",
16
},
17
},
18
});
19
20
type Props = {
21
+
orientation: "horizontal" | "vertical";
22
+
index: number;
23
variant: number;
24
};
25
26
+
export function VariantHeader({ orientation, index, variant }: Props) {
27
+
const dir = orientation === "horizontal" ? "row" : "col";
28
+
const boardRow = useBoardSubset(dir, index);
29
+
const boardHints = groupByValue(boardRow, variant);
30
+
const sessionRow = useSessionSubset(dir, index);
31
+
const sessionHints = groupByValue(sessionRow, variant);
32
+
// TODO This re-renders way too much because of the selectAtom
33
34
+
const result =
35
+
sessionRow.toString() === boardRow.toString()
36
+
? "correct"
37
+
: sessionHints.toString() === boardHints.toString() &&
38
+
sessionRow.filter((i) => i > -1).length === boardRow.length
39
+
? "almost"
40
+
: "inProgress";
41
0
0
0
0
0
0
0
42
return (
43
+
<div className={variantHeader({ orientation, result })}>
44
+
{boardHints.map((count, i) => (
45
<p key={i}>{count}</p>
46
))}
47
</div>
+8
-12
src/components/game/variant-hints.tsx
···
22
blocks: number[][];
23
}
24
25
-
function headerProps<T extends "row" | "col">(orientation: T) {
26
-
return (index: number) => {
27
-
return {
28
-
[orientation]: index,
29
-
} as Record<T, number>;
30
-
};
31
-
}
32
-
33
export function VariantHints({ variant, position, blocks }: Props) {
34
const orientation = ["top", "bottom"].includes(position)
35
? "horizontal"
36
: "vertical";
37
-
const propsFactory = headerProps(
38
-
orientation === "horizontal" ? "col" : "row",
39
-
);
40
return (
41
<div className={header({ position, orientation })}>
42
{blocks.map((_, i) => (
···
49
position === "top" ? "justify-end" : "",
50
].join(" ")}
51
>
52
-
<VariantHeader key={i} variant={variant} {...propsFactory(i)} />
0
0
0
0
0
0
0
53
</div>
54
))}
55
</div>
···
22
blocks: number[][];
23
}
24
0
0
0
0
0
0
0
0
25
export function VariantHints({ variant, position, blocks }: Props) {
26
const orientation = ["top", "bottom"].includes(position)
27
? "horizontal"
28
: "vertical";
0
0
0
29
return (
30
<div className={header({ position, orientation })}>
31
{blocks.map((_, i) => (
···
38
position === "top" ? "justify-end" : "",
39
].join(" ")}
40
>
41
+
<VariantHeader
42
+
key={i}
43
+
orientation={
44
+
orientation === "horizontal" ? "vertical" : "horizontal"
45
+
}
46
+
index={i}
47
+
variant={variant}
48
+
/>
49
</div>
50
))}
51
</div>
+13
-18
src/lib/hooks.ts
···
1
import { useAtom, useAtomValue } from "jotai";
2
import { useMemo } from "react";
3
import {
4
-
_boardAtom,
5
boardAtom,
6
boardMatrixAtom,
7
gameResultAtom,
8
-
getGuessAtomAt,
9
-
getVariantAtomAt,
10
heightAtom,
11
preferencesAtom,
0
0
0
0
12
sessionMatrixAtom,
13
variantsAtom,
14
widthAtom,
15
} from "./state";
16
-
import { groupByValue, groupIntoCols, groupIntoRows } from "./game";
17
18
export function useGuessAt(index: number) {
19
-
const atomCell = useMemo(() => getGuessAtomAt(index), [index]);
20
const [cell] = useAtom(atomCell);
21
return cell;
22
}
···
26
}
27
28
export function useVariantAt(index: number) {
29
-
const variantAtom = useMemo(() => getVariantAtomAt(index), [index]);
30
return useAtomValue(variantAtom);
31
}
32
···
52
return useAtomValue(gameResultAtom);
53
}
54
55
-
export function useBoardSubset(
56
-
orientation: "row" | "col",
57
-
index: number,
58
-
variant: number,
59
-
) {
60
-
const [width] = useBoardDimensions();
61
-
const board = useBoard();
62
63
-
if (orientation === "row") {
64
-
return groupByValue(groupIntoRows(board, width)[index]!, variant);
65
-
} else {
66
-
return groupByValue(groupIntoCols(board, width)[index]!, variant);
67
-
}
68
}
69
70
export function usePreferences() {
···
1
import { useAtom, useAtomValue } from "jotai";
2
import { useMemo } from "react";
3
import {
0
4
boardAtom,
5
boardMatrixAtom,
6
gameResultAtom,
0
0
7
heightAtom,
8
preferencesAtom,
9
+
selectBoardSubset,
10
+
selectGuessAt,
11
+
selectSessionSubset,
12
+
selectVariantAt,
13
sessionMatrixAtom,
14
variantsAtom,
15
widthAtom,
16
} from "./state";
0
17
18
export function useGuessAt(index: number) {
19
+
const atomCell = useMemo(() => selectGuessAt(index), [index]);
20
const [cell] = useAtom(atomCell);
21
return cell;
22
}
···
26
}
27
28
export function useVariantAt(index: number) {
29
+
const variantAtom = useMemo(() => selectVariantAt(index), [index]);
30
return useAtomValue(variantAtom);
31
}
32
···
52
return useAtomValue(gameResultAtom);
53
}
54
55
+
export function useBoardSubset(dir: "row" | "col", index: number) {
56
+
const subset = useMemo(() => selectBoardSubset(dir, index), [dir, index]);
57
+
return useAtomValue(subset);
58
+
}
0
0
0
59
60
+
export function useSessionSubset(dir: "row" | "col", index: number) {
61
+
const subset = useMemo(() => selectSessionSubset(dir, index), [dir, index]);
62
+
return useAtomValue(subset);
0
0
63
}
64
65
export function usePreferences() {
+40
-2
src/lib/state.ts
···
103
},
104
);
105
106
-
export const getGuessAtomAt = (index: number) =>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
107
selectAtom(sessionAtom, (cells) => {
108
const cellAtom = cells[index];
109
if (typeof cellAtom === "undefined") {
···
112
return cellAtom;
113
});
114
115
-
export function getVariantAtomAt(index: number) {
116
return selectAtom(variantsAtom, (variants) => {
117
const variant = variants[index];
118
if (typeof variant === "undefined") {
···
103
},
104
);
105
106
+
export const selectBoardSubset = (dir: "row" | "col", index: number) =>
107
+
selectAtom(boardMatrixAtom, (board) => {
108
+
if (dir === "row") {
109
+
const row = board[index];
110
+
if (typeof row === "undefined") {
111
+
throw new Error(`No board ${dir} at index ${index}`);
112
+
}
113
+
return row;
114
+
} else {
115
+
return board.map((row) => {
116
+
const col = row[index];
117
+
if (typeof col === "undefined") {
118
+
throw new Error(`No board ${dir} at index ${index}`);
119
+
}
120
+
return col;
121
+
});
122
+
}
123
+
});
124
+
125
+
export const selectSessionSubset = (dir: "row" | "col", index: number) =>
126
+
selectAtom(sessionMatrixAtom, (session) => {
127
+
if (dir === "row") {
128
+
const row = session[index];
129
+
if (typeof row === "undefined") {
130
+
throw new Error(`No session ${dir} at index ${index}`);
131
+
}
132
+
return row;
133
+
} else {
134
+
return session.map((row) => {
135
+
const col = row[index];
136
+
if (typeof col === "undefined") {
137
+
throw new Error(`No session ${dir} at index ${index}`);
138
+
}
139
+
return col;
140
+
});
141
+
}
142
+
});
143
+
144
+
export const selectGuessAt = (index: number) =>
145
selectAtom(sessionAtom, (cells) => {
146
const cellAtom = cells[index];
147
if (typeof cellAtom === "undefined") {
···
150
return cellAtom;
151
});
152
153
+
export function selectVariantAt(index: number) {
154
return selectAtom(variantsAtom, (variants) => {
155
const variant = variants[index];
156
if (typeof variant === "undefined") {