From f8fd9ac4d8070d0ce64d1510461cce449af40326 Mon Sep 17 00:00:00 2001 From: Neeldhara Misra Date: Tue, 22 Jul 2025 04:50:42 +0530 Subject: [PATCH] Add Chessboard Repaint Puzzle - a challenging parity puzzle --- src/App.tsx | 6 +- src/components/ChessboardRepaintPuzzle.tsx | 376 +++++++++++++++++++++ src/components/InteractiveIndex.tsx | 9 + src/pages/ChessboardRepaintPuzzlePage.tsx | 12 + src/pages/theme-pages/Puzzles.tsx | 10 + 5 files changed, 411 insertions(+), 2 deletions(-) create mode 100644 src/components/ChessboardRepaintPuzzle.tsx create mode 100644 src/pages/ChessboardRepaintPuzzlePage.tsx diff --git a/src/App.tsx b/src/App.tsx index f6533d3..8cb6b39 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -33,6 +33,7 @@ import NimGamePage from "./pages/NimGamePage"; import AssistedNimGamePage from "./pages/AssistedNimGamePage"; import GoldCoinGamePage from "./pages/GoldCoinGamePage"; import PlateSwapPuzzlePage from "./pages/PlateSwapPuzzlePage"; +import ChessboardRepaintPuzzlePage from "./pages/ChessboardRepaintPuzzlePage"; import Foundations from "./pages/theme-pages/discrete-math/Foundations"; import Proofs from "./pages/theme-pages/discrete-math/Proofs"; import Counting from "./pages/theme-pages/discrete-math/Counting"; @@ -90,8 +91,9 @@ const App = () => ( } /> } /> } /> - } /> - } /> + } /> + } /> + } /> {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} } /> diff --git a/src/components/ChessboardRepaintPuzzle.tsx b/src/components/ChessboardRepaintPuzzle.tsx new file mode 100644 index 0000000..7598f55 --- /dev/null +++ b/src/components/ChessboardRepaintPuzzle.tsx @@ -0,0 +1,376 @@ +import React, { useState } from 'react'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { RefreshCw, RotateCcw, BookOpen, Trophy, CheckCircle, ArrowLeft, ExternalLink } from 'lucide-react'; +import SocialShare from './SocialShare'; +import { useSearchParams, useNavigate } from 'react-router-dom'; + +interface ChessboardRepaintPuzzleProps { + showSocialShare?: boolean; +} + +const ChessboardRepaintPuzzle: React.FC = ({ showSocialShare = false }) => { + const [searchParams] = useSearchParams(); + const navigate = useNavigate(); + const isFromPuzzles = searchParams.get('from') === 'puzzles'; + + // Initialize 8x8 chessboard with alternating colors + const initializeBoard = (): boolean[][] => { + const board: boolean[][] = []; + for (let row = 0; row < 8; row++) { + board[row] = []; + for (let col = 0; col < 8; col++) { + // true = black, false = white + board[row][col] = (row + col) % 2 === 0; + } + } + return board; + }; + + const [board, setBoard] = useState(initializeBoard); + const [moves, setMoves] = useState(0); + const [isComplete, setIsComplete] = useState(false); + const [showRules, setShowRules] = useState(false); + const [showVictory, setShowVictory] = useState(false); + + const resetPuzzle = () => { + setBoard(initializeBoard()); + setMoves(0); + setIsComplete(false); + setShowVictory(false); + }; + + const countBlackSquares = (board: boolean[][]): number => { + return board.flat().filter(square => square).length; + }; + + const repaintRow = (rowIndex: number) => { + const newBoard = board.map(row => [...row]); + for (let col = 0; col < 8; col++) { + newBoard[rowIndex][col] = !newBoard[rowIndex][col]; + } + setBoard(newBoard); + setMoves(moves + 1); + + const blackCount = countBlackSquares(newBoard); + if (blackCount === 1) { + setIsComplete(true); + setShowVictory(true); + } + }; + + const repaintColumn = (colIndex: number) => { + const newBoard = board.map(row => [...row]); + for (let row = 0; row < 8; row++) { + newBoard[row][colIndex] = !newBoard[row][colIndex]; + } + setBoard(newBoard); + setMoves(moves + 1); + + const blackCount = countBlackSquares(newBoard); + if (blackCount === 1) { + setIsComplete(true); + setShowVictory(true); + } + }; + + const repaint2x2Square = (startRow: number, startCol: number) => { + const newBoard = board.map(row => [...row]); + for (let row = startRow; row < startRow + 2 && row < 8; row++) { + for (let col = startCol; col < startCol + 2 && col < 8; col++) { + newBoard[row][col] = !newBoard[row][col]; + } + } + setBoard(newBoard); + setMoves(moves + 1); + + const blackCount = countBlackSquares(newBoard); + if (blackCount === 1) { + setIsComplete(true); + setShowVictory(true); + } + }; + + const renderSquare = (row: number, col: number) => { + const isBlack = board[row][col]; + return ( +
+ ); + }; + + const render2x2Button = (startRow: number, startCol: number) => { + return ( + + ); + }; + + return ( +
+ {/* Back to Puzzles button - only show when coming from puzzles */} + {isFromPuzzles && ( +
+ +
+ )} + + + + + + Chessboard Repaint Puzzle + + + + Repaint rows, columns, or 2×2 squares to achieve exactly one black square! + + + + {/* Game Controls */} +
+ + +
+ Moves: + + {moves} + +
+
+ Black Squares: + + {countBlackSquares(board)} + +
+
+ + {/* Chessboard */} +
+
+ {/* Row buttons */} +
+ {Array.from({ length: 8 }, (_, i) => ( + + ))} +
+ + {/* Column buttons */} +
+ {Array.from({ length: 8 }, (_, i) => ( + + ))} +
+ + {/* 2x2 square buttons */} +
+ {Array.from({ length: 7 }, (_, row) => + Array.from({ length: 7 }, (_, col) => ( +
+ {render2x2Button(row, col)} +
+ )) + )} +
+ + {/* Chessboard grid */} +
+ {board.map((row, rowIndex) => + row.map((square, colIndex) => renderSquare(rowIndex, colIndex)) + )} +
+
+
+ + {/* Instructions */} +
+
+

+ {isComplete ? ( + "🎉 Congratulations! You achieved exactly one black square!" + ) : ( + "Click row/column buttons or 2×2 square buttons to repaint. Goal: exactly one black square!" + )} +

+
+
+ + {/* Legend */} +
+

Legend

+
+
+
+ White square +
+
+
+ Black square +
+
+ + Repaint 2×2 square +
+
+
+
+
+ + {/* Rules Dialog */} + + + + + + Chessboard Repaint Puzzle Rules + + +
+
+

Objective

+

Repaint the chessboard to achieve exactly one black square.

+
+ +
+

Initial Setup

+

You start with a standard 8×8 chessboard with alternating black and white squares.

+
+ +
+

How to Play

+
    +
  1. Repaint a row: Click any "Row X" button to flip all squares in that row (black becomes white, white becomes black).
  2. +
  3. Repaint a column: Click any "Col X" button to flip all squares in that column.
  4. +
  5. Repaint a 2×2 square: Click any "2×2" button to flip all four squares in that 2×2 region.
  6. +
  7. Goal: Achieve exactly one black square on the entire board.
  8. +
+
+ +
+

Rules

+
    +
  • You can repaint any row, any column, or any 2×2 square
  • +
  • Each click counts as one move
  • +
  • The puzzle is solved when exactly one square is black
  • +
  • Try to solve it in as few moves as possible!
  • +
+
+ +
+

Strategy Tips

+
    +
  • Think about parity - how do different operations affect the total number of black squares?
  • +
  • Consider the effect of each operation on the overall pattern
  • +
  • Remember that flipping a row or column affects 8 squares, while flipping a 2×2 affects 4 squares
  • +
  • Look for patterns in how the operations interact with each other
  • +
+
+
+
+
+ + {/* Victory Dialog */} + + + + + + Puzzle Solved! + + +
+
+ + + Congratulations! + +
+

+ You solved the puzzle in {moves} moves! +

+ {moves > 5 && ( +

+ Can you solve it in fewer moves? +

+ )} + +
+
+
+ + {/* Attribution */} +
+

+ This puzzle explores the fascinating concept of parity and how different operations affect the overall state of a system. +

+
+ + {/* Social Share Section */} + {showSocialShare && ( + + )} +
+ ); +}; + +export default ChessboardRepaintPuzzle; \ No newline at end of file diff --git a/src/components/InteractiveIndex.tsx b/src/components/InteractiveIndex.tsx index 5347090..fe43a7f 100644 --- a/src/components/InteractiveIndex.tsx +++ b/src/components/InteractiveIndex.tsx @@ -91,6 +91,15 @@ const allInteractives: Interactive[] = [ theme: 'Puzzles', dateAdded: '2024-12-19' }, + { + id: 'chessboard-repaint', + title: 'Chessboard Repaint Puzzle', + description: 'Repaint rows, columns, or 2×2 squares to achieve exactly one black square. A puzzle about parity and operations!', + tags: ['logic', 'parity', 'strategy', 'puzzle', 'chessboard', 'operations'], + path: '/puzzles/chessboard-repaint', + theme: 'Puzzles', + dateAdded: '2024-12-19' + }, { id: 'knights-puzzle', title: 'Knights Exchange Puzzle', diff --git a/src/pages/ChessboardRepaintPuzzlePage.tsx b/src/pages/ChessboardRepaintPuzzlePage.tsx new file mode 100644 index 0000000..6a338a4 --- /dev/null +++ b/src/pages/ChessboardRepaintPuzzlePage.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import ChessboardRepaintPuzzle from '@/components/ChessboardRepaintPuzzle'; + +const ChessboardRepaintPuzzlePage = () => { + return ( +
+ +
+ ); +}; + +export default ChessboardRepaintPuzzlePage; \ No newline at end of file diff --git a/src/pages/theme-pages/Puzzles.tsx b/src/pages/theme-pages/Puzzles.tsx index 2365b79..93513c9 100644 --- a/src/pages/theme-pages/Puzzles.tsx +++ b/src/pages/theme-pages/Puzzles.tsx @@ -16,6 +16,16 @@ const Puzzles = () => { duration: "10-30 min", participants: "1 player" }, + { + id: "chessboard-repaint", + title: "Chessboard Repaint Puzzle", + description: "Repaint rows, columns, or 2×2 squares to achieve exactly one black square. A puzzle about parity and operations!", + path: "/puzzles/chessboard-repaint", + tags: ["Logic", "Parity", "Operations"], + difficulty: "Advanced" as const, + duration: "15-45 min", + participants: "1 player" + }, { id: "knights-puzzle", title: "Knights Exchange Puzzle",