From 7451682dd51ab121b396495261375c04cab6960c Mon Sep 17 00:00:00 2001 From: Neeldhara Misra Date: Sun, 20 Jul 2025 10:29:09 +0530 Subject: [PATCH] Add Ternary Search Magic Trick interactive and fix layout issues - Create new Ternary Search Magic Trick interactive with ternary search logic - Add emoji buttons and 'not present' option for better UX - Implement 'How it Works' modal with embedded YouTube video - Fix layout margins when accessing interactives from theme pages - Add standalone and green screen pages for the new interactive - Update Data Structures theme to include the new interactive --- src/App.tsx | 4 + src/components/InteractiveGallery.tsx | 61 ++-- src/components/InteractiveIndex.tsx | 9 + src/components/TernarySearchTrick.tsx | 331 ++++++++++++++++++++ src/pages/TernarySearchTrickGreenScreen.tsx | 18 ++ src/pages/TernarySearchTrickPage.tsx | 13 + src/pages/theme-pages/DataStructures.tsx | 9 +- 7 files changed, 419 insertions(+), 26 deletions(-) create mode 100644 src/components/TernarySearchTrick.tsx create mode 100644 src/pages/TernarySearchTrickGreenScreen.tsx create mode 100644 src/pages/TernarySearchTrickPage.tsx diff --git a/src/App.tsx b/src/App.tsx index 236755a..6c66e9a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -22,6 +22,8 @@ import BinarySearchTrickPage from "./pages/BinarySearchTrickPage"; import BinarySearchTrickGreenScreen from "./pages/BinarySearchTrickGreenScreen"; import GameOfSimGreenScreen from "./pages/GameOfSimGreenScreen"; import GameOfSimPage from "./pages/GameOfSimPage"; +import TernarySearchTrickPage from "./pages/TernarySearchTrickPage"; +import TernarySearchTrickGreenScreen from "./pages/TernarySearchTrickGreenScreen"; 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"; @@ -70,6 +72,8 @@ const App = () => ( } /> } /> } /> + } /> + } /> {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} } /> diff --git a/src/components/InteractiveGallery.tsx b/src/components/InteractiveGallery.tsx index 89b6933..1bb52a5 100644 --- a/src/components/InteractiveGallery.tsx +++ b/src/components/InteractiveGallery.tsx @@ -3,38 +3,60 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Search } from 'lucide-react'; +import Layout from '@/components/Layout'; import BinarySearchTrick from '@/components/BinarySearchTrick'; +import TernarySearchTrick from '@/components/TernarySearchTrick'; + interface Interactive { id: string; title: string; description: string; tags: string[]; - component: React.ComponentType; + component: React.ComponentType<{ showSocialShare?: boolean }>; } -const interactives: Interactive[] = [{ - id: 'binary-search-trick', - title: 'Binary Search Magic Trick', - description: 'Perform an amazing magic trick that reveals how binary numbers work. Think of a number and watch the magic happen!', - tags: ['binary', 'magic', 'numbers', 'trick', 'data-structures'], - component: BinarySearchTrick -}]; + +const interactives: Interactive[] = [ + { + id: 'binary-search-trick', + title: 'Binary Search Magic Trick', + description: 'Perform an amazing magic trick that reveals how binary numbers work. Think of a number and watch the magic happen!', + tags: ['binary', 'magic', 'numbers', 'trick', 'data-structures'], + component: BinarySearchTrick + }, + { + id: 'ternary-search-trick', + title: 'Ternary Search Magic Trick', + description: 'Experience the magic of ternary search! Think of a number and watch as the cards reveal it through mathematical calculations.', + tags: ['ternary', 'magic', 'numbers', 'trick', 'data-structures', 'search'], + component: TernarySearchTrick + } +]; const InteractiveGallery = () => { const [searchTerm, setSearchTerm] = useState(''); const [selectedInteractive, setSelectedInteractive] = useState(null); const filteredInteractives = interactives.filter(interactive => interactive.title.toLowerCase().includes(searchTerm.toLowerCase()) || interactive.description.toLowerCase().includes(searchTerm.toLowerCase()) || interactive.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()))); if (selectedInteractive) { const InteractiveComponent = selectedInteractive.component; - return
-
- - + return ( +
+
+
+ +
+
-
- -
-
; +
+ ); } - return
+ return ( + +
+

Data Structures and Algorithms

Interactive explorations of elementary data structures and algorithms.

@@ -68,6 +90,9 @@ const InteractiveGallery = () => { {filteredInteractives.length === 0 &&

No interactives found matching your search.

} -
; +
+
+
+ ); }; export default InteractiveGallery; \ No newline at end of file diff --git a/src/components/InteractiveIndex.tsx b/src/components/InteractiveIndex.tsx index adb4608..cbe4515 100644 --- a/src/components/InteractiveIndex.tsx +++ b/src/components/InteractiveIndex.tsx @@ -45,6 +45,15 @@ const allInteractives: Interactive[] = [ path: '/game-of-sim', theme: 'Games', dateAdded: '2024-03-10' + }, + { + id: 'ternary-search-trick', + title: 'Ternary Search Magic Trick', + description: 'Experience the magic of ternary search! Think of a number and watch as the cards reveal it through mathematical calculations.', + tags: ['ternary', 'magic', 'numbers', 'trick', 'data-structures', 'search'], + path: '/ternary-search-trick', + theme: 'Data Structures', + dateAdded: '2024-07-20' } ]; diff --git a/src/components/TernarySearchTrick.tsx b/src/components/TernarySearchTrick.tsx new file mode 100644 index 0000000..91157c2 --- /dev/null +++ b/src/components/TernarySearchTrick.tsx @@ -0,0 +1,331 @@ +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, DialogTrigger } from '@/components/ui/dialog'; +import { RotateCcw, Wand2, Info } from 'lucide-react'; +import SocialShare from '@/components/SocialShare'; + +interface TernarySearchTrickProps { + showSocialShare?: boolean; +} + +const TernarySearchTrick: React.FC = ({ showSocialShare = true }) => { + const [step, setStep] = useState<'cards' | 'final-question' | 'result'>('cards'); + const [cardSelections, setCardSelections] = useState<{ + card1: 'left' | 'right' | 'missing' | null; + card2: 'left' | 'right' | 'missing' | null; + card3: 'left' | 'right' | 'missing' | null; + }>({ + card1: null, + card2: null, + card3: null + }); + const [isBiggerThan13, setIsBiggerThan13] = useState(null); + const [calculatedNumber, setCalculatedNumber] = useState(null); + + const cardData = { + card1: { + left: [5, 6, 7, 8, 9, 10, 11, 12, 13, 32, 33, 34, 35, 36, 37, 38, 39, 40], + right: [14, 15, 16, 17, 18, 19, 20, 21, 22] + }, + card2: { + left: [2, 3, 4, 11, 12, 13, 20, 21, 22, 29, 30, 31, 38, 39, 40], + right: [5, 6, 7, 14, 15, 16, 23, 24, 25, 32, 33, 34] + }, + card3: { + left: [1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40], + right: [2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38] + } + }; + + const resetTrick = () => { + setStep('cards'); + setCardSelections({ + card1: null, + card2: null, + card3: null + }); + setIsBiggerThan13(null); + setCalculatedNumber(null); + }; + + const handleCardSelection = (cardNumber: 1 | 2 | 3, side: 'left' | 'right' | 'missing') => { + setCardSelections(prev => ({ + ...prev, + [`card${cardNumber}`]: side + })); + }; + + const handleContinue = () => { + if (cardSelections.card1 && cardSelections.card2 && cardSelections.card3) { + setStep('final-question'); + } + }; + + const handleFinalAnswer = (biggerThan13: boolean) => { + setIsBiggerThan13(biggerThan13); + + // Calculate the number + let guess = 0; + + // Card 1: left adds 9, right subtracts 9, missing adds 0 + if (cardSelections.card1 === 'left') guess += 9; + else if (cardSelections.card1 === 'right') guess -= 9; + // missing adds 0, so no change + + // Card 2: left adds 3, right subtracts 3, missing adds 0 + if (cardSelections.card2 === 'left') guess += 3; + else if (cardSelections.card2 === 'right') guess -= 3; + // missing adds 0, so no change + + // Card 3: left adds 1, right subtracts 1, missing adds 0 + if (cardSelections.card3 === 'left') guess += 1; + else if (cardSelections.card3 === 'right') guess -= 1; + // missing adds 0, so no change + + // If bigger than 13, add 27 + if (biggerThan13) guess += 27; + + setCalculatedNumber(guess); + setStep('result'); + }; + + const renderCard = (cardNumber: 1 | 2 | 3) => { + const cardKey = `card${cardNumber}` as keyof typeof cardData; + const isSelected = cardSelections[cardKey as keyof typeof cardSelections]; + + return ( + + + Card {cardNumber} + + {isSelected ? `You selected: ${isSelected === 'missing' ? 'not present' : isSelected}` : 'Choose where your number appears'} + + + + {/* Emoji Buttons at Top */} +
+
+ +

Left

+
+ +
+ +

Not Present

+
+ +
+ +

Right

+
+
+ + {/* Number Boxes at Bottom */} +
+
+
+ {cardData[cardKey].left.map(num => ( + {num} + ))} +
+
+ +
+
+ {cardData[cardKey].right.map(num => ( + {num} + ))} +
+
+
+
+
+ ); + }; + + const renderFinalQuestion = () => ( + + + Final Question + + Is your number bigger than 13? + + + +
+ + +
+
+
+ ); + + const renderResult = () => ( + + + + + Magic Result! + + + +
+ {calculatedNumber} +
+

+ That's your number! 🎉 +

+
+
+ ); + + return ( +
+ + + Ternary Search Magic Trick + + Think of a number between 1 and 40, then follow the magic cards to reveal your number! + + + + {/* Step Indicator */} +
+
+ 1. Cards + + 2. Question + + 3. Result +
+
+ + {/* Content based on step */} + {step === 'cards' && ( +
+
+ {renderCard(1)} + {renderCard(2)} + {renderCard(3)} +
+ +
+ +
+
+ )} + + {step === 'final-question' && renderFinalQuestion()} + {step === 'result' && renderResult()} + + {/* Action Buttons */} +
+ + + + + + + + + + + How the Ternary Search Magic Trick Works + + +
+

+ This trick uses the balanced ternary representation of numbers, about which you can learn more in the following video: +

+
+