import React, { useState, useEffect, useContext, useCallback } from 'react'; import { Link } from 'react-router-dom'; import { ArrowLeftIcon, XIcon, EraserIcon, LightbulbIcon, ShapesIcon, } from '@phosphor-icons/react'; import Seo from '../../components/Seo'; import { ToastContext } from '../../context/ToastContext'; import GenerativeArt from '../../components/GenerativeArt'; import BreadcrumbTitle from '../../components/BreadcrumbTitle'; const PUZZLES = [ [ [0, 1, 0, 1, 0], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [0, 1, 1, 1, 0], [0, 0, 1, 0, 0], ], [ [1, 1, 1, 1, 1], [1, 0, 0, 0, 0], [1, 1, 1, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], ], [ [0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [1, 1, 1, 1, 1], [0, 1, 1, 1, 0], [0, 0, 1, 0, 0], ], [ [0, 1, 0, 1, 0], [1, 1, 1, 1, 1], [0, 1, 0, 1, 0], [1, 1, 1, 1, 1], [0, 1, 0, 1, 0], ], [ [0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], ], [ [0, 1, 1, 1, 0], [1, 0, 1, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0], [0, 0, 0, 0, 0], ], [ [1, 0, 0, 0, 1], [1, 1, 0, 0, 1], [1, 0, 1, 0, 1], [1, 0, 0, 1, 1], [1, 0, 0, 0, 1], ], [ [1, 0, 1, 0, 1], [0, 1, 0, 1, 0], [1, 0, 1, 0, 1], [0, 1, 0, 1, 0], [1, 0, 1, 0, 1], ], [ [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [1, 1, 1, 1, 1], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], ], [ [0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [1, 1, 1, 1, 1], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], ], [ [0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [1, 1, 1, 1, 1], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], ], [ [0, 0, 1, 1, 0], [0, 1, 1, 0, 0], [1, 1, 1, 1, 1], [0, 1, 1, 0, 0], [0, 0, 1, 1, 0], ], [ [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0], ], [ [1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 1, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 1], ], [ [1, 0, 0, 0, 0], [1, 1, 0, 0, 0], [0, 1, 1, 0, 0], [0, 0, 1, 1, 0], [0, 0, 0, 1, 1], ], [ [0, 0, 0, 0, 0], [0, 1, 0, 1, 0], [0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [0, 1, 0, 1, 0], ], [ [0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], ], [ [0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [1, 1, 1, 1, 1], [0, 1, 0, 1, 0], [1, 0, 0, 0, 1], ], [ [1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 1, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 1], ], [ [0, 1, 1, 1, 0], [1, 1, 1, 1, 1], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], ], [ [1, 1, 1, 1, 1], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0], [1, 1, 1, 1, 1], ], [ [0, 0, 1, 0, 0], [0, 1, 0, 1, 0], [1, 0, 0, 0, 1], [0, 1, 0, 1, 0], [0, 0, 1, 0, 0], ], [ [0, 0, 0, 0, 0], [0, 1, 0, 1, 0], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [0, 1, 1, 1, 0], ], ]; const PUZZLENAMES = [ 'Heart', 'Fez', 'Diamond', 'Cross', 'House', 'Smile', 'Letter N', 'Checker', 'Plus', 'Arrow', 'Tree', 'Fish', 'Letter U', 'Target', 'Stairs', 'Face', 'Letter A', 'Star', 'Spiral', 'Mushroom', 'Rook', 'Hollow', 'Boat', ]; const generateClues = (puzzle) => { const numRows = puzzle.length; const numCols = puzzle[0].length; const rowClues = []; const colClues = []; for (let r = 0; r < numRows; r++) { const row = puzzle[r]; let current = []; let size = 0; for (let c = 0; c < numCols; c++) { if (row[c] === 1) size++; else if (size > 0) { current.push(size); size = 0; } } if (size > 0) current.push(size); rowClues.push(current.length > 0 ? current : [0]); } for (let c = 0; c < numCols; c++) { let current = []; let size = 0; for (let r = 0; r < numRows; r++) { if (puzzle[r][c] === 1) size++; else if (size > 0) { current.push(size); size = 0; } } if (size > 0) current.push(size); colClues.push(current.length > 0 ? current : [0]); } return { rowClues, colClues }; }; const NonogramPage = () => { const appName = 'Nonogram'; const { addToast } = useContext(ToastContext); const [solvedPuzzle, setSolvedPuzzle] = useState([]); const [playerGrid, setPlayerGrid] = useState([]); const [rowClues, setRowClues] = useState([]); const [colClues, setColClues] = useState([]); const [gameWon, setGameWon] = useState(false); const [message, setMessage] = useState(''); const [puzzleName, setPuzzleName] = useState(''); const initGame = useCallback(() => { const idx = Math.floor(Math.random() * PUZZLES.length); const puzzle = PUZZLES[idx]; setPuzzleName(PUZZLENAMES[idx]); setSolvedPuzzle(puzzle); const { rowClues: r, colClues: c } = generateClues(puzzle); setRowClues(r); setColClues(c); setPlayerGrid( Array(puzzle.length) .fill(null) .map(() => Array(puzzle[0].length).fill(0)), ); setGameWon(false); setMessage(''); }, []); useEffect(() => { initGame(); }, [initGame]); const checkWin = useCallback(() => { if (solvedPuzzle.length === 0) return false; for (let r = 0; r < solvedPuzzle.length; r++) { for (let c = 0; c < solvedPuzzle[0].length; c++) { if (playerGrid[r][c] === 1 && solvedPuzzle[r][c] !== 1) return false; if (playerGrid[r][c] === 0 && solvedPuzzle[r][c] === 1) return false; } } return true; }, [playerGrid, solvedPuzzle]); useEffect(() => { if (playerGrid.length > 0 && solvedPuzzle.length > 0 && checkWin()) { setGameWon(true); setMessage(`Pattern Identified :: ${puzzleName} decrypted.`); addToast({ title: 'Success', message: 'Puzzle Solved!', type: 'success', }); } }, [playerGrid, solvedPuzzle, checkWin, addToast, puzzleName]); const handleCellClick = (row, col, event) => { if (gameWon) return; event.preventDefault(); setPlayerGrid((prev) => { const next = prev.map((r) => [...r]); if (event.type === 'contextmenu') next[row][col] = next[row][col] === 2 ? 0 : 2; else next[row][col] = next[row][col] === 1 ? 0 : 1; return next; }); }; return (
Applications

Picture logic protocol. Decrypt the pattern through grid-based identification.

Target {puzzleName}
Status {gameWon ? 'SOLVED' : 'ACTIVE'}
{/* Main Action Column */}
{/* Nonogram Grid Container */}
{colClues.map((clues, i) => (
{clues.map((c, j) => ( {c} ))}
))}
{rowClues.map((clues, i) => (
{clues.map((c, j) => ( {c} ))}
))}
{playerGrid.map((row, r) => row.map((cell, c) => ( )), )}
{message && (

{message}

)}
{/* Info Column */}

Logic_Manual

  • 01 Fill cells based on row/column numbers.
  • 02 Numbers represent consecutive filled blocks.
  • 03 Left-click to fill, Right-click to mark empty space.
); }; export default NonogramPage;