From 2580f760256f17da3338ae843b11fed0e67e8dec Mon Sep 17 00:00:00 2001 From: Neeldhara Misra Date: Tue, 22 Jul 2025 11:16:30 +0530 Subject: [PATCH] Add Zeckendorf Search Magic Trick - Fibonacci-based magic trick using Zeckendorf representation --- src/App.tsx | 2 + src/components/InteractiveIndex.tsx | 9 + src/components/ZeckendorfSearchTrick.tsx | 367 ++++++++++++++++++ src/pages/ZeckendorfSearchTrickPage.tsx | 13 + .../theme-pages/discrete-math/Foundations.tsx | 8 + 5 files changed, 399 insertions(+) create mode 100644 src/components/ZeckendorfSearchTrick.tsx create mode 100644 src/pages/ZeckendorfSearchTrickPage.tsx diff --git a/src/App.tsx b/src/App.tsx index 3209589..1bac3b1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -35,6 +35,7 @@ import GoldCoinGamePage from "./pages/GoldCoinGamePage"; import PlateSwapPuzzlePage from "./pages/PlateSwapPuzzlePage"; import ChessboardRepaintPuzzlePage from "./pages/ChessboardRepaintPuzzlePage"; import ZeckendorfGamePage from "./pages/ZeckendorfGamePage"; +import ZeckendorfSearchTrickPage from "./pages/ZeckendorfSearchTrickPage"; 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"; @@ -96,6 +97,7 @@ const App = () => ( } /> } /> } /> + } /> {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} } /> diff --git a/src/components/InteractiveIndex.tsx b/src/components/InteractiveIndex.tsx index 301410f..2433ce9 100644 --- a/src/components/InteractiveIndex.tsx +++ b/src/components/InteractiveIndex.tsx @@ -55,6 +55,15 @@ const allInteractives: Interactive[] = [ theme: 'Discrete Math', dateAdded: '2024-12-22' }, + { + id: 'zeckendorf-search-trick', + title: 'Zeckendorf Search Magic Trick', + description: 'Experience the magic of Zeckendorf representation! Think of a number and watch as the cards reveal it through Fibonacci numbers.', + tags: ['fibonacci', 'magic', 'numbers', 'trick', 'zeckendorf', 'representation'], + path: '/discrete-math/foundations/zeckendorf-search', + theme: 'Discrete Math', + dateAdded: '2024-12-22' + }, { id: 'binary-search-trick', title: 'Binary Search Magic Trick', diff --git a/src/components/ZeckendorfSearchTrick.tsx b/src/components/ZeckendorfSearchTrick.tsx new file mode 100644 index 0000000..53b89e9 --- /dev/null +++ b/src/components/ZeckendorfSearchTrick.tsx @@ -0,0 +1,367 @@ +import { useState, useEffect } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; +import { Switch } from '@/components/ui/switch'; +import { Label } from '@/components/ui/label'; +import { HelpCircle, RefreshCw, ChevronLeft, ChevronRight } from 'lucide-react'; +import SocialShare from '@/components/SocialShare'; + +interface ZeckendorfSearchTrickProps { + showSocialShare?: boolean; +} + +// First 8 Fibonacci numbers: 1, 1, 2, 3, 5, 8, 13, 21 +const FIBONACCI_NUMBERS = [21, 13, 8, 5, 3, 2, 1, 1]; + +// Function to get Zeckendorf representation of a number +const getZeckendorfRepresentation = (n: number): number[] => { + if (n === 0) return []; + + // Find the largest Fibonacci number less than or equal to n + let largestFib = 0; + let largestFibIndex = 0; + + for (let i = 0; i < FIBONACCI_NUMBERS.length; i++) { + if (FIBONACCI_NUMBERS[i] <= n) { + largestFib = FIBONACCI_NUMBERS[i]; + largestFibIndex = i; + break; + } + } + + if (largestFib === 0) return []; + + // Recursively get representation for the remainder + const remainder = n - largestFib; + const remainderRep = getZeckendorfRepresentation(remainder); + + // Check if adding this Fibonacci number would create consecutive numbers + if (remainderRep.length > 0 && remainderRep[0] === FIBONACCI_NUMBERS[largestFibIndex + 1]) { + // If it would create consecutive, try the next smaller Fibonacci number + return getZeckendorfRepresentation(n); + } + + return [largestFib, ...remainderRep]; +}; + +// Function to check if a number contains a specific Fibonacci number in its Zeckendorf representation +const containsFibonacciInZeckendorf = (number: number, fibonacci: number): boolean => { + const representation = getZeckendorfRepresentation(number); + return representation.includes(fibonacci); +}; + +const ZeckendorfSearchTrick = ({ showSocialShare = true }: ZeckendorfSearchTrickProps) => { + const [cardStates, setCardStates] = useState>({}); + const [result, setResult] = useState(null); + const [showResult, setShowResult] = useState(false); + const [isOneByOne, setIsOneByOne] = useState(false); + const [isRandomized, setIsRandomized] = useState(true); + const [currentCardIndex, setCurrentCardIndex] = useState(0); + const [cards, setCards] = useState>([]); + + // Function to shuffle array + const shuffleArray = (array: T[]): T[] => { + const shuffled = [...array]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + return shuffled; + }; + + // Generate cards with optional randomization + const generateCards = () => { + const baseCards = FIBONACCI_NUMBERS.map(fib => { + const numbers: number[] = []; + // Check numbers from 1 to 33 to see which ones contain this Fibonacci number + for (let i = 1; i <= 33; i++) { + if (containsFibonacciInZeckendorf(i, fib)) { + numbers.push(i); + } + } + + return { + id: fib, + title: `Card ${fib}`, + numbers: numbers + }; + }); + + // Apply randomization based on toggle state + return baseCards.map(card => ({ + ...card, + numbers: isRandomized ? shuffleArray(card.numbers) : card.numbers + })); + }; + + // Initialize cards on component mount + useEffect(() => { + setCards(generateCards()); + }, [isRandomized]); + + // Handle mode toggle + const handleModeToggle = (checked: boolean) => { + setIsOneByOne(checked); + setCurrentCardIndex(0); + setCardStates({}); + setShowResult(false); + }; + + // Handle randomization toggle + const handleRandomizationToggle = (checked: boolean) => { + setIsRandomized(checked); + setCardStates({}); + setShowResult(false); + setCurrentCardIndex(0); + }; + + // Navigation functions for one-by-one mode + const goToPreviousCard = () => { + setCurrentCardIndex(prev => Math.max(0, prev - 1)); + }; + + const goToNextCard = () => { + setCurrentCardIndex(prev => Math.min(cards.length - 1, prev + 1)); + }; + + const handleCardSelection = (cardId: number, state: 'present' | 'absent') => { + setCardStates(prev => ({ + ...prev, + [cardId]: state + })); + setShowResult(false); + }; + + const calculateResult = () => { + const sum = Object.entries(cardStates) + .filter(([_, state]) => state === 'present') + .reduce((acc, [cardId, _]) => acc + parseInt(cardId), 0); + setResult(sum); + setShowResult(true); + }; + + const reset = () => { + setCardStates({}); + setResult(null); + setShowResult(false); + setCurrentCardIndex(0); + setCards(generateCards()); // Regenerate with current randomization setting + }; + + // Check if at least one card has been selected (present or absent) + const hasAnySelection = Object.values(cardStates).some(state => state !== 'unselected'); + + // Check if all cards have been committed to in one-by-one mode + const allCardsCommitted = isOneByOne + ? cards.every(card => cardStates[card.id] && cardStates[card.id] !== 'unselected') + : hasAnySelection; + + // Get cards to display based on mode + const getDisplayCards = () => { + if (isOneByOne) { + return cards.length > 0 ? [cards[currentCardIndex]] : []; + } + return cards; + }; + + return ( +
+ + + + Zeckendorf Search Magic Trick + +
+ + + + + + + Zeckendorf Representation Magic Trick Explained + +
+

How to perform the trick:

+
    +
  1. Ask someone to pick a secret number between 1 and 33
  2. +
  3. Show them the 8 cards below and ask them to tell you which cards contain their number
  4. +
  5. Add up the first numbers (21, 13, 8, 5, 3, 2, 1, 1) from the cards they selected
  6. +
  7. The sum will be their secret number!
  8. +
+ +

Why does it work?

+

+ This trick is based on Zeckendorf representation. Every positive integer can be uniquely represented as a sum of Fibonacci numbers, where no two consecutive Fibonacci numbers are used. +

+

+ Each card contains all numbers that have a specific Fibonacci number in their Zeckendorf representation: +

+
    +
  • Card 21: Numbers that have 21 in their Zeckendorf representation
  • +
  • Card 13: Numbers that have 13 in their Zeckendorf representation
  • +
  • Card 8: Numbers that have 8 in their Zeckendorf representation
  • +
  • Card 5: Numbers that have 5 in their Zeckendorf representation
  • +
  • Card 3: Numbers that have 3 in their Zeckendorf representation
  • +
  • Card 2: Numbers that have 2 in their Zeckendorf representation
  • +
  • Card 1: Numbers that have 1 in their Zeckendorf representation
  • +
+ +
+

Example:

+

The number 19 in Zeckendorf representation is 13 + 5 + 1 = 19

+

So 19 appears on cards 13, 5, and 1, but not on cards 21, 8, 3, or 2.

+
+ +

+ When you add up the first numbers from the selected cards, you're reconstructing the Zeckendorf representation of their secret number! +

+
+
+
+
+
+
+
+ +
+

+ Think of a number between 1 and 33, then tell me which cards contain your number! +

+ + {/* Controls */} +
+
+ + +
+ +
+ + +
+ + +
+ + {/* Navigation for one-by-one mode */} + {isOneByOne && cards.length > 0 && ( +
+ + + Card {currentCardIndex + 1} of {cards.length} + + +
+ )} +
+
+
+ + {/* Cards */} +
+ {getDisplayCards().map(card => ( + + + {card.title} + + +
+ {card.numbers.map(num => ( +
+ {num} +
+ ))} +
+ +
+ + +
+
+
+ ))} +
+ + {/* Reveal button */} +
+ +
+ + {/* Result */} + {showResult && result !== null && ( + + +

+ Your number is: {result} +

+

+ Magic! 🎩✨ +

+
+
+ )} + + {showSocialShare && ( + + )} +
+ ); +}; + +export default ZeckendorfSearchTrick; \ No newline at end of file diff --git a/src/pages/ZeckendorfSearchTrickPage.tsx b/src/pages/ZeckendorfSearchTrickPage.tsx new file mode 100644 index 0000000..59e977f --- /dev/null +++ b/src/pages/ZeckendorfSearchTrickPage.tsx @@ -0,0 +1,13 @@ +import ZeckendorfSearchTrick from '@/components/ZeckendorfSearchTrick'; + +const ZeckendorfSearchTrickPage = () => { + return ( +
+
+ +
+
+ ); +}; + +export default ZeckendorfSearchTrickPage; \ No newline at end of file diff --git a/src/pages/theme-pages/discrete-math/Foundations.tsx b/src/pages/theme-pages/discrete-math/Foundations.tsx index 5f40fe1..8fc0d0e 100644 --- a/src/pages/theme-pages/discrete-math/Foundations.tsx +++ b/src/pages/theme-pages/discrete-math/Foundations.tsx @@ -7,6 +7,7 @@ import BinaryNumberGame from "@/components/BinaryNumberGame"; import TernaryNumberGame from "@/components/TernaryNumberGame"; import BalancedTernaryGame from "@/components/BalancedTernaryGame"; import ZeckendorfGame from "@/components/ZeckendorfGame"; +import ZeckendorfSearchTrick from "@/components/ZeckendorfSearchTrick"; interface Interactive { id: string; title: string; @@ -43,6 +44,13 @@ const interactives: Interactive[] = [{ tags: ["fibonacci", "numbers", "representation", "zeckendorf", "combinatorics"], component: ZeckendorfGame, greenScreenPath: "/discrete-math/foundations/zeckendorf" +}, { + id: "zeckendorf-search-trick", + title: "Zeckendorf Search Magic Trick", + description: "Experience the magic of Zeckendorf representation! Think of a number and watch as the cards reveal it through Fibonacci numbers.", + tags: ["fibonacci", "magic", "numbers", "trick", "zeckendorf", "representation"], + component: ZeckendorfSearchTrick, + greenScreenPath: "/discrete-math/foundations/zeckendorf-search" }]; const Foundations = () => { const [searchTerm, setSearchTerm] = useState("");