// @ts-nocheck
// This file bundles all logic (Components, Services, Types) to run directly in the browser.
// Explicitly access globals to ensure we use the UMD build
const React = window.React;
const ReactDOM = window.ReactDOM;
const { useState, useEffect, useRef, useCallback } = React;
// --- CONFIGURATION ---
const PUZZLE_IMAGES = [
"images/puzzle1.jpg",
"images/puzzle2.jpg",
"images/puzzle3.jpg"
];
const BG_IMAGE = "bg.jpg";
// --- TYPES ---
const ViewState = {
MENU: 'MENU',
GAME: 'GAME',
};
// --- SERVICES ---
const STORAGE_KEY = 'cubes_leaderboard';
const getScores = async () => {
const stored = localStorage.getItem(STORAGE_KEY);
if (!stored) return [];
try {
return JSON.parse(stored);
} catch (e) {
return [];
}
};
const saveScore = async (newScore) => {
const currentScores = await getScores();
const updatedScores = [...currentScores, newScore];
// Sort by time (ascending)
updatedScores.sort((a, b) => a.time - b.time);
// Keep top 20
const top20 = updatedScores.slice(0, 20);
localStorage.setItem(STORAGE_KEY, JSON.stringify(top20));
return top20;
};
// --- COMPONENTS ---
// 1. Leaderboard Component
const Leaderboard = ({ onClose, highlightName }) => {
const [scores, setScores] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchScores = async () => {
const data = await getScores();
setScores(data);
setLoading(false);
};
fetchScores();
}, []);
const formatTime = (ms) => {
const seconds = Math.floor(ms / 1000);
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
};
return (
Tabuľka výsledkov
{loading ? (
Načítavam...
) : scores.length === 0 ? (
Zatiaľ žiadne výsledky. Buď prvý!
) : (
| # |
Meno |
Čas |
{scores.map((score, index) => (
| {index + 1}. |
{score.name}
{index === 0 && 👑}
|
{formatTime(score.time)}
|
))}
)}
);
};
// 2. Main Menu Component
const MainMenu = ({ onNavigate }) => {
return (
{/* Background Image Layer */}

e.target.style.display = 'none'}
alt="Background"
className="w-full h-full object-cover opacity-20"
/>
{/* Content Layer */}
{/* Header Area */}
{/* 6-Grid Layout for Games */}
{/* Game 1: CUBES (Active) */}
{/* Game 2: PONG (PRIPRAVUJEME 2) - Opravený ODKAZ na https://capoli.net/fun/pong/index.html */}
{/* Corner accent (šedý) */}
PONG
{/* Placeholder Slots 3-6 (pre ostávajúce 4 voľné sloty) */}
{Array.from({ length: 4 }).map((_, index) => (
))}
© 2024 Capoli.net
);
};
// 3. Game Component
const CubesGame = ({ onBack }) => {
const GRID_SIZE = 5;
const TILE_COUNT = GRID_SIZE * GRID_SIZE;
const EMPTY_INDEX = TILE_COUNT - 1;
// Game State
const [grid, setGrid] = useState([]);
const [isGameActive, setIsGameActive] = useState(false);
const [isSolved, setIsSolved] = useState(false);
const [moveCount, setMoveCount] = useState(0);
// Timer State
const [startTime, setStartTime] = useState(null);
const [elapsedTime, setElapsedTime] = useState(0);
// Settings
const [currentImageIndex, setCurrentImageIndex] = useState(0);
const [playerName, setPlayerName] = useState('');
const [showLeaderboard, setShowLeaderboard] = useState(false);
const timerRef = useRef(null);
// Initialize ordered grid
const getSolvedGrid = () => Array.from({ length: TILE_COUNT }, (_, i) => i);
// Start/Reset Game
const initGame = useCallback(() => {
const solved = getSolvedGrid();
// Shuffle logic
let tempGrid = [...solved];
let emptyPos = EMPTY_INDEX;
let previousPos = -1;
// Perform 200 random moves
for (let i = 0; i < 200; i++) {
const neighbors = [];
const row = Math.floor(emptyPos / GRID_SIZE);
const col = emptyPos % GRID_SIZE;
if (row > 0) neighbors.push(emptyPos - GRID_SIZE); // Up
if (row < GRID_SIZE - 1) neighbors.push(emptyPos + GRID_SIZE); // Down
if (col > 0) neighbors.push(emptyPos - 1); // Left
if (col < GRID_SIZE - 1) neighbors.push(emptyPos + 1); // Right
const validNeighbors = neighbors.filter(n => n !== previousPos);
if (validNeighbors.length > 0) {
const randomNeighbor = validNeighbors[Math.floor(Math.random() * validNeighbors.length)];
// Swap
tempGrid[emptyPos] = tempGrid[randomNeighbor];
tempGrid[randomNeighbor] = EMPTY_INDEX;
previousPos = emptyPos;
emptyPos = randomNeighbor;
}
}
setGrid(tempGrid);
setIsGameActive(false);
setIsSolved(false);
setMoveCount(0);
setStartTime(null);
setElapsedTime(0);
if (timerRef.current) clearInterval(timerRef.current);
}, []);
useEffect(() => {
initGame();
return () => {
if (timerRef.current) clearInterval(timerRef.current);
};
}, [initGame, currentImageIndex]);
// Timer Effect
useEffect(() => {
if (isGameActive && !isSolved) {
timerRef.current = window.setInterval(() => {
setElapsedTime(Date.now() - (startTime || Date.now()));
}, 100);
} else {
if (timerRef.current) clearInterval(timerRef.current);
}
return () => {
if (timerRef.current) clearInterval(timerRef.current);
};
}, [isGameActive, isSolved, startTime]);
const handleTileClick = (index) => {
if (isSolved) return;
if (!isGameActive) {
setIsGameActive(true);
setStartTime(Date.now());
}
const emptyPos = grid.indexOf(EMPTY_INDEX);
const row = Math.floor(index / GRID_SIZE);
const col = index % GRID_SIZE;
const emptyRow = Math.floor(emptyPos / GRID_SIZE);
const emptyCol = emptyPos % GRID_SIZE;
const isAdjacent =
(Math.abs(row - emptyRow) === 1 && col === emptyCol) ||
(Math.abs(col - emptyCol) === 1 && row === emptyRow);
if (isAdjacent) {
const newGrid = [...grid];
newGrid[emptyPos] = newGrid[index];
newGrid[index] = EMPTY_INDEX;
setGrid(newGrid);
setMoveCount(prev => prev + 1);
// Check win
const isWin = newGrid.every((val, idx) => val === idx);
if (isWin) {
setIsSolved(true);
setIsGameActive(false);
}
}
};
const handleSaveScore = async () => {
if (!playerName.trim()) {
alert("Prosím zadaj meno (max 8 znakov)");
return;
}
await saveScore({
name: playerName.trim().substring(0, 8),
time: elapsedTime,
date: new Date().toISOString()
});
setShowLeaderboard(true);
};
const formatTime = (ms) => {
const seconds = Math.floor(ms / 1000);
const minutes = Math.floor(seconds / 60);
return `${minutes.toString().padStart(2, '0')}:${(seconds % 60).toString().padStart(2, '0')}`;
};
return (
{/* Top Bar */}
CUBES
Ťahy: {moveCount}
{formatTime(elapsedTime)}
{/* Main Game Area */}
{/* The Grid */}
{/* Success Overlay */}
{isSolved && (
)}
{grid.map((tileNumber, index) => {
const originalRow = Math.floor(tileNumber / GRID_SIZE);
const originalCol = tileNumber % GRID_SIZE;
const isBlank = tileNumber === EMPTY_INDEX;
return (
handleTileClick(index)}
className={`
relative w-full h-full overflow-hidden transition-all duration-100 rounded-sm
${isBlank ? 'opacity-0 cursor-default' : 'cursor-pointer hover:brightness-110 shadow-inner'}
`}
style={{ backgroundColor: '#e5e7eb' }}
>
{!isBlank && (
)}
);
})}
{/* Controls & Mini View */}
{/* Preview */}
Predloha

{
// Fallback if image not found
e.target.style.display='none';
e.target.parentElement.innerHTML += '
Obrázok nenájdený.
Nahrajte puzzle1.jpg
';
}}
alt="Target"
className="w-full h-full object-cover"
/>
{/* Controls */}
{PUZZLE_IMAGES.map((img, idx) => (
))}
{showLeaderboard && (
setShowLeaderboard(false)}
highlightName={playerName}
/>
)}
);
};
// --- APP ROOT ---
const App = () => {
const [currentView, setCurrentView] = useState(ViewState.MENU);
return (
// Global Framing (The "Capoli" Frame)
{/* Inner Gold Border */}
{/* Main Content Area */}
{currentView === ViewState.MENU && (
)}
{currentView === ViewState.GAME && (
setCurrentView(ViewState.MENU)} />
)}
);
};
// Mount the App
const rootElement = document.getElementById('root');
if (rootElement) {
const root = ReactDOM.createRoot(rootElement);
root.render();
}