Some refactoring

This commit is contained in:
AmeyGohil
2022-04-22 19:38:59 +05:30
parent ddc78cc46c
commit 1ebeab929f
13 changed files with 298 additions and 269 deletions
+2
View File
@@ -1,3 +1,4 @@
// eslint-disable-next-line no-undef
module.exports = {
env: {
browser: true,
@@ -20,6 +21,7 @@ module.exports = {
},
plugins: ["react", "prettier", "react-hooks", "import"],
rules: {
"no-undef": "error",
"react/prop-types": "off",
"react/no-unescaped-entities": "warn",
"react-hooks/rules-of-hooks": "error",
+5
View File
@@ -0,0 +1,5 @@
body, html, #root {
height: 100%;
width: 100%;
box-sizing: border-box;
}
+3 -28
View File
@@ -1,35 +1,10 @@
import * as React from "react";
import styles from "./App.styles.module.scss";
import CheckSolution from "./components/CheckSolution";
import Grid from "./components/Grid";
import Info from "./components/Info";
import {examples} from "./examples";
import Game from "./components/Game";
import "./App.css";
const App = () => {
const [matrix, setMatirx] = React.useState([]);
const [{dim, numbers, solution}] = React.useState(
examples[Math.floor(Math.random() * examples.length)]
);
const [check, setCheck] = React.useState("none");
return (
<main>
<h1 className={styles.heading}>
<span>Slither</span>
<span>Link</span>
</h1>
<Grid
dim={dim}
numbers={numbers}
matrix={matrix}
updateMatrix={setMatirx}
check={check}
/>
<Info />
<CheckSolution matrix={matrix} solution={solution} setCheck={setCheck} />
</main>
);
return <Game />;
};
export default App;
+27 -24
View File
@@ -1,5 +1,6 @@
import * as React from "react";
import {getNumbers, multiStyles} from "../../utils";
import {getMatrix, getNumbers, multiStyles} from "../../utils";
import Controls from "../Controls";
import Grid from "../Grid";
import N from "../Grid/components/N";
@@ -8,8 +9,14 @@ import styles from "./styles.module.scss";
const AddPuzzleScreen = ({open, onClose}) => {
const [dim, setDim] = React.useState([3, 3]);
const [gridMat, setGridMat] = React.useState([]);
const [gridNumbers, setGridNumbers] = React.useState([]);
React.useEffect(() => {
if (!isNaN(dim[0]) && dim[0] > 0 && !isNaN(dim[1]) && dim[1] > 0) {
setGridMat(getMatrix(dim, [], gridNumbers));
}
}, [dim[0], dim[1]]);
React.useEffect(() => {
setGridNumbers(getNumbers(gridMat));
}, [JSON.stringify(gridMat)]);
@@ -17,10 +24,13 @@ const AddPuzzleScreen = ({open, onClose}) => {
const [gridReset, setGridReset] = React.useState(false);
React.useEffect(() => {
if (gridReset) {
if (!isNaN(dim[0]) && dim[0] > 0 && !isNaN(dim[1]) && dim[1] > 0) {
setGridNumbers([]);
setGridMat(getMatrix(dim));
}
setGridReset(false);
}
}, [gridReset]);
const [submitState, setSubmitState] = React.useState("none");
const [submitText, setSubmitText] = React.useState("Submit");
const onSubmit = () => {
@@ -40,7 +50,7 @@ const AddPuzzleScreen = ({open, onClose}) => {
}, [submitState]);
const shouldShowGrid =
!isNaN(dim[0]) && dim[0] > 0 && !isNaN(dim[1]) && dim[1] > 0 && !gridReset;
!isNaN(dim[0]) && dim[0] > 0 && !isNaN(dim[1]) && dim[1] > 0;
return (
<>
@@ -79,34 +89,27 @@ const AddPuzzleScreen = ({open, onClose}) => {
<div className={styles.content}>
<div className={styles.matrixGrid}>
{shouldShowGrid ? (
<Grid
dim={dim}
matrix={gridMat}
updateMatrix={setGridMat}
numbers={gridNumbers}
onReset={() => {
setGridNumbers([]);
setGridReset(true);
}}
editorMode
/>
<Grid matrix={gridMat} setMatrix={setGridMat} editorMode />
) : (
"Please set valid dimensions above"
)}
</div>
</div>
<div className={styles.content}>
<a
href={`mailto:amey23399@gmail.com?subject=Slither Link Example&body=${JSON.stringify(
gridNumbers
)}`}
className={multiStyles(styles, ["submitButton", submitState])}
onClick={onSubmit}
>
{submitText}
</a>
{shouldShowGrid && (
<a
href={`mailto:amey23399@gmail.com?subject=Slither Link Example&body=${JSON.stringify(
gridNumbers
)}`}
className={multiStyles(styles, ["submitButton", submitState])}
onClick={onSubmit}
>
{submitText}
</a>
)}
</div>
</section>
<Controls onReset={() => setGridReset(true)} editorMode />
</div>
)}
</>
+6 -7
View File
@@ -19,6 +19,7 @@
font-size: 12px;
color: #90a4ae;
gap: 2px;
cursor: pointer;
& > svg {
background-color: #263238;
@@ -28,21 +29,19 @@
border: 2px solid #455a64;
font-family: Georgia, 'Times New Roman', Times, serif;
color: #fafafa;
border-radius: 50%;
cursor: pointer;
transition: all 0.2s;
font-style: italic;
}
&:hover {
&:hover {
transform: scale(1.1);
& > svg {
border-color: #78909c;
font-size: 22px;
color: white;
}
}
&:hover {
transform: scale(1.1);
}
}
}
+49
View File
@@ -0,0 +1,49 @@
import * as React from "react";
import {examples} from "../../examples";
import {getMatrix} from "../../utils";
import CheckSolution from "../CheckSolution";
import Controls from "../Controls";
import Grid from "../Grid";
import Info from "../Info";
import styles from "./styles.module.scss";
const Game = () => {
const [matrix, setMatrix] = React.useState([]);
const [{dim = [], numbers, solution}] = React.useState(
examples[Math.floor(Math.random() * examples.length)]
);
const [check, setCheck] = React.useState("none");
// initializing matrix with example
React.useEffect(() => {
if (dim.length) {
setMatrix(getMatrix(dim, [], numbers));
}
}, [dim.length]);
const onReset = () => {
if (dim.length) {
setMatrix(getMatrix(dim, [], numbers));
}
};
const onRefresh = () => {
window.location.reload();
};
return (
<main>
<h1 className={styles.heading}>
<span>Slither</span>
<span>Link</span>
</h1>
<Grid matrix={matrix} setMatrix={setMatrix} state={check} />
<Info />
<CheckSolution matrix={matrix} solution={solution} setCheck={setCheck} />
<Controls onReset={onReset} onRefresh={onRefresh} />
</main>
);
};
export default Game;
+62 -178
View File
@@ -20,110 +20,19 @@ import styles from "./styles.module.scss";
V V V
*/
const DEFAULT_NODE = {neigh: [], n: -1};
export const DEFAULT_NODE = {neigh: [], n: -1};
/*
structure for neigh elements:
{state: <EDGE_STATE>, loc: [x, y]}
*/
const Grid = ({
dim = [3, 3],
edges = [],
numbers = [],
matrix: propMatrix = [],
updateMatrix: updatePropMatrix = () => {},
readOnly,
onReset: propOnReset,
editorMode,
check,
className,
}) => {
const nRows = dim[0];
const nCols = dim[1];
const nHorLine = nRows + 1;
const nVerLine = nCols + 1;
const [matrix, setMatrix] = React.useState([]);
const Grid = ({matrix, setMatrix, readOnly, editorMode, state, className}) => {
const nHorLine = matrix?.length || 0;
const nVerLine = matrix?.[0]?.length || 0;
const setPropMatrix = (matrix) => {
if (Array.isArray(propMatrix)) {
updatePropMatrix(matrix);
}
};
// initializing matrix with prop edges and numbers
React.useEffect(() => {
let temp = new Array(nHorLine)
.fill(0)
.map(() => new Array(nVerLine).fill(DEFAULT_NODE));
numbers.forEach(({r, c, n}) => {
if (r <= nRows && c <= nCols) {
temp[r - 1][c - 1] = {...temp[r - 1][c - 1], n};
}
});
edges.forEach(({a: [sx, sy], b: [ex, ey], notAllowed, hovered}) => {
const edgeState = notAllowed
? EDGE_STATE.NOT_ALLOWED
: hovered
? EDGE_STATE.HOVERED
: EDGE_STATE.ACTIVE;
let startX, startY, endX, endY, hor, ver;
hor = sx === ex;
ver = sy === ey;
// excluding cases where both points are same
// or where edge is not hor | ver
// Assumption: all edges are between two adjacent nodes
if ((hor && ver) || (!hor && !ver)) {
return;
}
// horizonal line
if (hor) {
startX = endX = sx;
if (sy > ey) {
startY = ey;
endY = sy;
}
if (sy < ey) {
startY = sy;
endY = ey;
}
}
// vertical line
if (ver) {
startY = endY = sy;
if (sx > ex) {
startX = ex;
endX = sx;
}
if (sx < ex) {
startX = sx;
endX = ex;
}
}
// check if all nodes are valid nodes
if (areValid([startX, endX], nRows) && areValid([startY, endY], nCols)) {
temp[startX][startY] = {
...temp[startX][startY],
neigh: [
...temp[startX][startY].neigh,
{state: edgeState, loc: [endX, endY]},
],
};
temp[endX][endY] = {
...temp[endX][endY],
neigh: [
...temp[endX][endY].neigh,
{state: edgeState, loc: [startX, startY]},
],
};
}
});
setMatrix(temp);
setPropMatrix(temp);
}, []);
if (nHorLine === 0 || nVerLine === 0) {
return null;
}
const updateMatrix = (dataArray) => {
let temp = matrix.slice();
@@ -131,10 +40,13 @@ const Grid = ({
temp[x][y] = {...temp[x][y], ...data};
});
setMatrix(temp);
setPropMatrix(temp);
};
const onLineClick = (x, y, direction, click) => {
if (readOnly) {
return;
}
const isMiddleClick = click === "middle";
const node = [x, y];
const otherNode = direction === "horizontal" ? [x, y + 1] : [x + 1, y];
@@ -204,89 +116,61 @@ const Grid = ({
onLineClick(x, y, direction, "middle");
const onNumberChange = (x, y, num) => {
if (!editorMode || readOnly) {
return;
}
updateMatrix([{node: [x, y], data: {n: num}}]);
};
const onReset = () => {
let temp = new Array(nHorLine)
.fill(0)
.map(() => new Array(nVerLine).fill(DEFAULT_NODE));
numbers.forEach(({r, c, n}) => {
if (r <= nRows && c <= nCols) {
temp[r - 1][c - 1] = {...temp[r - 1][c - 1], n};
}
});
if (editorMode) {
propOnReset();
}
setMatrix(temp);
setPropMatrix(temp);
};
const onRefresh = () => {
window.location.reload();
};
return (
<>
{matrix.length > 0 && (
<div
className={multiStyles(styles, [
"canvas",
className,
check,
readOnly && "readOnly",
])}
// disabling right click on grid
onContextMenu={(e) => e.preventDefault()}
>
{Array(nHorLine - 1)
.fill(0)
.map((_, i) => {
return [
<LineRow
matX={i}
mat={matrix}
n={nVerLine}
onLineClick={onLineLeftClick}
onLineRightClick={onLineRightClick}
onLineMiddleClick={onLineMiddleClick}
key={`line_row_${i}`}
/>,
<NumberRow
matX={i}
mat={matrix}
n={nVerLine}
onLineClick={onLineLeftClick}
onLineRightClick={onLineRightClick}
onLineMiddleClick={onLineMiddleClick}
onNumberChange={onNumberChange}
editorMode={editorMode}
key={`number_row_${i}`}
/>,
];
})
.concat([
<LineRow
matX={nHorLine - 1}
mat={matrix}
n={nVerLine}
onLineClick={onLineLeftClick}
onLineRightClick={onLineRightClick}
onLineMiddleClick={onLineMiddleClick}
key={`line_row_${nHorLine - 1}`}
/>,
])}
</div>
)}
{!readOnly && (
<Controls
onReset={onReset}
onRefresh={onRefresh}
editorMode={editorMode}
/>
)}
</>
<div
className={multiStyles(styles, [
"canvas",
className,
state,
readOnly && "readOnly",
])}
// disabling right click on grid
onContextMenu={(e) => e.preventDefault()}
>
{Array(nHorLine - 1)
.fill(0)
.map((_, i) => {
return [
<LineRow
matX={i}
mat={matrix}
n={nVerLine}
onLineClick={onLineLeftClick}
onLineRightClick={onLineRightClick}
onLineMiddleClick={onLineMiddleClick}
key={`line_row_${i}`}
/>,
<NumberRow
matX={i}
mat={matrix}
n={nVerLine}
onLineClick={onLineLeftClick}
onLineRightClick={onLineRightClick}
onLineMiddleClick={onLineMiddleClick}
onNumberChange={onNumberChange}
editorMode={editorMode}
key={`number_row_${i}`}
/>,
];
})
.concat([
<LineRow
matX={nHorLine - 1}
mat={matrix}
n={nVerLine}
onLineClick={onLineLeftClick}
onLineRightClick={onLineRightClick}
onLineMiddleClick={onLineMiddleClick}
key={`line_row_${nHorLine - 1}`}
/>,
])}
</div>
);
};
+28 -5
View File
@@ -4,6 +4,7 @@ import {
NOT_ALLOWED_1_GRID_PROPS,
NOT_ALLOWED_2_GRID_PROPS,
} from "../../constants";
import {getMatrix} from "../../utils";
import Grid from "../Grid";
import styles from "./styles.module.scss";
@@ -13,8 +14,9 @@ const Info = () => {
return (
<>
<div className={styles.button} onClick={() => setOpen(true)}>
i
<div className={styles.container} onClick={() => setOpen(true)}>
<div className={styles.button}>i</div>
<span>Info</span>
</div>
{open && (
<div className={styles.info}>
@@ -26,15 +28,36 @@ const Info = () => {
Connect adjacent dots with vertical or horizontal lines, creating
a single loop.
<div className={styles.eg}>
<Grid {...EXAMPLE_GRID_PROPS} />
<Grid
matrix={getMatrix(
EXAMPLE_GRID_PROPS.dim,
EXAMPLE_GRID_PROPS.edges,
EXAMPLE_GRID_PROPS.numbers
)}
readOnly
/>
</div>
</div>
<div className={styles.item}>
Crossovers or branches are not allowed (as shown by dotted lines
below).
<div className={styles.eg}>
<Grid {...NOT_ALLOWED_1_GRID_PROPS} />
<Grid {...NOT_ALLOWED_2_GRID_PROPS} />
<Grid
matrix={getMatrix(
NOT_ALLOWED_1_GRID_PROPS.dim,
NOT_ALLOWED_1_GRID_PROPS.edges,
NOT_ALLOWED_1_GRID_PROPS.numbers
)}
readOnly
/>
<Grid
matrix={getMatrix(
NOT_ALLOWED_2_GRID_PROPS.dim,
NOT_ALLOWED_2_GRID_PROPS.edges,
NOT_ALLOWED_2_GRID_PROPS.numbers
)}
readOnly
/>
</div>
</div>
<div className={styles.item}>
+33 -18
View File
@@ -2,31 +2,46 @@
box-sizing: border-box;
}
.button {
position: fixed;
bottom: 10px;
right: 10px;
.container {
display: flex;
align-items: center;
justify-content: center;
background-color: #263238;
height: 40px;
width: 40px;
padding: 10px;
font-size: 20px;
border: 2px solid #455a64;
font-family: Georgia, 'Times New Roman', Times, serif;
border-radius: 50%;
flex-direction: column;
position: fixed;
bottom: 10px;
right: 10px;
gap: 2px;
cursor: pointer;
transition: all 0.2s;
font-style: italic;
.button {
display: flex;
align-items: center;
justify-content: center;
background-color: #263238;
height: 40px;
width: 40px;
padding: 10px;
font-size: 20px;
border: 2px solid #455a64;
font-family: Georgia, 'Times New Roman', Times, serif;
border-radius: 50%;
transition: all 0.2s;
font-style: italic;
}
span {
font-size: 12px;
color: #90a4ae;
}
&:hover {
border-color: #78909c;
font-size: 22px;
transform: scale(1.1);
color: white;
.button {
border-color: #78909c;
font-size: 22px;
color: white;
}
}
}
-3
View File
@@ -5,7 +5,6 @@ export const EDGE_STATE = {
};
export const EXAMPLE_GRID_PROPS = {
readOnly: true,
dim: [7, 7],
edges: [
{a: [0, 0], b: [0, 1]},
@@ -135,7 +134,6 @@ export const EXAMPLE_GRID_PROPS = {
};
export const NOT_ALLOWED_1_GRID_PROPS = {
readOnly: true,
dim: [2, 2],
edges: [
{a: [0, 1], b: [1, 1], hovered: true},
@@ -147,7 +145,6 @@ export const NOT_ALLOWED_1_GRID_PROPS = {
};
export const NOT_ALLOWED_2_GRID_PROPS = {
readOnly: true,
dim: [2, 3],
edges: [
{a: [0, 1], b: [1, 1]},
-6
View File
@@ -11,9 +11,3 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
body, html, #root {
height: 100%;
width: 100%;
box-sizing: border-box;
}
+83
View File
@@ -1,3 +1,4 @@
import {DEFAULT_NODE} from "./components/Grid";
import {EDGE_STATE} from "./constants";
// to get multiple styles in space-seperated format
@@ -18,6 +19,88 @@ export const isValid = (n, lim) => n >= 0 && n <= lim;
// to check all elements are valid numbers
export const areValid = (n, lim) => n.every((n) => isValid(n, lim));
export const getMatrix = ([dim1, dim2], edges = [], numbers = []) => {
const nRows = dim1;
const nCols = dim2;
let temp = new Array(dim1 + 1)
.fill(0)
.map(() => new Array(dim2 + 1).fill(DEFAULT_NODE));
if (numbers.length > 0) {
numbers.forEach(({r, c, n}) => {
if (r <= nRows && c <= nCols) {
temp[r - 1][c - 1] = {...temp[r - 1][c - 1], n};
}
});
}
if (edges.length > 0) {
edges.forEach(({a: [sx, sy], b: [ex, ey], notAllowed, hovered}) => {
const edgeState = notAllowed
? EDGE_STATE.NOT_ALLOWED
: hovered
? EDGE_STATE.HOVERED
: EDGE_STATE.ACTIVE;
let startX, startY, endX, endY, hor, ver;
hor = sx === ex;
ver = sy === ey;
// excluding cases where both points are same
// or where edge is not hor | ver
// Assumption: all edges are between two adjacent nodes
if ((hor && ver) || (!hor && !ver)) {
return;
}
// horizonal line
if (hor) {
startX = endX = sx;
if (sy > ey) {
startY = ey;
endY = sy;
}
if (sy < ey) {
startY = sy;
endY = ey;
}
}
// vertical line
if (ver) {
startY = endY = sy;
if (sx > ex) {
startX = ex;
endX = sx;
}
if (sx < ex) {
startX = sx;
endX = ex;
}
}
// check if all nodes are valid nodes
if (areValid([startX, endX], nRows) && areValid([startY, endY], nCols)) {
temp[startX][startY] = {
...temp[startX][startY],
neigh: [
...temp[startX][startY].neigh,
{state: edgeState, loc: [endX, endY]},
],
};
temp[endX][endY] = {
...temp[endX][endY],
neigh: [
...temp[endX][endY].neigh,
{state: edgeState, loc: [startX, startY]},
],
};
}
});
}
return temp;
};
// to check if a node has an active edge
export const isNodeActive = (n) =>
Array.isArray(n?.neigh) &&