diff --git a/.eslintrc.js b/.eslintrc.js index 43056e9..3dce7d9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -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", diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..74574bf --- /dev/null +++ b/src/App.css @@ -0,0 +1,5 @@ +body, html, #root { + height: 100%; + width: 100%; + box-sizing: border-box; +} diff --git a/src/App.js b/src/App.js index a766fa1..23395fc 100644 --- a/src/App.js +++ b/src/App.js @@ -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 ( -
-

- Slither - Link -

- - - -
- ); + return ; }; export default App; diff --git a/src/components/AddPuzzleScreen/index.js b/src/components/AddPuzzleScreen/index.js index bea4d52..4dd5eb1 100644 --- a/src/components/AddPuzzleScreen/index.js +++ b/src/components/AddPuzzleScreen/index.js @@ -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}) => {
{shouldShowGrid ? ( - { - setGridNumbers([]); - setGridReset(true); - }} - editorMode - /> + ) : ( "Please set valid dimensions above" )}
- - {submitText} - + {shouldShowGrid && ( + + {submitText} + + )}
+ setGridReset(true)} editorMode /> )} diff --git a/src/components/Controls/styles.module.scss b/src/components/Controls/styles.module.scss index 4106fa1..fb13aea 100644 --- a/src/components/Controls/styles.module.scss +++ b/src/components/Controls/styles.module.scss @@ -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); - } } } diff --git a/src/components/Game/index.js b/src/components/Game/index.js new file mode 100644 index 0000000..07b6fb1 --- /dev/null +++ b/src/components/Game/index.js @@ -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 ( +
+

+ Slither + Link +

+ + + + +
+ ); +}; + +export default Game; diff --git a/src/App.styles.module.scss b/src/components/Game/styles.module.scss similarity index 100% rename from src/App.styles.module.scss rename to src/components/Game/styles.module.scss diff --git a/src/components/Grid/index.js b/src/components/Grid/index.js index 7aeaf6a..ee64620 100644 --- a/src/components/Grid/index.js +++ b/src/components/Grid/index.js @@ -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: , 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 && ( -
e.preventDefault()} - > - {Array(nHorLine - 1) - .fill(0) - .map((_, i) => { - return [ - , - , - ]; - }) - .concat([ - , - ])} -
- )} - {!readOnly && ( - - )} - +
e.preventDefault()} + > + {Array(nHorLine - 1) + .fill(0) + .map((_, i) => { + return [ + , + , + ]; + }) + .concat([ + , + ])} +
); }; diff --git a/src/components/Info/index.js b/src/components/Info/index.js index 64d77ac..b7b91ad 100644 --- a/src/components/Info/index.js +++ b/src/components/Info/index.js @@ -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 ( <> -
setOpen(true)}> - i +
setOpen(true)}> +
i
+ Info
{open && (
@@ -26,15 +28,36 @@ const Info = () => { Connect adjacent dots with vertical or horizontal lines, creating a single loop.
- +
Crossovers or branches are not allowed (as shown by dotted lines below).
- - + +
diff --git a/src/components/Info/styles.module.scss b/src/components/Info/styles.module.scss index fe869bd..4a2a94d 100644 --- a/src/components/Info/styles.module.scss +++ b/src/components/Info/styles.module.scss @@ -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; + } } } diff --git a/src/constants.js b/src/constants.js index 2b1ae2c..cb74265 100644 --- a/src/constants.js +++ b/src/constants.js @@ -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]}, diff --git a/src/index.css b/src/index.css index 8684abe..ec2585e 100644 --- a/src/index.css +++ b/src/index.css @@ -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; -} diff --git a/src/utils.js b/src/utils.js index d4c44d1..0ea4c16 100644 --- a/src/utils.js +++ b/src/utils.js @@ -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) &&