diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index e9a45b4..0000000
--- a/.dockerignore
+++ /dev/null
@@ -1,8 +0,0 @@
-.git
-.github
-.forgejo
-dist
-node_modules
-npm-debug.log*
-Dockerfile*
-README.md
diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml
deleted file mode 100644
index 40a10b9..0000000
--- a/.forgejo/workflows/ci.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-name: CI
-
-on:
- push:
- branches:
- - main
- pull_request:
- branches:
- - main
- workflow_dispatch:
-
-permissions:
- contents: read
-
-concurrency:
- group: ci-${{ forgejo.ref }}
- cancel-in-progress: true
-
-jobs:
- verify:
- runs-on: ubuntu-latest
- timeout-minutes: 15
-
- steps:
- - name: Check out repository
- uses: actions/checkout@v4
-
- - name: Set up Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 22
- cache: npm
-
- - name: Install dependencies
- run: npm ci
-
- - name: Lint
- run: npm run lint
-
- - name: Build
- run: npm run build
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index ff67f4c..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,19 +0,0 @@
-FROM node:22-alpine AS build
-
-WORKDIR /app
-
-COPY package.json package-lock.json ./
-RUN npm ci
-
-COPY . .
-RUN npm run build
-
-FROM nginx:1.27-alpine AS runtime
-
-COPY nginx.conf /etc/nginx/conf.d/default.conf
-COPY --from=build /app/dist /usr/share/nginx/html
-
-EXPOSE 80
-
-HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
- CMD wget -q --spider http://127.0.0.1/ || exit 1
diff --git a/README.md b/README.md
index 672b6b0..e0eb78a 100644
--- a/README.md
+++ b/README.md
@@ -64,12 +64,6 @@ This project is built with:
Simply open [Lovable](https://lovable.dev/projects/5ee9d1e7-af25-47e9-ab4c-bdbca4554e32) and click on Share -> Publish.
-### Forgejo and Dokploy
-
-Pushes and pull requests targeting `main` are verified by the Forgejo Actions workflow in `.forgejo/workflows/ci.yml`. The production container is built from `Dockerfile` and serves the Vite output with NGINX, including React Router fallback handling.
-
-Dokploy is connected to the `Websites/interactives` Forgejo repository with autodeploy enabled. A successful push to `main` triggers a fresh Dockerfile build and replaces the running service only after the image has built successfully.
-
## Can I connect a custom domain to my Lovable project?
Yes, you can!
diff --git a/nginx.conf b/nginx.conf
deleted file mode 100644
index aca9d71..0000000
--- a/nginx.conf
+++ /dev/null
@@ -1,22 +0,0 @@
-server {
- listen 80;
- listen [::]:80;
- server_name _;
-
- root /usr/share/nginx/html;
- index index.html;
-
- location / {
- try_files $uri $uri/ /index.html;
- }
-
- location = /index.html {
- add_header Cache-Control "no-cache";
- }
-
- location ~* \.(?:css|js|mjs|jpg|jpeg|png|gif|ico|svg|webp|woff|woff2)$ {
- expires 1y;
- add_header Cache-Control "public, immutable";
- try_files $uri =404;
- }
-}
diff --git a/src/App.tsx b/src/App.tsx
index f94e757..55ab15d 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -3,7 +3,6 @@ import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import ErrorBoundary from "@/components/ErrorBoundary";
-import CommandSearch from "@/components/CommandSearch";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Index from "./pages/Index";
import InteractiveIndex from "./components/InteractiveIndex";
@@ -68,7 +67,6 @@ import BagchalGamePage from './pages/BagchalGamePage';
import AscDescGridPuzzlePage from './pages/AscDescGridPuzzlePage';
import EternalDominationGamePage from './pages/EternalDominationGamePage';
import LadybugClockPuzzlePage from './pages/LadybugClockPuzzlePage';
-import ErdosDiscrepancyPuzzlePage from './pages/ErdosDiscrepancyPuzzlePage';
const queryClient = new QueryClient();
@@ -79,7 +77,6 @@ const App = () => (
-
} />
} />
@@ -147,7 +144,6 @@ const App = () => (
} />
} />
} />
- } />
} />
} />
} />
diff --git a/src/components/AssistedNimGame.tsx b/src/components/AssistedNimGame.tsx
index d04028c..ee3fa28 100644
--- a/src/components/AssistedNimGame.tsx
+++ b/src/components/AssistedNimGame.tsx
@@ -66,11 +66,7 @@ const getOddPowers = (heaps: number[]): Set => {
return oddPowers;
};
-interface AssistedNimGameProps {
- showSocialShare?: boolean;
-}
-
-const AssistedNimGame: React.FC = ({ showSocialShare = true }) => {
+const AssistedNimGame: React.FC = () => {
const [mode, setMode] = useState(null);
const [showSetup, setShowSetup] = useState(false);
const [userHeapCount, setUserHeapCount] = useState(3);
@@ -388,14 +384,14 @@ const AssistedNimGame: React.FC = ({ showSocialShare = tru
- {showSocialShare && (
-
- )}
+ {/* Social Share */}
+
);
};
-export default AssistedNimGame;
\ No newline at end of file
+export default AssistedNimGame;
\ No newline at end of file
diff --git a/src/components/CommandSearch.tsx b/src/components/CommandSearch.tsx
deleted file mode 100644
index 1b5a6fb..0000000
--- a/src/components/CommandSearch.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { useEffect, useState } from "react";
-import { useNavigate } from "react-router-dom";
-import { allInteractives } from "@/components/InteractiveIndex";
-import {
- CommandDialog,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
-} from "@/components/ui/command";
-import { Badge } from "@/components/ui/badge";
-
-const CommandSearch = () => {
- const [open, setOpen] = useState(false);
- const navigate = useNavigate();
-
- useEffect(() => {
- const down = (e: KeyboardEvent) => {
- if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
- e.preventDefault();
- setOpen((prev) => !prev);
- }
- };
- document.addEventListener("keydown", down);
- return () => document.removeEventListener("keydown", down);
- }, []);
-
- const handleSelect = (path: string) => {
- setOpen(false);
- navigate(path);
- };
-
- return (
-
-
-
- No interactives found.
-
- {allInteractives.map((interactive) => (
- handleSelect(interactive.path)}
- >
-
-
- {interactive.title}
-
- {interactive.theme}
-
-
-
- {interactive.description}
-
-
-
- ))}
-
-
-
- );
-};
-
-export default CommandSearch;
diff --git a/src/components/ErdosDiscrepancyPuzzle.tsx b/src/components/ErdosDiscrepancyPuzzle.tsx
deleted file mode 100644
index 8046d40..0000000
--- a/src/components/ErdosDiscrepancyPuzzle.tsx
+++ /dev/null
@@ -1,902 +0,0 @@
-import React, { useState, useEffect, useCallback, useMemo } from 'react';
-import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
-import { Button } from '@/components/ui/button';
-import { Badge } from '@/components/ui/badge';
-import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
-import { Slider } from '@/components/ui/slider';
-import {
- Play, RotateCcw, Info, ChevronDown, ChevronUp,
- ArrowLeft, ArrowRight, Skull, AlertTriangle,
- BookOpen, Zap, Eye, Grid3X3, TrendingUp, Pause,
- Volume2, VolumeX, Target, Trophy
-} from 'lucide-react';
-import SocialShare from '@/components/SocialShare';
-import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
-
-interface ErdosDiscrepancyPuzzleProps {
- showSocialShare?: boolean;
-}
-
-type GamePhase = 'intro' | 'writing' | 'captor-reveal' | 'walking' | 'fallen' | 'survived';
-type Direction = 1 | -1 | null;
-
-interface DiscrepancyResult {
- d: number;
- k: number;
- sum: number;
- positions: number[];
-}
-
-const ErdosDiscrepancyPuzzle: React.FC = ({ showSocialShare = false }) => {
- // Core game state
- const [sequence, setSequence] = useState([]);
- const [discrepancyBound, setDiscrepancyBound] = useState(1);
- const [gamePhase, setGamePhase] = useState('intro');
- const [showRules, setShowRules] = useState(false);
-
- // Walk visualization state
- const [selectedD, setSelectedD] = useState(1);
- const [walkPosition, setWalkPosition] = useState(0);
- const [walkStep, setWalkStep] = useState(0);
- const [isWalking, setIsWalking] = useState(false);
- const [walkSpeed, setWalkSpeed] = useState(500);
-
- // Prisoner's Walk story mode
- const [captorChosenD, setCaptorChosenD] = useState(null);
- const [prisonerPosition, setPrisonerPosition] = useState(0);
- const [prisonerStep, setPrisonerStep] = useState(0);
-
- // Heatmap state
- const [hoveredCell, setHoveredCell] = useState<{d: number, k: number} | null>(null);
- const [highlightedSubsequence, setHighlightedSubsequence] = useState([]);
-
- // Best score tracking
- const [bestLength, setBestLength] = useState(null);
-
- // Load best score from localStorage
- useEffect(() => {
- const saved = localStorage.getItem(`erdos-best-length-c${discrepancyBound}`);
- if (saved) {
- setBestLength(parseInt(saved));
- }
- }, [discrepancyBound]);
-
- // Calculate all discrepancies for current sequence
- const allDiscrepancies = useMemo((): DiscrepancyResult[] => {
- if (sequence.length === 0) return [];
-
- const results: DiscrepancyResult[] = [];
- const n = sequence.length;
-
- for (let d = 1; d <= n; d++) {
- let sum = 0;
- const positions: number[] = [];
-
- for (let k = 1; k * d <= n; k++) {
- const pos = k * d - 1; // 0-indexed
- if (sequence[pos] !== null) {
- sum += sequence[pos]!;
- positions.push(k * d);
- results.push({
- d,
- k,
- sum,
- positions: [...positions]
- });
- }
- }
- }
-
- return results;
- }, [sequence]);
-
- // Find maximum discrepancy
- const maxDiscrepancy = useMemo(() => {
- if (allDiscrepancies.length === 0) return { value: 0, result: null };
-
- let maxAbs = 0;
- let maxResult: DiscrepancyResult | null = null;
-
- for (const result of allDiscrepancies) {
- if (Math.abs(result.sum) > maxAbs) {
- maxAbs = Math.abs(result.sum);
- maxResult = result;
- }
- }
-
- return { value: maxAbs, result: maxResult };
- }, [allDiscrepancies]);
-
- // Check if current sequence violates bound
- const hasViolation = maxDiscrepancy.value > discrepancyBound;
-
- // Find worst step size for captor
- const findWorstD = useCallback((): number => {
- let worstD = 1;
- let worstDiscrepancy = 0;
-
- for (const result of allDiscrepancies) {
- if (Math.abs(result.sum) > worstDiscrepancy) {
- worstDiscrepancy = Math.abs(result.sum);
- worstD = result.d;
- }
- }
-
- return worstD;
- }, [allDiscrepancies]);
-
- // Get discrepancy for specific d,k
- const getDiscrepancy = (d: number, k: number): number | null => {
- const result = allDiscrepancies.find(r => r.d === d && r.k === k);
- return result ? result.sum : null;
- };
-
- // Toggle sequence value
- const togglePosition = (index: number) => {
- if (gamePhase !== 'writing') return;
-
- setSequence(prev => {
- const newSeq = [...prev];
- if (newSeq[index] === null) {
- newSeq[index] = 1;
- } else if (newSeq[index] === 1) {
- newSeq[index] = -1;
- } else {
- newSeq[index] = 1;
- }
- return newSeq;
- });
- };
-
- // Add new position to sequence
- const addPosition = () => {
- if (gamePhase !== 'writing') return;
- setSequence(prev => [...prev, null]);
- };
-
- // Start the game
- const startGame = () => {
- setSequence([null, null, null, null, null]);
- setGamePhase('writing');
- setWalkPosition(0);
- setWalkStep(0);
- setPrisonerPosition(0);
- setPrisonerStep(0);
- setCaptorChosenD(null);
- setHighlightedSubsequence([]);
- };
-
- // Reset the game
- const resetGame = () => {
- setSequence([]);
- setGamePhase('intro');
- setWalkPosition(0);
- setWalkStep(0);
- setIsWalking(false);
- setPrisonerPosition(0);
- setPrisonerStep(0);
- setCaptorChosenD(null);
- setHighlightedSubsequence([]);
- };
-
- // Handle captor reveal (Prisoner's Walk mode)
- const revealCaptor = () => {
- const worstD = findWorstD();
- setCaptorChosenD(worstD);
- setGamePhase('captor-reveal');
- setPrisonerPosition(0);
- setPrisonerStep(0);
- };
-
- // Start prisoner walking
- const startPrisonerWalk = () => {
- if (captorChosenD === null) return;
- setGamePhase('walking');
- setPrisonerPosition(0);
- setPrisonerStep(0);
- };
-
- // Prisoner walk animation
- useEffect(() => {
- if (gamePhase !== 'walking' || captorChosenD === null) return;
-
- const interval = setInterval(() => {
- setPrisonerStep(prev => {
- const nextStep = prev + 1;
- const posIndex = nextStep * captorChosenD - 1;
-
- if (posIndex >= sequence.length || sequence[posIndex] === null) {
- // Sequence ended without falling
- setGamePhase('survived');
- clearInterval(interval);
-
- // Save best score
- const validLength = sequence.filter(s => s !== null).length;
- if (!bestLength || validLength > bestLength) {
- setBestLength(validLength);
- localStorage.setItem(`erdos-best-length-c${discrepancyBound}`, validLength.toString());
- }
-
- return prev;
- }
-
- const newPosition = prisonerPosition + sequence[posIndex]!;
- setPrisonerPosition(newPosition);
-
- if (Math.abs(newPosition) > discrepancyBound) {
- setGamePhase('fallen');
- clearInterval(interval);
- return nextStep;
- }
-
- return nextStep;
- });
- }, walkSpeed);
-
- return () => clearInterval(interval);
- }, [gamePhase, captorChosenD, sequence, prisonerPosition, discrepancyBound, walkSpeed, bestLength]);
-
- // Manual walk control
- const walkSubsequence = () => {
- if (isWalking) {
- setIsWalking(false);
- return;
- }
-
- setWalkPosition(0);
- setWalkStep(0);
- setIsWalking(true);
- };
-
- // Walk animation effect
- useEffect(() => {
- if (!isWalking) return;
-
- const interval = setInterval(() => {
- setWalkStep(prev => {
- const nextStep = prev + 1;
- const posIndex = nextStep * selectedD - 1;
-
- if (posIndex >= sequence.length || sequence[posIndex] === null) {
- setIsWalking(false);
- return prev;
- }
-
- setWalkPosition(current => current + sequence[posIndex]!);
- return nextStep;
- });
- }, walkSpeed);
-
- return () => clearInterval(interval);
- }, [isWalking, selectedD, sequence, walkSpeed]);
-
- // Get color for heatmap cell
- const getHeatmapColor = (discrepancy: number | null): string => {
- if (discrepancy === null) return 'bg-muted/30';
-
- const absVal = Math.abs(discrepancy);
- if (absVal > discrepancyBound) {
- return 'bg-destructive/80 text-destructive-foreground';
- }
- if (absVal === discrepancyBound) {
- return 'bg-amber-500/70 text-white';
- }
- if (absVal === 0) {
- return 'bg-emerald-500/50 text-emerald-900 dark:text-emerald-100';
- }
-
- const intensity = absVal / discrepancyBound;
- if (intensity < 0.5) {
- return 'bg-emerald-400/40 text-emerald-900 dark:text-emerald-100';
- }
- return 'bg-amber-400/50 text-amber-900 dark:text-amber-100';
- };
-
- // Max valid k for given d
- const maxK = (d: number) => Math.floor(sequence.length / d);
-
- // Highlighted positions for selected d
- const selectedPositions = useMemo(() => {
- const positions: number[] = [];
- for (let k = 1; k * selectedD <= sequence.length; k++) {
- positions.push(k * selectedD);
- }
- return positions;
- }, [selectedD, sequence.length]);
-
- // Get fill status of sequence
- const filledCount = sequence.filter(s => s !== null).length;
- const allFilled = filledCount === sequence.length && sequence.length > 0;
-
- // Known optimal sequences
- const optimalSequences: Record = {
- 1: [1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1], // One of the optimal 11-length sequences for C=1
- };
-
- const loadOptimalSequence = () => {
- const optimal = optimalSequences[discrepancyBound];
- if (optimal) {
- setSequence([...optimal]);
- setGamePhase('writing');
- }
- };
-
- return (
-
- {/* Header */}
-
-
-
- The Erdős Discrepancy Problem
-
-
- Can you write instructions to survive the tunnel forever?
-
-
-
- setShowRules(!showRules)}
- >
- {showRules ? : }
- {showRules ? 'Hide' : 'Rules'}
-
-
-
-
- {/* Rules Panel */}
- {showRules && (
-
-
-
-
-
- The Prisoner's Dilemma
-
-
- You're trapped in a tunnel with a deadly cliff {discrepancyBound} step{discrepancyBound > 1 ? 's' : ''} to your LEFT and
- a pit of vipers {discrepancyBound} step{discrepancyBound > 1 ? 's' : ''} to your RIGHT .
-
-
- Write a list of instructions: each is either LEFT or RIGHT .
-
-
- The evil captor then picks a step size d and you follow every d-th instruction.
-
-
-
-
- The Impossible Task
-
-
- C = 1: Maximum possible sequence length is 11
-
-
- C = 2: Maximum possible sequence length is 1,160
-
-
- Terence Tao proved in 2015: No infinite sequence can survive!
-
-
-
-
-
- )}
-
- {/* Game Phase: Intro */}
- {gamePhase === 'intro' && (
-
-
-
-
-
The Prisoner's Walk
-
- You wake up in a dark tunnel. There's a cliff to your left and vipers to your right.
- Your captor demands you write a sequence of LEFT/RIGHT instructions...
- but they get to choose which instructions you follow.
-
-
-
-
- Discrepancy Bound (C): {discrepancyBound}
-
-
- 1
- setDiscrepancyBound(v[0])}
- min={1}
- max={3}
- step={1}
- className="flex-1"
- />
- 3
-
-
- C=1: Max 11 steps | C=2: Max 1,160 steps | C=3: Unknown (very large)
-
-
-
-
-
-
- Start Writing
-
- {discrepancyBound === 1 && (
-
-
- Load Optimal (11)
-
- )}
-
-
- {bestLength && (
-
- Your best: {bestLength} steps with C={discrepancyBound}
-
- )}
-
-
-
- )}
-
- {/* Main Game UI */}
- {gamePhase !== 'intro' && (
-
- {/* Status Bar */}
-
-
- Length: {sequence.length}
-
-
- Filled: {filledCount}/{sequence.length}
-
-
- Max Discrepancy: {maxDiscrepancy.value}
- {maxDiscrepancy.result && ` (d=${maxDiscrepancy.result.d}, k=${maxDiscrepancy.result.k})`}
-
-
- Bound: C = {discrepancyBound}
-
- {bestLength && (
-
-
- Best: {bestLength}
-
- )}
-
-
- {/* Violation Alert */}
- {hasViolation && gamePhase === 'writing' && (
-
-
- Bound Exceeded!
-
- The subsequence with d={maxDiscrepancy.result?.d}, k={maxDiscrepancy.result?.k} has
- discrepancy {maxDiscrepancy.value}, which exceeds your bound of {discrepancyBound}.
-
-
- )}
-
-
-
-
-
- Sequence
-
-
-
- Walk
-
-
-
- Heatmap
-
-
-
- {/* Sequence Builder Tab */}
-
-
-
-
- Write Your Instructions
- {gamePhase === 'writing' && (
- Click to toggle
- )}
-
-
-
- {/* Sequence Grid */}
-
-
- {sequence.map((val, idx) => {
- const pos = idx + 1;
- const isHighlighted = highlightedSubsequence.includes(pos) || selectedPositions.includes(pos);
- const isViolationPart = maxDiscrepancy.result?.positions.includes(pos) && hasViolation;
-
- return (
-
togglePosition(idx)}
- disabled={gamePhase !== 'writing'}
- className={`
- relative w-10 h-14 rounded-lg border-2 flex flex-col items-center justify-center
- transition-all duration-200
- ${val === 1 ? 'bg-blue-500/20 border-blue-500 text-blue-600 dark:text-blue-400' : ''}
- ${val === -1 ? 'bg-rose-500/20 border-rose-500 text-rose-600 dark:text-rose-400' : ''}
- ${val === null ? 'bg-muted/50 border-muted-foreground/30 text-muted-foreground' : ''}
- ${isHighlighted ? 'ring-2 ring-amber-400 ring-offset-1' : ''}
- ${isViolationPart ? 'ring-2 ring-destructive ring-offset-1' : ''}
- ${gamePhase === 'writing' ? 'hover:scale-105 cursor-pointer' : 'cursor-default'}
- `}
- >
- {pos}
- {val === 1 && }
- {val === -1 && }
- {val === null && ? }
-
- );
- })}
-
- {gamePhase === 'writing' && (
-
- +
-
- )}
-
-
-
- {/* Controls */}
-
- {gamePhase === 'writing' && (
- <>
-
-
- Face the Captor
-
-
-
- Reset
-
- >
- )}
-
-
-
-
-
- {/* Walk Visualization Tab */}
-
-
-
- Walk Visualization
-
-
- {/* Step size selector */}
-
-
-
Step size (d):
-
- {Array.from({ length: Math.min(sequence.length, 8) }, (_, i) => i + 1).map(d => (
- {
- setSelectedD(d);
- setWalkPosition(0);
- setWalkStep(0);
- setIsWalking(false);
- }}
- className="w-8 h-8 p-0"
- >
- {d}
-
- ))}
-
-
-
-
- Speed:
- setWalkSpeed(1000 - v[0])}
- min={0}
- max={900}
- step={100}
- className="w-24"
- />
-
-
-
- {/* Number line visualization */}
-
- {/* Danger zones */}
-
- CLIFF
-
-
- VIPERS
-
-
- {/* Safe zone markers */}
-
- {Array.from({ length: discrepancyBound * 2 + 1 }, (_, i) => i - discrepancyBound).map(pos => (
-
- {pos}
-
- ))}
-
-
- {/* Walker position */}
-
-
-
- {/* Walk info */}
-
- Position: {walkPosition}
- Steps taken: {walkStep}
- Following: positions {selectedPositions.slice(0, 5).join(', ')}{selectedPositions.length > 5 ? '...' : ''}
-
-
- {/* Walk controls */}
-
-
- {isWalking ? : }
- {isWalking ? 'Pause' : 'Walk d=' + selectedD}
-
-
{
- setWalkPosition(0);
- setWalkStep(0);
- setIsWalking(false);
- }}
- >
-
-
-
-
-
-
-
- {/* Heatmap Tab */}
-
- {/* This is actually handled in the walk tab above */}
-
-
-
-
-
- Discrepancy Heatmap
-
-
-
- Each cell shows the sum of values at positions d, 2d, 3d, ..., kd.
- Red cells exceed the bound.
-
-
-
-
-
-
- d \ k
- {Array.from({ length: Math.min(12, sequence.length) }, (_, i) => i + 1).map(k => (
- {k}
- ))}
-
-
-
- {Array.from({ length: Math.min(8, sequence.length) }, (_, i) => i + 1).map(d => (
-
- {d}
- {Array.from({ length: Math.min(12, sequence.length) }, (_, i) => i + 1).map(k => {
- const discrepancy = getDiscrepancy(d, k);
- const isValid = d * k <= sequence.length;
-
- return (
- {
- if (isValid) {
- setHoveredCell({ d, k });
- const result = allDiscrepancies.find(r => r.d === d && r.k === k);
- if (result) {
- setHighlightedSubsequence(result.positions);
- }
- }
- }}
- onMouseLeave={() => {
- setHoveredCell(null);
- setHighlightedSubsequence([]);
- }}
- >
- {isValid && discrepancy !== null ? discrepancy : ''}
-
- );
- })}
-
- ))}
-
-
-
-
- {hoveredCell && (
-
- d={hoveredCell.d}, k={hoveredCell.k}: Looking at positions{' '}
- {Array.from({ length: hoveredCell.k }, (_, i) => (i + 1) * hoveredCell.d).join(', ')}
-
- )}
-
-
-
-
-
- {/* Captor Reveal Phase */}
- {gamePhase === 'captor-reveal' && captorChosenD !== null && (
-
-
-
- The Captor Speaks...
-
- "Interesting sequence... Let me see... I choose d = {captorChosenD} !"
-
-
- You will follow every {captorChosenD === 1 ? '' : captorChosenD === 2 ? '2nd' : captorChosenD === 3 ? '3rd' : `${captorChosenD}th`} instruction:
- positions {Array.from({ length: Math.floor(sequence.length / captorChosenD) }, (_, i) => (i + 1) * captorChosenD).slice(0, 8).join(', ')}...
-
-
-
- Begin Walking
-
-
-
- )}
-
- {/* Walking Phase */}
- {gamePhase === 'walking' && captorChosenD !== null && (
-
-
-
-
Walking with d = {captorChosenD}...
-
- Step {prisonerStep} | Position: {prisonerPosition}
-
-
-
- {/* Tunnel visualization */}
-
-
- 🏔️
-
-
- 🐍
-
-
-
- 🚶
-
-
-
-
- )}
-
- {/* Fallen Phase */}
- {gamePhase === 'fallen' && (
-
-
- You Fell!
-
- After {prisonerStep} steps with d={captorChosenD}, you ended up at position {prisonerPosition}.
- The captor found your weakness! Your sequence lasted {sequence.length} instructions.
-
-
- Try Again
-
-
-
- Reset
-
-
-
-
- )}
-
- {/* Survived Phase (temporary - sequence ended) */}
- {gamePhase === 'survived' && (
-
-
- Sequence Complete!
-
- Your {sequence.length}-instruction sequence survived d={captorChosenD}!
- But remember: no sequence can survive forever. Try making it longer!
-
- setGamePhase('writing')} variant="outline" size="sm">
- Extend Sequence
-
-
-
- New Game
-
-
-
-
- )}
-
- )}
-
- {/* Acknowledgement */}
-
-
- Credits & History
-
- This puzzle is based on the Erdős Discrepancy Problem, posed by Paul Erdős in the 1930s
- (with a $500 prize!). The "Prisoner's Walk" framing comes from various mathematical expositions,
- popularized by James Grime's singingbanana video.
-
-
- 2014: Boris Konev & Alexei Lisitsa proved C=2 has maximum length 1,160 using SAT solvers
- (generating a 13GB proof!). 2015: Terence Tao proved that NO infinite sequence
- can have bounded discrepancy, settling the problem completely.
-
-
-
-
- {/* Social Share */}
- {showSocialShare !== false && (
-
- )}
-
- );
-};
-
-export default ErdosDiscrepancyPuzzle;
diff --git a/src/components/EternalDominationGame.tsx b/src/components/EternalDominationGame.tsx
index fd2e461..4720bf1 100644
--- a/src/components/EternalDominationGame.tsx
+++ b/src/components/EternalDominationGame.tsx
@@ -3,7 +3,6 @@ import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { Shield, Target, RotateCcw, Info } from "lucide-react";
-import SocialShare from '@/components/SocialShare';
// Extended good rectangle: 10 columns × 12 rows (following Fig. 10)
// Border: columns 0 and 9, rows 0/1 (top) and 10/11 (bottom)
@@ -350,11 +349,7 @@ const defendAttack = (
};
-interface EternalDominationGameProps {
- showSocialShare?: boolean;
-}
-
-const EternalDominationGame: React.FC = ({ showSocialShare = true }) => {
+const EternalDominationGame: React.FC = () => {
const [guards, setGuards] = useState>(generateInitialGuards);
const [attackHistory, setAttackHistory] = useState([]);
const [message, setMessage] = useState("Click any unguarded cell to attack.");
@@ -684,13 +679,6 @@ const EternalDominationGame: React.FC = ({ showSocia
)}
-
- {showSocialShare && (
-
- )}
);
};
diff --git a/src/components/FerrersRogersRamanujan.tsx b/src/components/FerrersRogersRamanujan.tsx
index a5c95ac..1da4500 100644
--- a/src/components/FerrersRogersRamanujan.tsx
+++ b/src/components/FerrersRogersRamanujan.tsx
@@ -4,13 +4,8 @@ import { Button } from "@/components/ui/button";
import { Slider } from "@/components/ui/slider";
import { Input } from "@/components/ui/input";
import { toast } from "sonner";
-import SocialShare from '@/components/SocialShare';
-interface FerrersRogersRamanujanProps {
- showSocialShare?: boolean;
-}
-
-const FerrersRogersRamanujan = ({ showSocialShare = true }: FerrersRogersRamanujanProps) => {
+const FerrersRogersRamanujan = () => {
const [n, setN] = useState(14);
const [partitionSliders, setPartitionSliders] = useState([]);
const [committedPartition, setCommittedPartition] = useState([]);
@@ -335,13 +330,6 @@ const FerrersRogersRamanujan = ({ showSocialShare = true }: FerrersRogersRamanuj
The hook sizes form a new partition where parts differ by at least 2.
-
- {showSocialShare && (
-
- )}
);
diff --git a/src/components/GoldCoinGame.tsx b/src/components/GoldCoinGame.tsx
index 456dc17..f2dd211 100644
--- a/src/components/GoldCoinGame.tsx
+++ b/src/components/GoldCoinGame.tsx
@@ -18,11 +18,7 @@ interface GameState {
moveHistory: string[];
}
-interface GoldCoinGameProps {
- showSocialShare?: boolean;
-}
-
-const GoldCoinGame: React.FC = ({ showSocialShare = true }) => {
+const GoldCoinGame: React.FC = () => {
const [gameState, setGameState] = useState({
track: Array(15).fill('empty'),
currentPlayer: 0,
@@ -425,14 +421,13 @@ const GoldCoinGame: React.FC = ({ showSocialShare = true }) =
- {showSocialShare && (
-
- )}
+
);
};
-export default GoldCoinGame;
\ No newline at end of file
+export default GoldCoinGame;
\ No newline at end of file
diff --git a/src/components/InteractiveGallery.tsx b/src/components/InteractiveGallery.tsx
index 24a3a03..1086712 100644
--- a/src/components/InteractiveGallery.tsx
+++ b/src/components/InteractiveGallery.tsx
@@ -67,7 +67,7 @@ const InteractiveGallery = () => {
Data Structures and Algorithms
-
Interactive explorations of elementary data structures and algorithms.
+
Interactive explorations of elementary data structures and algorithms.
{/* Search bar */}
@@ -103,4 +103,4 @@ const InteractiveGallery = () => {
);
};
-export default InteractiveGallery;
+export default InteractiveGallery;
\ No newline at end of file
diff --git a/src/components/InteractiveIndex.tsx b/src/components/InteractiveIndex.tsx
index 4223abe..a9ce43c 100644
--- a/src/components/InteractiveIndex.tsx
+++ b/src/components/InteractiveIndex.tsx
@@ -18,7 +18,7 @@ interface Interactive {
}
// This would ideally be generated from the actual components, but for now we'll hardcode
-export const allInteractives: Interactive[] = [
+const allInteractives: Interactive[] = [
{
id: 'binary-number-game',
title: 'Binary Number Representation',
@@ -288,15 +288,6 @@ export const allInteractives: Interactive[] = [
path: '/puzzles/ladybug-clock',
theme: 'Puzzles',
dateAdded: '2025-01-21'
- },
- {
- id: 'erdos-discrepancy',
- title: 'The Erdős Discrepancy Problem',
- description: 'Can you write instructions to survive the tunnel forever? Explore this famous mathematical puzzle through the Prisoner\'s Walk game.',
- tags: ['erdos', 'discrepancy', 'sequences', 'game-theory', 'impossibility', 'puzzle', 'number-theory'],
- path: '/puzzles/erdos-discrepancy',
- theme: 'Puzzles',
- dateAdded: '2025-01-22'
}
];
@@ -323,7 +314,7 @@ const InteractiveIndex = () => {
// Filter and sort interactives
const filteredAndSortedInteractives = useMemo(() => {
- const filtered = allInteractives.filter(interactive => {
+ let filtered = allInteractives.filter(interactive => {
const matchesSearch = interactive.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
interactive.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
interactive.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()));
@@ -549,4 +540,4 @@ const InteractiveIndex = () => {
);
};
-export default InteractiveIndex;
+export default InteractiveIndex;
\ No newline at end of file
diff --git a/src/components/LadybugClockPuzzle.tsx b/src/components/LadybugClockPuzzle.tsx
index 7de133f..4c6de70 100644
--- a/src/components/LadybugClockPuzzle.tsx
+++ b/src/components/LadybugClockPuzzle.tsx
@@ -8,9 +8,11 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Play, Pause, RotateCcw, SkipForward, FastForward, Info, ExternalLink, BarChart3 } from 'lucide-react';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, ReferenceLine, Cell } from 'recharts';
import SocialShare from '@/components/SocialShare';
+
interface LadybugClockPuzzleProps {
showSocialShare?: boolean;
}
+
interface SimulationState {
position: number; // 0-11 (0 = 12 o'clock, 1 = 1 o'clock, etc.)
visited: Set
;
@@ -19,10 +21,10 @@ interface SimulationState {
isComplete: boolean;
moveCount: number;
}
+
const CLOCK_NUMBERS = [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
-const LadybugClockPuzzle: React.FC = ({
- showSocialShare = true
-}) => {
+
+const LadybugClockPuzzle: React.FC = ({ showSocialShare = true }) => {
// Simulation state
const [simulation, setSimulation] = useState({
position: 0,
@@ -32,32 +34,33 @@ const LadybugClockPuzzle: React.FC = ({
isComplete: false,
moveCount: 0
});
-
+
// Controls
const [isPlaying, setIsPlaying] = useState(false);
const [speed, setSpeed] = useState(50); // 1-100, maps to delay
const [numPositions, setNumPositions] = useState(12);
-
+
// Statistics
const [lastPaintedCounts, setLastPaintedCounts] = useState>({});
const [totalSimulations, setTotalSimulations] = useState(0);
const [averageMoves, setAverageMoves] = useState(0);
const [totalMoves, setTotalMoves] = useState(0);
-
+
// User prediction
const [userPrediction, setUserPrediction] = useState(null);
const [showPredictionResult, setShowPredictionResult] = useState(false);
-
+
// Refs
const animationRef = useRef(null);
const isPlayingRef = useRef(isPlaying);
+
useEffect(() => {
isPlayingRef.current = isPlaying;
}, [isPlaying]);
// Get delay from speed (inverse relationship)
const getDelay = useCallback(() => {
- return Math.max(50, 1000 - speed * 9);
+ return Math.max(50, 1000 - (speed * 9));
}, [speed]);
// Reset simulation
@@ -80,15 +83,18 @@ const LadybugClockPuzzle: React.FC = ({
const step = useCallback(() => {
setSimulation(prev => {
if (prev.isComplete) return prev;
-
+
// Random move: clockwise or counterclockwise
const direction = Math.random() < 0.5 ? 1 : -1;
const newPosition = ((prev.position + direction) % numPositions + numPositions) % numPositions;
+
const newVisited = new Set(prev.visited);
const wasNewlyPainted = !newVisited.has(newPosition);
newVisited.add(newPosition);
+
const isNowComplete = newVisited.size === numPositions;
const lastPainted = isNowComplete && wasNewlyPainted ? newPosition : prev.lastPainted;
+
return {
position: newPosition,
visited: newVisited,
@@ -105,11 +111,13 @@ const LadybugClockPuzzle: React.FC = ({
if (!isPlaying || simulation.isComplete) {
return;
}
+
const timeoutId = setTimeout(() => {
if (isPlayingRef.current && !simulation.isComplete) {
step();
}
}, getDelay());
+
return () => clearTimeout(timeoutId);
}, [isPlaying, simulation, step, getDelay]);
@@ -126,40 +134,44 @@ const LadybugClockPuzzle: React.FC = ({
const visited = new Set([0]);
let moveCount = 0;
let lastPainted = 0;
+
while (visited.size < numPositions) {
const direction = Math.random() < 0.5 ? 1 : -1;
position = ((position + direction) % numPositions + numPositions) % numPositions;
+
if (!visited.has(position)) {
lastPainted = position;
visited.add(position);
}
moveCount++;
}
- return {
- lastPainted,
- moveCount
- };
+
+ return { lastPainted, moveCount };
}, [numPositions]);
// Run to completion
const runToCompletion = useCallback(() => {
setIsPlaying(false);
- const currentSim = simulation;
+
+ let currentSim = simulation;
let position = currentSim.position;
const visited = new Set(currentSim.visited);
let moveCount = currentSim.moveCount;
let lastPainted = currentSim.lastPainted;
const path = [...currentSim.path];
+
while (visited.size < numPositions) {
const direction = Math.random() < 0.5 ? 1 : -1;
position = ((position + direction) % numPositions + numPositions) % numPositions;
path.push(position);
+
if (!visited.has(position)) {
lastPainted = position;
visited.add(position);
}
moveCount++;
}
+
setSimulation({
position,
visited,
@@ -172,15 +184,15 @@ const LadybugClockPuzzle: React.FC = ({
// Run batch simulations
const runBatchSimulations = useCallback((count: number) => {
- const newCounts = {
- ...lastPaintedCounts
- };
+ const newCounts = { ...lastPaintedCounts };
let batchMoves = 0;
+
for (let i = 0; i < count; i++) {
const result = runInstantSimulation();
newCounts[result.lastPainted] = (newCounts[result.lastPainted] || 0) + 1;
batchMoves += result.moveCount;
}
+
setLastPaintedCounts(newCounts);
setTotalSimulations(prev => prev + count);
setTotalMoves(prev => prev + batchMoves);
@@ -213,7 +225,7 @@ const LadybugClockPuzzle: React.FC = ({
// Calculate clock position for a given index
const getClockPosition = (index: number, radius: number) => {
- const angle = index / numPositions * 2 * Math.PI - Math.PI / 2;
+ const angle = (index / numPositions) * 2 * Math.PI - Math.PI / 2;
return {
x: Math.cos(angle) * radius,
y: Math.sin(angle) * radius
@@ -221,12 +233,10 @@ const LadybugClockPuzzle: React.FC = ({
};
// Prepare chart data
- const chartData = Array.from({
- length: numPositions - 1
- }, (_, i) => {
+ const chartData = Array.from({ length: numPositions - 1 }, (_, i) => {
const pos = i + 1; // 1 to n-1 (0/12 can't be last)
const count = lastPaintedCounts[pos] || 0;
- const percentage = totalSimulations > 0 ? count / totalSimulations * 100 : 0;
+ const percentage = totalSimulations > 0 ? (count / totalSimulations) * 100 : 0;
return {
position: positionToNumber(pos),
count,
@@ -234,9 +244,15 @@ const LadybugClockPuzzle: React.FC = ({
label: `${positionToNumber(pos)}`
};
});
+
const theoreticalProbability = 100 / (numPositions - 1);
- const shareUrl = typeof window !== 'undefined' ? `${window.location.origin}/puzzles/ladybug-clock` : '';
- return
+
+ const shareUrl = typeof window !== 'undefined'
+ ? `${window.location.origin}/puzzles/ladybug-clock`
+ : '';
+
+ return (
+
{/* Header */}
The Ladybug Clock Puzzle
@@ -256,34 +272,71 @@ const LadybugClockPuzzle: React.FC = ({
{/* Hour markers and numbers */}
- {Array.from({
- length: numPositions
- }, (_, i) => {
- const pos = getClockPosition(i, 100);
- const isPainted = simulation.visited.has(i);
- const isLast = simulation.isComplete && simulation.lastPainted === i;
- const isCurrent = simulation.position === i;
- return
+ {Array.from({ length: numPositions }, (_, i) => {
+ const pos = getClockPosition(i, 100);
+ const isPainted = simulation.visited.has(i);
+ const isLast = simulation.isComplete && simulation.lastPainted === i;
+ const isCurrent = simulation.position === i;
+
+ return (
+
{/* Tick mark */}
-
+
{/* Number circle background */}
-
+
{/* Glow effect for newly painted or last */}
- {(isPainted || isLast) && }
+ {(isPainted || isLast) && (
+
+ )}
{/* Number text */}
-
+
{positionToNumber(i)}
- ;
- })}
+
+ );
+ })}
{/* Ladybug */}
{(() => {
- const ladybugPos = getClockPosition(simulation.position, 100);
- return
+ const ladybugPos = getClockPosition(simulation.position, 100);
+ return (
+
{/* Ladybug body */}
{/* Head */}
@@ -298,8 +351,9 @@ const LadybugClockPuzzle: React.FC = ({
{/* Antennae */}
- ;
- })()}
+
+ );
+ })()}
{/* Center decoration */}
@@ -320,11 +374,13 @@ const LadybugClockPuzzle: React.FC = ({
- {simulation.isComplete && simulation.lastPainted !== null &&
+ {simulation.isComplete && simulation.lastPainted !== null && (
+
🎉 Complete! Last painted: {positionToNumber(simulation.lastPainted)}
-
}
+
+ )}
@@ -338,17 +394,32 @@ const LadybugClockPuzzle: React.FC
= ({
-
setIsPlaying(!isPlaying)} disabled={simulation.isComplete} variant={isPlaying ? "destructive" : "default"} size="sm">
+ setIsPlaying(!isPlaying)}
+ disabled={simulation.isComplete}
+ variant={isPlaying ? "destructive" : "default"}
+ size="sm"
+ >
{isPlaying ? : }
{isPlaying ? 'Pause' : 'Play'}
-
+
Step
-
+
Complete
@@ -361,12 +432,26 @@ const LadybugClockPuzzle: React.FC = ({
Animation Speed
- setSpeed(v[0])} min={1} max={100} step={1} className="w-full" />
+ setSpeed(v[0])}
+ min={1}
+ max={100}
+ step={1}
+ className="w-full"
+ />
Number of Positions: {numPositions}
- setNumPositions(v[0])} min={4} max={24} step={2} className="w-full" />
+ setNumPositions(v[0])}
+ min={4}
+ max={24}
+ step={2}
+ className="w-full"
+ />
@@ -381,9 +466,16 @@ const LadybugClockPuzzle: React.FC = ({
- {[100, 1000, 10000].map(count =>
runBatchSimulations(count)} variant="outline" size="sm">
+ {[100, 1000, 10000].map(count => (
+ runBatchSimulations(count)}
+ variant="outline"
+ size="sm"
+ >
Run {count.toLocaleString()}
- )}
+
+ ))}
Clear Stats
@@ -393,15 +485,18 @@ const LadybugClockPuzzle: React.FC
= ({
Total simulations: {totalSimulations.toLocaleString()}
- {totalSimulations > 0 &&
+ {totalSimulations > 0 && (
+
Average moves to complete: {averageMoves.toFixed(1)}
-
}
+
+ )}
{/* User Prediction */}
- {totalSimulations === 0 &&
+ {totalSimulations === 0 && (
+
Make a Prediction! 🤔
@@ -409,23 +504,33 @@ const LadybugClockPuzzle: React.FC = ({
Which number do you think is most likely to be painted last?
-
- {Array.from({
- length: numPositions - 1
- }, (_, i) => i + 1).map(pos =>
setUserPrediction(pos)} variant={userPrediction === pos ? "default" : "outline"} size="sm" className="w-8 h-8 text-xs p-0">
+
+ {Array.from({ length: numPositions - 1 }, (_, i) => i + 1).map(pos => (
+ setUserPrediction(pos)}
+ variant={userPrediction === pos ? "default" : "outline"}
+ size="sm"
+ className="w-10 h-10"
+ >
{positionToNumber(pos)}
- )}
+
+ ))}
- {userPrediction !== null &&
+ {userPrediction !== null && (
+
You predicted: {positionToNumber(userPrediction)} . Run some simulations to see!
-
}
+
+ )}
- }
+
+ )}
{/* Statistics Chart */}
- {totalSimulations > 0 &&
+ {totalSimulations > 0 && (
+
Statistics: Which number was painted last?
@@ -435,42 +540,52 @@ const LadybugClockPuzzle: React.FC = ({
-
+
- `${v.toFixed(1)}%`} domain={[0, Math.max(theoreticalProbability * 1.5, 15)]} />
- [`${value.toFixed(2)}%`, 'Probability']} />
-
+ `${v.toFixed(1)}%`}
+ domain={[0, Math.max(theoreticalProbability * 1.5, 15)]}
+ />
+ [`${value.toFixed(2)}%`, 'Probability']}
+ />
+
- {chartData.map((entry, index) => | )}
+ {chartData.map((entry, index) => (
+ |
+ ))}
- {userPrediction !== null &&
+ {userPrediction !== null && (
+
Your prediction ({positionToNumber(userPrediction)}): {' '}
{((lastPaintedCounts[userPrediction] || 0) / totalSimulations * 100).toFixed(2)}%
{' '}— Theory predicts all numbers have equal probability of{' '}
{theoreticalProbability.toFixed(2)}% !
-
}
+
+ )}
@@ -479,21 +594,34 @@ const LadybugClockPuzzle: React.FC = ({
- }
+
+ )}
{/* Path History */}
- {simulation.path.length > 1 &&
+ {simulation.path.length > 1 && (
+
Path History
- {simulation.path.map((pos, i) =>
+ {simulation.path.map((pos, i) => (
+
{positionToNumber(pos)}
- )}
+
+ ))}
- }
+
+ )}
{/* Educational Info */}
@@ -513,14 +641,19 @@ const LadybugClockPuzzle: React.FC = ({
The simulation ends when all numbers have been visited at least once
+ The Surprising Result
+
+ Intuitively, you might expect the number 6 (opposite to 12) to be the most likely to be painted last,
+ since it's the farthest from the starting point. However, the probability is exactly 1/{numPositions - 1} for
+ every number from 1 to {numPositions - 1}! This is because once all but one number is painted, the random walk
+ will eventually reach that last number, regardless of where it is.
+
-
-
-
+ Mathematical Connections
-
-
-
+ Random Walks on Cycles: This is an example of a random walk on a cycle graph
+ Cover Time: The number of moves to visit all vertices is called the "cover time"
+ Gambler's Ruin: Related to the classic probability problem of a gambler with a finite amount
@@ -537,12 +670,21 @@ const LadybugClockPuzzle: React.FC = ({
{' '}and demonstrated in{' '}
this YouTube short
- .
+
+ . It illustrates a beautiful result about random walks on cycle graphs: every position (except the start) has equal probability of being visited last.
- {showSocialShare && }
- ;
+ {showSocialShare && (
+
+ )}
+
+ );
};
+
export default LadybugClockPuzzle;
diff --git a/src/components/NimGame.tsx b/src/components/NimGame.tsx
index 5fe070c..924a523 100644
--- a/src/components/NimGame.tsx
+++ b/src/components/NimGame.tsx
@@ -27,11 +27,7 @@ const heapColors = [
'#f87171', '#a78bfa', '#fbbf24', '#60a5fa', '#34d399', '#f472b6', '#f87171', '#a78bfa', '#facc15', '#38bdf8',
];
-interface NimGameProps {
- showSocialShare?: boolean;
-}
-
-const NimGame: React.FC = ({ showSocialShare = true }) => {
+const NimGame: React.FC = () => {
const [mode, setMode] = useState(null);
const [showSetup, setShowSetup] = useState(false);
const [userHeapCount, setUserHeapCount] = useState(3);
@@ -279,14 +275,16 @@ const NimGame: React.FC = ({ showSocialShare = true }) => {
- {showSocialShare && (
+ {/* Social Share */}
+
- )}
+
);
};
-export default NimGame;
\ No newline at end of file
+export default NimGame;
\ No newline at end of file
diff --git a/src/components/RentDivisionPuzzle.tsx b/src/components/RentDivisionPuzzle.tsx
index 51f9951..b06a5b1 100644
--- a/src/components/RentDivisionPuzzle.tsx
+++ b/src/components/RentDivisionPuzzle.tsx
@@ -11,7 +11,6 @@ import {
SelectValue,
} from "@/components/ui/select";
import { RefreshCw, User, UserRound, Users, UserCheck } from "lucide-react";
-import SocialShare from '@/components/SocialShare';
const AGENTS = ["Alice", "Bob", "Charlie", "Dana"];
const AGENT_ICONS = [User, UserRound, Users, UserCheck];
@@ -24,10 +23,9 @@ const ROOM_COLORS = [
interface RentDivisionPuzzleProps {
onComplete?: (completed: boolean) => void;
- showSocialShare?: boolean;
}
-const RentDivisionPuzzle = ({ onComplete, showSocialShare = true }: RentDivisionPuzzleProps) => {
+const RentDivisionPuzzle = ({ onComplete }: RentDivisionPuzzleProps) => {
// V-matrix: valuations[agent][room]
const [valuations, setValuations] = useState(() =>
Array.from({ length: 4 }, () =>
@@ -471,12 +469,6 @@ const RentDivisionPuzzle = ({ onComplete, showSocialShare = true }: RentDivision
- {showSocialShare && (
-
- )}
);
};
diff --git a/src/components/RulesOfInferencePlayground.tsx b/src/components/RulesOfInferencePlayground.tsx
index 54d85b9..1f226f8 100644
--- a/src/components/RulesOfInferencePlayground.tsx
+++ b/src/components/RulesOfInferencePlayground.tsx
@@ -3,7 +3,6 @@ import { Button } from "@/components/ui/button";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { useToast } from "@/hooks/use-toast";
import { AlertDialog, AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
-import SocialShare from '@/components/SocialShare';
// Rules of Inference Playground (Example 1: Resolution)
// Layout: 3 columns (Premises | Notepad lines with rule margin | Rules)
@@ -197,7 +196,7 @@ function DropZone({ children, onDrop, className }: { children?: React.ReactNode;
);
}
-export default function RulesOfInferencePlayground({ onComplete, showSocialShare = true }: { onComplete?: (completed: boolean) => void; showSocialShare?: boolean }) {
+export default function RulesOfInferencePlayground({ onComplete }: { onComplete?: (completed: boolean) => void }) {
const { toast } = useToast();
const [exampleId, setExampleId] = useState(EXAMPLES[0].id);
const example = useMemo(() => EXAMPLES.find((e) => e.id === exampleId)!, [exampleId]);
@@ -580,13 +579,6 @@ export default function RulesOfInferencePlayground({ onComplete, showSocialShare
-
- {showSocialShare && (
-
- )}
);
-}
+}
\ No newline at end of file
diff --git a/src/components/ZeckendorfGame.tsx b/src/components/ZeckendorfGame.tsx
index 1655e46..439b0d0 100644
--- a/src/components/ZeckendorfGame.tsx
+++ b/src/components/ZeckendorfGame.tsx
@@ -34,7 +34,7 @@ const ZeckendorfGame: React.FC = ({ showSocialShare = true
// Generate a random combination of Fibonacci numbers
const numTerms = Math.floor(Math.random() * 4) + 2; // 2-5 terms
let target = 0;
- const usedIndices = new Set();
+ let usedIndices = new Set();
for (let i = 0; i < numTerms; i++) {
let index;
@@ -235,4 +235,4 @@ const ZeckendorfGame: React.FC = ({ showSocialShare = true
);
};
-export default ZeckendorfGame;
+export default ZeckendorfGame;
\ No newline at end of file
diff --git a/src/components/guessing-game/QuestionGuessingMode.tsx b/src/components/guessing-game/QuestionGuessingMode.tsx
index d76a11a..d30855a 100644
--- a/src/components/guessing-game/QuestionGuessingMode.tsx
+++ b/src/components/guessing-game/QuestionGuessingMode.tsx
@@ -217,7 +217,7 @@ const QuestionGuessingMode: React.FC = ({
case 'odd':
baseCondition = (num % 2 !== 0);
break;
- case 'prime': {
+ case 'prime':
const isPrimeNum = (n: number): boolean => {
if (n < 2) return false;
for (let i = 2; i <= Math.sqrt(n); i++) {
@@ -227,7 +227,6 @@ const QuestionGuessingMode: React.FC = ({
};
baseCondition = isPrimeNum(num);
break;
- }
case 'perfectSquare':
baseCondition = Math.sqrt(num) % 1 === 0;
break;
diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx
index 69d63a4..56a0979 100644
--- a/src/components/ui/command.tsx
+++ b/src/components/ui/command.tsx
@@ -21,7 +21,7 @@ const Command = React.forwardRef<
))
Command.displayName = CommandPrimitive.displayName
-type CommandDialogProps = DialogProps
+interface CommandDialogProps extends DialogProps {}
const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
return (
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
index 12c9136..9f9a6dc 100644
--- a/src/components/ui/textarea.tsx
+++ b/src/components/ui/textarea.tsx
@@ -2,7 +2,8 @@ import * as React from "react"
import { cn } from "@/lib/utils"
-export type TextareaProps = React.TextareaHTMLAttributes
+export interface TextareaProps
+ extends React.TextareaHTMLAttributes {}
const Textarea = React.forwardRef(
({ className, ...props }, ref) => {
diff --git a/src/pages/AscDescGridPuzzlePage.tsx b/src/pages/AscDescGridPuzzlePage.tsx
index da7dde4..7963768 100644
--- a/src/pages/AscDescGridPuzzlePage.tsx
+++ b/src/pages/AscDescGridPuzzlePage.tsx
@@ -1,12 +1,13 @@
+import Layout from "@/components/Layout";
import AscDescGridPuzzle from "@/components/AscDescGridPuzzle";
const AscDescGridPuzzlePage = () => {
return (
-
-
+
);
};
diff --git a/src/pages/AssistedNimGamePage.tsx b/src/pages/AssistedNimGamePage.tsx
index 61b9527..233302a 100644
--- a/src/pages/AssistedNimGamePage.tsx
+++ b/src/pages/AssistedNimGamePage.tsx
@@ -1,13 +1,12 @@
+import React from 'react';
import AssistedNimGame from '@/components/AssistedNimGame';
const AssistedNimGamePage = () => {
return (
-
-
+
);
};
-export default AssistedNimGamePage;
\ No newline at end of file
+export default AssistedNimGamePage;
\ No newline at end of file
diff --git a/src/pages/BagchalGamePage.tsx b/src/pages/BagchalGamePage.tsx
index 3d65664..2e1ff48 100644
--- a/src/pages/BagchalGamePage.tsx
+++ b/src/pages/BagchalGamePage.tsx
@@ -1,12 +1,13 @@
+import Layout from "@/components/Layout";
import BagchalGame from "@/components/BagchalGame";
const BagchalGamePage = () => {
return (
-
+
);
};
diff --git a/src/pages/BalancedTernaryGamePage.tsx b/src/pages/BalancedTernaryGamePage.tsx
index bd70437..61f4057 100644
--- a/src/pages/BalancedTernaryGamePage.tsx
+++ b/src/pages/BalancedTernaryGamePage.tsx
@@ -2,12 +2,12 @@ import BalancedTernaryGame from '@/components/BalancedTernaryGame';
const BalancedTernaryGamePage = () => {
return (
-
-
-
+
);
};
-export default BalancedTernaryGamePage;
+export default BalancedTernaryGamePage;
\ No newline at end of file
diff --git a/src/pages/BinaryNumberGamePage.tsx b/src/pages/BinaryNumberGamePage.tsx
index 15b8a5c..d12b6c1 100644
--- a/src/pages/BinaryNumberGamePage.tsx
+++ b/src/pages/BinaryNumberGamePage.tsx
@@ -2,12 +2,12 @@ import BinaryNumberGame from '@/components/BinaryNumberGame';
const BinaryNumberGamePage = () => {
return (
-
-
-
+
);
};
-export default BinaryNumberGamePage;
+export default BinaryNumberGamePage;
\ No newline at end of file
diff --git a/src/pages/BulgarianSolitairePage.tsx b/src/pages/BulgarianSolitairePage.tsx
index 0f6651d..2a9dfc6 100644
--- a/src/pages/BulgarianSolitairePage.tsx
+++ b/src/pages/BulgarianSolitairePage.tsx
@@ -1,13 +1,29 @@
import BulgarianSolitaire from "@/components/BulgarianSolitaire";
+import { useSearchParams } from "react-router-dom";
const BulgarianSolitairePage = () => {
- return (
-
-
-
+ const [searchParams] = useSearchParams();
+ const from = searchParams.get('from');
+
+ if (from === 'puzzles') {
+ return (
+
+
+
+ window.location.href = '/themes/puzzles'}
+ className="text-primary hover:text-primary/80 font-medium"
+ >
+ ← Back to Puzzles
+
+
+
+
-
- );
+ );
+ }
+
+ return
;
};
-export default BulgarianSolitairePage;
+export default BulgarianSolitairePage;
\ No newline at end of file
diff --git a/src/pages/BurnsidesLemmaPage.tsx b/src/pages/BurnsidesLemmaPage.tsx
index 713c547..c3b7303 100644
--- a/src/pages/BurnsidesLemmaPage.tsx
+++ b/src/pages/BurnsidesLemmaPage.tsx
@@ -1,12 +1,13 @@
+import Layout from '@/components/Layout';
import BurnsidesLemma from '@/components/BurnsidesLemma';
const BurnsidesLemmaPage = () => {
return (
-
+
);
};
diff --git a/src/pages/CubeColoringPage.tsx b/src/pages/CubeColoringPage.tsx
index 1d8d941..30ddb00 100644
--- a/src/pages/CubeColoringPage.tsx
+++ b/src/pages/CubeColoringPage.tsx
@@ -1,12 +1,16 @@
+import Layout from '@/components/Layout';
import CubeColoring from '@/components/CubeColoring';
const CubeColoringPage = () => {
return (
-
+
);
};
diff --git a/src/pages/DominoRetilingPuzzlePage.tsx b/src/pages/DominoRetilingPuzzlePage.tsx
index 8170ba1..e3f7af7 100644
--- a/src/pages/DominoRetilingPuzzlePage.tsx
+++ b/src/pages/DominoRetilingPuzzlePage.tsx
@@ -1,13 +1,28 @@
+import Layout from '@/components/Layout';
import DominoRetilingPuzzle from '@/components/DominoRetilingPuzzle';
+import { useLocation, Link } from 'react-router-dom';
+import { ArrowLeft } from 'lucide-react';
const DominoRetilingPuzzlePage = () => {
+ const location = useLocation();
+ const fromPuzzles = location.search.includes('from=puzzles');
+
return (
-
+
+ {fromPuzzles && (
+
+
+
+ Back to Puzzles
+
+
+ )}
+
+
);
};
-export default DominoRetilingPuzzlePage;
+export default DominoRetilingPuzzlePage;
\ No newline at end of file
diff --git a/src/pages/ErdosDiscrepancyPuzzlePage.tsx b/src/pages/ErdosDiscrepancyPuzzlePage.tsx
deleted file mode 100644
index 2578ef0..0000000
--- a/src/pages/ErdosDiscrepancyPuzzlePage.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import ErdosDiscrepancyPuzzle from "@/components/ErdosDiscrepancyPuzzle";
-
-const ErdosDiscrepancyPuzzlePage = () => {
- return (
-
- );
-};
-
-export default ErdosDiscrepancyPuzzlePage;
diff --git a/src/pages/EternalDominationGamePage.tsx b/src/pages/EternalDominationGamePage.tsx
index 54506fe..bd81bb9 100644
--- a/src/pages/EternalDominationGamePage.tsx
+++ b/src/pages/EternalDominationGamePage.tsx
@@ -1,12 +1,87 @@
+import Layout from "@/components/Layout";
import EternalDominationGame from "@/components/EternalDominationGame";
+import SocialShare from "@/components/SocialShare";
+import { Badge } from "@/components/ui/badge";
+import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
const EternalDominationGamePage = () => {
return (
-
-
-
+
+
+
+
+
Miscellany
+
Eternal Domination on a Grid
+
+ Explore the m-eternal domination problem on a 12×10 grid. Guards must maintain
+ a dominating set while defending against arbitrary attack sequences.
+
+
+
+
+
+
+
+ Mathematical Background
+
+
+ Dominating Set: A subset S of vertices in a graph G such that
+ every vertex not in S has a neighbor in S. Think of guards placed at certain
+ vertices that can "watch" their neighbors.
+
+
+ m-Eternal Domination: A two-player game where the defender
+ places guards on vertices, and the attacker repeatedly attacks unguarded vertices.
+ The defender responds by moving guards (all can move simultaneously, but only to
+ adjacent vertices) such that one guard ends on the attacked vertex and the guards
+ still form a dominating set.
+
+
+ The Challenge: The defender wins if they can maintain a dominating
+ set forever against any attack sequence. The m-eternal domination number γ∞(G) is
+ the minimum number of guards needed to win.
+
+
+
+
+
+ Defense Strategy
+
+
+ This implementation uses the strategy from research on finite grids:
+
+
+ Border guards: All vertices on the border (rows 0, 1, 8, 9 and
+ columns 0, 11) are always guarded, forming a protective cycle C.
+ Interior guards: Placed in a pattern that ensures every interior
+ vertex is dominated by exactly one guard.
+ Defense mechanism: When attacked, interior guards shift toward
+ the attack, potentially pushing a guard to the border. Border guards shift along
+ "complementary paths" to fill the resulting gaps.
+
+
+
+
+
+ Reference
+
+
+ Based on: "m-Eternal Domination and Variants on Some Classes of Finite and
+ Infinite Graphs" by Calamoneri et al. (CIAC 2025), which establishes bounds
+ and strategies for eternal domination on various grid types including square,
+ hexagonal, and triangular grids.
+
+
+
+
+
+
+
-
+
);
};
diff --git a/src/pages/FerrersRogersRamanujanPage.tsx b/src/pages/FerrersRogersRamanujanPage.tsx
index 35253d1..c343f8e 100644
--- a/src/pages/FerrersRogersRamanujanPage.tsx
+++ b/src/pages/FerrersRogersRamanujanPage.tsx
@@ -1,12 +1,27 @@
+import Layout from "@/components/Layout";
import FerrersRogersRamanujan from "@/components/FerrersRogersRamanujan";
+import SocialShare from "@/components/SocialShare";
const FerrersRogersRamanujanPage = () => {
return (
-
-
-
+
+
+
+
Ferrers Diagram & Rogers-Ramanujan Partition
+
+ Explore integer partitions through visual Ferrers diagrams and discover the elegant Rogers-Ramanujan transformation.
+
+
+
+
+
+
-
+
);
};
diff --git a/src/pages/GoldCoinGamePage.tsx b/src/pages/GoldCoinGamePage.tsx
index 0ef4b78..d838ae9 100644
--- a/src/pages/GoldCoinGamePage.tsx
+++ b/src/pages/GoldCoinGamePage.tsx
@@ -1,13 +1,12 @@
+import React from 'react';
import GoldCoinGame from '@/components/GoldCoinGame';
const GoldCoinGamePage = () => {
return (
-
-
-
-
+
+
);
};
-export default GoldCoinGamePage;
\ No newline at end of file
+export default GoldCoinGamePage;
\ No newline at end of file
diff --git a/src/pages/LadybugClockPuzzlePage.tsx b/src/pages/LadybugClockPuzzlePage.tsx
index 71bdf85..df6554a 100644
--- a/src/pages/LadybugClockPuzzlePage.tsx
+++ b/src/pages/LadybugClockPuzzlePage.tsx
@@ -1,12 +1,29 @@
+import Layout from '@/components/Layout';
import LadybugClockPuzzle from '@/components/LadybugClockPuzzle';
+import { useLocation, Link } from 'react-router-dom';
+import { ArrowLeft } from 'lucide-react';
const LadybugClockPuzzlePage = () => {
+ const location = useLocation();
+ const fromPuzzles = location.search.includes('from=puzzles');
+
return (
-
-
-
+
+ {fromPuzzles && (
+
+
+
+ Back to Puzzles
+
+
+ )}
+
+
-
+
);
};
diff --git a/src/pages/NimGamePage.tsx b/src/pages/NimGamePage.tsx
index f34463a..aa202f6 100644
--- a/src/pages/NimGamePage.tsx
+++ b/src/pages/NimGamePage.tsx
@@ -1,13 +1,12 @@
+import React from 'react';
import NimGame from '@/components/NimGame';
const NimGamePage = () => {
return (
-
-
-
-
+
+
);
};
-export default NimGamePage;
\ No newline at end of file
+export default NimGamePage;
\ No newline at end of file
diff --git a/src/pages/ParityBitsGamePage.tsx b/src/pages/ParityBitsGamePage.tsx
index 58949d2..6ba5a0b 100644
--- a/src/pages/ParityBitsGamePage.tsx
+++ b/src/pages/ParityBitsGamePage.tsx
@@ -1,13 +1,12 @@
+import Layout from "@/components/Layout";
import ParityBitsGame from "@/components/ParityBitsGame";
const ParityBitsGamePage = () => {
return (
-
+
+
+
);
};
-export default ParityBitsGamePage;
+export default ParityBitsGamePage;
\ No newline at end of file
diff --git a/src/pages/PebblePlacementGamePage.tsx b/src/pages/PebblePlacementGamePage.tsx
index c359304..e6013d1 100644
--- a/src/pages/PebblePlacementGamePage.tsx
+++ b/src/pages/PebblePlacementGamePage.tsx
@@ -1,13 +1,29 @@
import PebblePlacementGame from "@/components/PebblePlacementGame";
+import { useSearchParams } from "react-router-dom";
const PebblePlacementGamePage = () => {
- return (
-
-
-
+ const [searchParams] = useSearchParams();
+ const from = searchParams.get('from');
+
+ if (from === 'puzzles') {
+ return (
+
+
+
+ window.location.href = '/themes/puzzles'}
+ className="text-primary hover:text-primary/80 font-medium"
+ >
+ ← Back to Puzzles
+
+
+
+
-
- );
+ );
+ }
+
+ return
;
};
-export default PebblePlacementGamePage;
+export default PebblePlacementGamePage;
\ No newline at end of file
diff --git a/src/pages/PresentsPuzzlePage.tsx b/src/pages/PresentsPuzzlePage.tsx
index edb710a..cc51817 100644
--- a/src/pages/PresentsPuzzlePage.tsx
+++ b/src/pages/PresentsPuzzlePage.tsx
@@ -1,12 +1,13 @@
+import Layout from '@/components/Layout';
import PresentsPuzzle from "@/components/PresentsPuzzle";
const PresentsPuzzlePage = () => {
return (
-
-
+
);
};
diff --git a/src/pages/RentDivisionPuzzlePage.tsx b/src/pages/RentDivisionPuzzlePage.tsx
index 154033f..63dad7f 100644
--- a/src/pages/RentDivisionPuzzlePage.tsx
+++ b/src/pages/RentDivisionPuzzlePage.tsx
@@ -1,10 +1,34 @@
+import { useState } from "react";
import RentDivisionPuzzle from "@/components/RentDivisionPuzzle";
+import SocialShare from "@/components/SocialShare";
const RentDivisionPuzzlePage = () => {
+ const [isCompleted, setIsCompleted] = useState(false);
+
return (
-
-
-
+
+
+
+
+ Envy-Free Rent Division
+
+
+ Explore fair division by assigning rooms and setting rents. Can you find an
+ allocation where no one envies another?
+
+
+
+
+
+
);
diff --git a/src/pages/RulesOfInferencePlaygroundPage.tsx b/src/pages/RulesOfInferencePlaygroundPage.tsx
index 34a2fd3..8c9a2f8 100644
--- a/src/pages/RulesOfInferencePlaygroundPage.tsx
+++ b/src/pages/RulesOfInferencePlaygroundPage.tsx
@@ -1,13 +1,34 @@
+import { useState } from "react";
import RulesOfInferencePlayground from "@/components/RulesOfInferencePlayground";
+import SocialShare from "@/components/SocialShare";
const RulesOfInferencePlaygroundPage = () => {
+ const [isCompleted, setIsCompleted] = useState(false);
+
return (
-
-
-
+
+
+
+
Rules of Inference Playground
+
+ Practice applying resolution and equivalence rules to derive conclusions step by step.
+
+
+
+
+
+
);
};
-export default RulesOfInferencePlaygroundPage;
+export default RulesOfInferencePlaygroundPage;
\ No newline at end of file
diff --git a/src/pages/StackingBlocksPage.tsx b/src/pages/StackingBlocksPage.tsx
index ccb4f78..7239dfc 100644
--- a/src/pages/StackingBlocksPage.tsx
+++ b/src/pages/StackingBlocksPage.tsx
@@ -1,13 +1,29 @@
import StackingBlocks from "@/components/StackingBlocks";
+import { useSearchParams } from "react-router-dom";
const StackingBlocksPage = () => {
- return (
-
-
-
+ const [searchParams] = useSearchParams();
+ const from = searchParams.get('from');
+
+ if (from === 'puzzles') {
+ return (
+
+
+
+ window.location.href = '/themes/puzzles'}
+ className="text-primary hover:text-primary/80 font-medium"
+ >
+ ← Back to Puzzles
+
+
+
+
-
- );
+ );
+ }
+
+ return
;
};
-export default StackingBlocksPage;
+export default StackingBlocksPage;
\ No newline at end of file
diff --git a/src/pages/SubtractionGamePage.tsx b/src/pages/SubtractionGamePage.tsx
index 75c3d78..302ca9c 100644
--- a/src/pages/SubtractionGamePage.tsx
+++ b/src/pages/SubtractionGamePage.tsx
@@ -1,13 +1,7 @@
import SubtractionGame from '@/components/SubtractionGame';
const SubtractionGamePage = () => {
- return (
-
- );
+ return
;
};
-export default SubtractionGamePage;
+export default SubtractionGamePage;
\ No newline at end of file
diff --git a/src/pages/TernaryNumberGamePage.tsx b/src/pages/TernaryNumberGamePage.tsx
index a85620d..79ba882 100644
--- a/src/pages/TernaryNumberGamePage.tsx
+++ b/src/pages/TernaryNumberGamePage.tsx
@@ -2,12 +2,12 @@ import TernaryNumberGame from '@/components/TernaryNumberGame';
const TernaryNumberGamePage = () => {
return (
-
-
-
+
);
};
-export default TernaryNumberGamePage;
+export default TernaryNumberGamePage;
\ No newline at end of file
diff --git a/src/pages/ZeckendorfGamePage.tsx b/src/pages/ZeckendorfGamePage.tsx
index c585236..130ecfa 100644
--- a/src/pages/ZeckendorfGamePage.tsx
+++ b/src/pages/ZeckendorfGamePage.tsx
@@ -2,12 +2,12 @@ import ZeckendorfGame from '@/components/ZeckendorfGame';
const ZeckendorfGamePage = () => {
return (
-
-
-
+
);
};
-export default ZeckendorfGamePage;
\ No newline at end of file
+export default ZeckendorfGamePage;
\ No newline at end of file
diff --git a/src/pages/ZeckendorfSearchTrickPage.tsx b/src/pages/ZeckendorfSearchTrickPage.tsx
index f5cc437..59e977f 100644
--- a/src/pages/ZeckendorfSearchTrickPage.tsx
+++ b/src/pages/ZeckendorfSearchTrickPage.tsx
@@ -2,12 +2,12 @@ import ZeckendorfSearchTrick from '@/components/ZeckendorfSearchTrick';
const ZeckendorfSearchTrickPage = () => {
return (
-
-
-
+
);
};
-export default ZeckendorfSearchTrickPage;
\ No newline at end of file
+export default ZeckendorfSearchTrickPage;
\ No newline at end of file
diff --git a/src/pages/theme-pages/Puzzles.tsx b/src/pages/theme-pages/Puzzles.tsx
index 5e6b9b1..323270e 100644
--- a/src/pages/theme-pages/Puzzles.tsx
+++ b/src/pages/theme-pages/Puzzles.tsx
@@ -115,16 +115,6 @@ const Puzzles = () => {
difficulty: "Intermediate" as const,
duration: "5-15 min",
participants: "1 player"
- },
- {
- id: "erdos-discrepancy",
- title: "The Erdős Discrepancy Problem",
- description: "Can you write instructions to survive the prisoner's tunnel? Explore this famous mathematical puzzle and discover why no sequence can keep you safe forever!",
- path: "/puzzles/erdos-discrepancy",
- tags: ["Number Theory", "Sequences", "Impossibility", "Game Theory"],
- difficulty: "Advanced" as const,
- duration: "10-30 min",
- participants: "1 player"
}
];
diff --git a/src/pages/theme-pages/discrete-math/Foundations.tsx b/src/pages/theme-pages/discrete-math/Foundations.tsx
index c8eec1b..c4eba6c 100644
--- a/src/pages/theme-pages/discrete-math/Foundations.tsx
+++ b/src/pages/theme-pages/discrete-math/Foundations.tsx
@@ -3,6 +3,7 @@ import { Link } from "react-router-dom";
import { Search } from "lucide-react";
import Layout from "@/components/Layout";
import InteractiveCard from "@/components/InteractiveCard";
+import SocialShare from "@/components/SocialShare";
import BinaryNumberGame from "@/components/BinaryNumberGame";
import TernaryNumberGame from "@/components/TernaryNumberGame";
import BalancedTernaryGame from "@/components/BalancedTernaryGame";
@@ -79,6 +80,12 @@ const Foundations = () => {
+
+
;
}
@@ -110,4 +117,4 @@ const Foundations = () => {
;
};
-export default Foundations;
+export default Foundations;
\ No newline at end of file
diff --git a/tailwind.config.ts b/tailwind.config.ts
index a934352..a4b00a3 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -1,5 +1,4 @@
import type { Config } from "tailwindcss";
-import tailwindcssAnimate from "tailwindcss-animate";
export default {
darkMode: ["class"],
@@ -123,5 +122,5 @@ export default {
}
}
},
- plugins: [tailwindcssAnimate],
+ plugins: [require("tailwindcss-animate")],
} satisfies Config;