From 435f7a3d43ea42baaaebae492f2a3ba6b1ff1720 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sat, 19 Jul 2025 13:16:46 +0000 Subject: [PATCH] Add binary interactive to School Connect Implement a binary representation interactive game with cards, a timer, and score tracking on the School Connect page. --- src/components/BinaryNumberGame.tsx | 168 ++++++++++++++++++++++++ src/components/InteractiveGallery.tsx | 112 ++++++++++++++++ src/pages/theme-pages/SchoolConnect.tsx | 9 +- 3 files changed, 284 insertions(+), 5 deletions(-) create mode 100644 src/components/BinaryNumberGame.tsx create mode 100644 src/components/InteractiveGallery.tsx diff --git a/src/components/BinaryNumberGame.tsx b/src/components/BinaryNumberGame.tsx new file mode 100644 index 0000000..dba01f9 --- /dev/null +++ b/src/components/BinaryNumberGame.tsx @@ -0,0 +1,168 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; + +const POWERS_OF_TWO = [128, 64, 32, 16, 8, 4, 2, 1]; + +const BinaryNumberGame = () => { + const [targetNumber, setTargetNumber] = useState(0); + const [flippedCards, setFlippedCards] = useState(new Array(8).fill(false)); + const [currentSum, setCurrentSum] = useState(0); + const [timer, setTimer] = useState(0); + const [isGameActive, setIsGameActive] = useState(false); + const [isGameWon, setIsGameWon] = useState(false); + const [highScore, setHighScore] = useState(null); + + // Load high score from localStorage on component mount + useEffect(() => { + const savedHighScore = localStorage.getItem('binary-game-high-score'); + if (savedHighScore) { + setHighScore(parseInt(savedHighScore)); + } + }, []); + + // Start new game + const startNewGame = useCallback(() => { + const newTarget = Math.floor(Math.random() * 257); // 0-256 + setTargetNumber(newTarget); + setFlippedCards(new Array(8).fill(false)); + setCurrentSum(0); + setTimer(0); + setIsGameActive(true); + setIsGameWon(false); + }, []); + + // Initialize game on mount + useEffect(() => { + startNewGame(); + }, [startNewGame]); + + // Timer effect + useEffect(() => { + let interval: NodeJS.Timeout; + if (isGameActive && !isGameWon) { + interval = setInterval(() => { + setTimer(prev => prev + 0.1); + }, 100); + } + return () => clearInterval(interval); + }, [isGameActive, isGameWon]); + + // Check for win condition + useEffect(() => { + if (currentSum === targetNumber && isGameActive) { + setIsGameActive(false); + setIsGameWon(true); + + // Update high score + const currentTime = Math.round(timer * 10) / 10; + if (highScore === null || currentTime < highScore) { + setHighScore(currentTime); + localStorage.setItem('binary-game-high-score', currentTime.toString()); + } + } + }, [currentSum, targetNumber, isGameActive, timer, highScore]); + + // Calculate current sum + useEffect(() => { + const sum = flippedCards.reduce((acc, isFlipped, index) => { + return isFlipped ? acc + POWERS_OF_TWO[index] : acc; + }, 0); + setCurrentSum(sum); + }, [flippedCards]); + + const toggleCard = (index: number) => { + if (!isGameActive || isGameWon) return; + + setFlippedCards(prev => { + const newFlipped = [...prev]; + newFlipped[index] = !newFlipped[index]; + return newFlipped; + }); + }; + + return ( +
+
+

Binary Number Representation

+

Click the cards to represent the target number as a sum of powers of two!

+ {highScore !== null && ( + + Best Time: {highScore}s + + )} +
+ + {/* Cards representing powers of two */} +
+ {POWERS_OF_TWO.map((power, index) => ( +
+ toggleCard(index)} + > + + {flippedCards[index] ? ( + {power} + ) : ( +
+
+
+ )} +
+
+ {power} +
+ ))} +
+ + {/* Sum display boxes */} +
+ + +

Current Sum

+
{currentSum}
+
+
+ + +

Target Sum

+
{targetNumber}
+
+
+
+ + {/* Timer and controls */} +
+
+ Time: {Math.round(timer * 10) / 10}s +
+ + {isGameWon && ( +
+
Congratulations! 🎉
+
+ Your score: {Math.round(timer * 10) / 10} seconds +
+ {highScore === Math.round(timer * 10) / 10 && ( + + New High Score! 🏆 + + )} +
+ )} + + +
+
+ ); +}; + +export default BinaryNumberGame; \ No newline at end of file diff --git a/src/components/InteractiveGallery.tsx b/src/components/InteractiveGallery.tsx new file mode 100644 index 0000000..ccd8787 --- /dev/null +++ b/src/components/InteractiveGallery.tsx @@ -0,0 +1,112 @@ +import React, { useState } from 'react'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Input } from '@/components/ui/input'; +import { Badge } from '@/components/ui/badge'; +import { Search } from 'lucide-react'; +import BinaryNumberGame from '@/components/BinaryNumberGame'; + +interface Interactive { + id: string; + title: string; + description: string; + tags: string[]; + component: React.ComponentType; +} + +const interactives: Interactive[] = [ + { + id: 'binary-number-game', + title: 'Binary Number Representation', + description: 'Learn binary representation by expressing numbers as sums of powers of two. Toggle cards to match the target sum!', + tags: ['binary', 'math', 'numbers', 'representation', 'powers-of-two'], + component: BinaryNumberGame, + }, +]; + +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 ( +
+
+ +

{selectedInteractive.title}

+
+
+ +
+
+ ); + } + + return ( +
+
+

School Connect Interactives

+

+ Interactive tools and resources designed to connect theoretical concepts with practical applications in educational settings. +

+
+ + {/* Search bar */} +
+ + setSearchTerm(e.target.value)} + className="pl-10" + /> +
+ + {/* Gallery */} +
+ {filteredInteractives.map((interactive) => ( + setSelectedInteractive(interactive)} + > + + {interactive.title} + + {interactive.description} + + + +
+ {interactive.tags.map((tag) => ( + + {tag} + + ))} +
+
+
+ ))} +
+ + {filteredInteractives.length === 0 && ( +
+

No interactives found matching your search.

+
+ )} +
+ ); +}; + +export default InteractiveGallery; \ No newline at end of file diff --git a/src/pages/theme-pages/SchoolConnect.tsx b/src/pages/theme-pages/SchoolConnect.tsx index 54f3af9..cf52b88 100644 --- a/src/pages/theme-pages/SchoolConnect.tsx +++ b/src/pages/theme-pages/SchoolConnect.tsx @@ -1,11 +1,10 @@ -import ComingSoon from "@/components/ComingSoon"; +import InteractiveGallery from "@/components/InteractiveGallery"; const SchoolConnect = () => { return ( - +
+ +
); };