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