chat

"use client"; import React from "react"; function MainComponent() { const [grid, setGrid] = React.useState([]); const [currentNumber, setCurrentNumber] = React.useState(1); const [gameStarted, setGameStarted] = React.useState(false); const [gameCompleted, setGameCompleted] = React.useState(false); const [gameOver, setGameOver] = React.useState(false); const [selectedCell, setSelectedCell] = React.useState(null); const [validMoves, setValidMoves] = React.useState([]); const [startTime, setStartTime] = React.useState(null); const [completionTime, setCompletionTime] = React.useState(null); const [showNicknameInput, setShowNicknameInput] = React.useState(false); const [nickname, setNickname] = React.useState(""); const [leaderboard, setLeaderboard] = React.useState([]); const [showLeaderboard, setShowLeaderboard] = React.useState(false); const [audioContext, setAudioContext] = React.useState(null); const [backgroundMusic, setBackgroundMusic] = React.useState(null); const [darkMode, setDarkMode] = React.useState(false); const [musicEnabled, setMusicEnabled] = React.useState(true); const [hapticEnabled, setHapticEnabled] = React.useState(true); const [moveHistory, setMoveHistory] = React.useState([]); const [showSettings, setShowSettings] = React.useState(false); const [installPrompt, setInstallPrompt] = React.useState(null); const [isInstallable, setIsInstallable] = React.useState(false); React.useEffect(() => { const manifest = { name: "Centum Grid LM Edition", short_name: "Centum Grid", description: "Un puzzle game logico dove devi completare una griglia 10x10", start_url: "/", display: "standalone", background_color: "#4f46e5", theme_color: "#4f46e5", icons: [ { src: "data:image/svg+xml;base64," + btoa(` LM `), sizes: "192x192", type: "image/svg+xml", }, { src: "data:image/svg+xml;base64," + btoa(` LM `), sizes: "512x512", type: "image/svg+xml", }, ], }; const manifestBlob = new Blob([JSON.stringify(manifest)], { type: "application/json", }); const manifestUrl = URL.createObjectURL(manifestBlob); let manifestLink = document.querySelector('link[rel="manifest"]'); if (!manifestLink) { manifestLink = document.createElement("link"); manifestLink.rel = "manifest"; document.head.appendChild(manifestLink); } manifestLink.href = manifestUrl; const swCode = ` const CACHE_NAME = 'centum-grid-v1'; const urlsToCache = [ '/', '/static/js/bundle.js', '/static/css/main.css' ]; self.addEventListener('install', (event) => { console.log('Service Worker installing...'); event.waitUntil( caches.open(CACHE_NAME) .then((cache) => { console.log('Opened cache'); return cache.addAll(urlsToCache.filter(url => url !== '/static/js/bundle.js' && url !== '/static/css/main.css')); }) .catch(err => { console.log('Cache failed:', err); return Promise.resolve(); }) ); }); self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request) .then((response) => { if (response) { return response; } return fetch(event.request); }) ); }); self.addEventListener('activate', (event) => { console.log('Service Worker activated'); }); `; let swUrl = null; if ("serviceWorker" in navigator) { const swBlob = new Blob([swCode], { type: "application/javascript" }); swUrl = URL.createObjectURL(swBlob); navigator.serviceWorker .register(swUrl) .then((registration) => { console.log("SW registered: ", registration); registration.update(); }) .catch((registrationError) => { console.log("SW registration failed: ", registrationError); }); } const handleBeforeInstallPrompt = (e) => { console.log("beforeinstallprompt event fired"); e.preventDefault(); setInstallPrompt(e); setIsInstallable(true); }; const handleAppInstalled = () => { console.log("appinstalled event fired"); setInstallPrompt(null); setIsInstallable(false); }; window.addEventListener("beforeinstallprompt", handleBeforeInstallPrompt); window.addEventListener("appinstalled", handleAppInstalled); const debugTimer = setTimeout(() => { console.log("Debug: Forcing install button to show"); setIsInstallable(true); }, 3000); return () => { window.removeEventListener( "beforeinstallprompt", handleBeforeInstallPrompt ); window.removeEventListener("appinstalled", handleAppInstalled); clearTimeout(debugTimer); URL.revokeObjectURL(manifestUrl); if (swUrl) { URL.revokeObjectURL(swUrl); } }; }, []); const handleInstallClick = async () => { if (!installPrompt) return; const result = await installPrompt.prompt(); console.log("Install prompt result:", result); setInstallPrompt(null); setIsInstallable(false); }; const initializeGrid = React.useCallback(() => { const newGrid = []; for (let i = 0; i < 10; i++) { const row = []; for (let j = 0; j < 10; j++) { row.push({ number: null, row: i, col: j, filled: false, }); } newGrid.push(row); } setGrid(newGrid); }, []); const calculateValidMoves = React.useCallback((row, col, currentGrid) => { const moves = []; const directions = [ [-3, 0], [3, 0], [0, -3], [0, 3], ]; const diagonalDirections = [ [-2, -2], [-2, 2], [2, -2], [2, 2], ]; [...directions, ...diagonalDirections].forEach(([dRow, dCol]) => { const newRow = row + dRow; const newCol = col + dCol; if (newRow >= 0 && newRow < 10 && newCol >= 0 && newCol < 10) { if (!currentGrid[newRow][newCol].filled) { moves.push({ row: newRow, col: newCol }); } } }); return moves; }, []); const triggerHaptic = React.useCallback(() => { if (hapticEnabled && navigator.vibrate) { navigator.vibrate(50); } }, [hapticEnabled]); React.useEffect(() => { const saved = localStorage.getItem("centum-leaderboard"); if (saved) { setLeaderboard(JSON.parse(saved)); } const savedSettings = localStorage.getItem("centum-settings"); if (savedSettings) { const settings = JSON.parse(savedSettings); setDarkMode(settings.darkMode || false); setMusicEnabled(settings.musicEnabled !== false); setHapticEnabled(settings.hapticEnabled !== false); } initializeGrid(); }, [initializeGrid]); React.useEffect(() => { const settings = { darkMode, musicEnabled, hapticEnabled }; localStorage.setItem("centum-settings", JSON.stringify(settings)); }, [darkMode, musicEnabled, hapticEnabled]); React.useEffect(() => { const setupAudio = async () => { try { const ctx = new (window.AudioContext || window.webkitAudioContext)(); setAudioContext(ctx); const createTone = (frequency, duration, startTime) => { const oscillator = ctx.createOscillator(); const gainNode = ctx.createGain(); oscillator.connect(gainNode); gainNode.connect(ctx.destination); oscillator.frequency.setValueAtTime(frequency, startTime); oscillator.type = "sine"; gainNode.gain.setValueAtTime(0, startTime); gainNode.gain.linearRampToValueAtTime(0.08, startTime + 0.1); gainNode.gain.linearRampToValueAtTime( 0.04, startTime + duration - 0.1 ); gainNode.gain.linearRampToValueAtTime(0, startTime + duration); oscillator.start(startTime); oscillator.stop(startTime + duration); }; const playBackgroundMusic = () => { if (!musicEnabled || ctx.state === "suspended") { return; } if (ctx.state === "suspended") { ctx.resume(); } const melody = [ 261.63, 293.66, 329.63, 349.23, 392.0, 440.0, 493.88, 523.25, ]; const currentTime = ctx.currentTime; melody.forEach((freq, index) => { createTone(freq, 0.6, currentTime + index * 0.7); }); if (musicEnabled) { setTimeout(playBackgroundMusic, 6000); } }; setBackgroundMusic({ play: playBackgroundMusic, context: ctx }); } catch (error) { console.error("Audio setup failed:", error); } }; setupAudio(); }, [musicEnabled]); const handleCellClick = React.useCallback( (cell) => { if (gameCompleted || gameOver) return; if (!gameStarted) { setGameStarted(true); setStartTime(Date.now()); if (backgroundMusic && musicEnabled) { backgroundMusic.play(); } } if (currentNumber === 1 && !cell.filled) { const newGrid = grid.map((row) => row.map((c) => c.row === cell.row && c.col === cell.col ? { ...c, number: 1, filled: true } : c ) ); setGrid(newGrid); setCurrentNumber(2); setSelectedCell({ row: cell.row, col: cell.col }); setMoveHistory([ { grid: JSON.parse(JSON.stringify(grid)), currentNumber: 1, selectedCell: null, validMoves: [], }, ]); const moves = calculateValidMoves(cell.row, cell.col, newGrid); setValidMoves(moves); triggerHaptic(); return; } if (currentNumber > 1 && !cell.filled) { const isValidMove = validMoves.some( (move) => move.row === cell.row && move.col === cell.col ); if (isValidMove) { setMoveHistory((prev) => [ ...prev, { grid: JSON.parse(JSON.stringify(grid)), currentNumber, selectedCell, validMoves: [...validMoves], }, ]); const newGrid = grid.map((row) => row.map((c) => c.row === cell.row && c.col === cell.col ? { ...c, number: currentNumber, filled: true } : c ) ); setGrid(newGrid); setSelectedCell({ row: cell.row, col: cell.col }); if (currentNumber === 100) { const endTime = Date.now(); const timeTaken = Math.floor((endTime - startTime) / 1000); setCompletionTime(timeTaken); setGameCompleted(true); setShowNicknameInput(true); setValidMoves([]); triggerHaptic(); } else { const nextNumber = currentNumber + 1; setCurrentNumber(nextNumber); const moves = calculateValidMoves(cell.row, cell.col, newGrid); setValidMoves(moves); if (moves.length === 0) { setGameOver(true); } triggerHaptic(); } } } }, [ gameStarted, gameCompleted, gameOver, currentNumber, grid, validMoves, calculateValidMoves, startTime, backgroundMusic, musicEnabled, triggerHaptic, selectedCell, ] ); const undoMove = React.useCallback(() => { if (moveHistory.length === 0 || gameCompleted || gameOver) return; const lastState = moveHistory[moveHistory.length - 1]; setGrid(lastState.grid); setCurrentNumber(lastState.currentNumber); setSelectedCell(lastState.selectedCell); setValidMoves(lastState.validMoves); setMoveHistory((prev) => prev.slice(0, -1)); triggerHaptic(); }, [moveHistory, gameCompleted, gameOver, triggerHaptic]); const saveScore = React.useCallback(() => { if (nickname.trim() && completionTime) { const newScore = { nickname: nickname.trim(), time: completionTime, moves: currentNumber - 1, date: new Date().toLocaleDateString(), }; const updatedLeaderboard = [...leaderboard, newScore] .sort((a, b) => { if (a.moves === 100 && b.moves === 100) { return a.time - b.time; } return b.moves - a.moves; }) .slice(0, 10); setLeaderboard(updatedLeaderboard); localStorage.setItem( "centum-leaderboard", JSON.stringify(updatedLeaderboard) ); setShowNicknameInput(false); setShowLeaderboard(true); } }, [nickname, completionTime, currentNumber, leaderboard]); const resetGame = React.useCallback(() => { setCurrentNumber(1); setGameStarted(false); setGameCompleted(false); setGameOver(false); setSelectedCell(null); setValidMoves([]); setStartTime(null); setCompletionTime(null); setShowNicknameInput(false); setShowLeaderboard(false); setNickname(""); setMoveHistory([]); initializeGrid(); }, [initializeGrid]); const formatTime = (seconds) => { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins}:${secs.toString().padStart(2, "0")}`; }; const progress = ((currentNumber - 1) / 100) * 100; const themeClasses = darkMode ? { bg: "bg-gradient-to-br from-gray-900 to-gray-800", cardBg: "bg-gray-800", text: "text-white", textSecondary: "text-gray-300", textAccent: "text-blue-400", border: "border-gray-600", cellEmpty: "bg-gray-700 text-gray-400 hover:bg-gray-600", cellFilled: "bg-green-600 text-white", cellValid: "bg-yellow-500 text-gray-900 ring-yellow-400", cellLast: "bg-blue-600 text-white ring-blue-400", } : { bg: "bg-gradient-to-br from-blue-50 to-indigo-100", cardBg: "bg-white", text: "text-gray-900", textSecondary: "text-gray-600", textAccent: "text-indigo-800", border: "border-gray-300", cellEmpty: "bg-gray-100 text-gray-400 hover:bg-gray-200", cellFilled: "bg-green-500 text-white", cellValid: "bg-yellow-200 text-gray-800 ring-yellow-400", cellLast: "bg-blue-500 text-white ring-blue-300", }; return (
LM
{Array.from({ length: 9 }).map((_, i) => (
))}

Centum Grid

LM Edition

{currentNumber}
Prossima mossa
{currentNumber - 1}/100
Mosse completate
{validMoves.length}
Mosse possibili
{!gameStarted && (

Regole del gioco:

  • • Clicca una cella per iniziare con il numero 1
  • • Orizzontalmente/verticalmente: salta 2 celle
  • • Diagonalmente: salta 1 cella
  • • Completa tutte le 100 mosse o fino a quando non ci sono più mosse possibili
)}
{gameOver && !gameCompleted && (

Game Over! Nessuna mossa possibile. Hai raggiunto{" "} {currentNumber - 1} mosse!

)}
{grid.map((row, rowIndex) => row.map((cell, colIndex) => { const isValidMove = validMoves.some( (move) => move.row === cell.row && move.col === cell.col ); const isLastPlaced = selectedCell && selectedCell.row === cell.row && selectedCell.col === cell.col; let cellClass = ""; if (cell.filled) { cellClass = isLastPlaced ? themeClasses.cellLast : themeClasses.cellFilled; } else if (isValidMove && !gameCompleted && !gameOver) { cellClass = themeClasses.cellValid; } else if (currentNumber === 1 && !gameStarted) { cellClass = `${themeClasses.cellEmpty} hover:text-gray-600`; } else { cellClass = themeClasses.cellEmpty; } return (
{showSettings && (

Impostazioni
Modalità scura
Musica di sottofondo

Feedback tattile
)} {showLeaderboard && (

Classifica {leaderboard.length === 0 ? (

Nessun punteggio ancora

) : (
{leaderboard.map((score, index) => (
#{index + 1} {score.nickname}
{score.moves}/100
{formatTime(score.completion_time || score.time)} •{" "} {new Date( score.date_played || score.date ).toLocaleDateString()}
))}

)}
{validMoves.length > 0 && gameStarted && !gameCompleted && !gameOver && (

{validMoves.length}{" "} {validMoves.length === 1 ? "mossa possibile" : "mosse possibili"}

)}
); } export default MainComponent;
Nessun post.
Nessun post.