import { defineStore } from 'pinia'; import { ref, computed } from 'vue'; import { generateRandomGrid } from '@/utils/puzzleUtils'; // Definicje zagadek (Static Puzzles) const PUZZLES = { easy: { id: 'easy', name: 'Uśmiech', size: 5, grid: [ [0, 1, 0, 1, 0], [0, 1, 0, 1, 0], [0, 0, 0, 0, 0], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0] ] }, medium: { id: 'medium', name: 'Domek', size: 10, grid: [ [0, 0, 0, 0, 1, 1, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 1, 1, 0, 0], [0, 1, 1, 0, 0, 0, 0, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 1, 1, 1, 1, 0, 0, 1], [1, 0, 0, 1, 0, 0, 1, 0, 0, 1], [1, 0, 0, 1, 1, 1, 1, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1, 1, 1, 1, 0] ] }, hard: { id: 'hard', name: 'Statek', size: 15, grid: [ [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0], [0,0,0,0,0,0,1,1,1,0,0,0,0,0,0], [0,0,0,0,0,1,1,1,1,1,0,0,0,0,0], [0,0,0,0,1,1,1,1,1,1,1,0,0,0,0], [0,0,0,0,0,0,1,1,1,0,0,0,0,0,0], [0,0,0,0,0,0,1,1,1,0,0,0,0,0,0], [0,0,0,0,0,0,1,1,1,0,0,0,0,0,0], [0,0,0,0,0,0,1,1,1,0,0,0,0,0,0], [0,0,0,0,0,0,1,1,1,0,0,0,0,0,0], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], [0,1,1,1,1,1,1,1,1,1,1,1,1,1,0], [0,0,1,1,1,1,1,1,1,1,1,1,1,0,0], [0,0,0,1,1,1,1,1,1,1,1,1,0,0,0], [0,0,0,0,1,1,1,1,1,1,1,0,0,0,0], [0,0,0,0,0,1,1,1,1,1,0,0,0,0,0] ] } }; export const usePuzzleStore = defineStore('puzzle', () => { // State const currentLevelId = ref('easy'); const solution = ref([]); const playerGrid = ref([]); // 0: empty, 1: filled, 2: cross const isGameWon = ref(false); const hasUsedGuide = ref(false); const guideUsageCount = ref(0); const currentDifficulty = ref(null); // 'easy', 'medium', 'hard', 'custom' or object { density: 0.5 } const currentDensity = ref(0); const size = ref(5); const startTime = ref(null); const elapsedTime = ref(0); const moves = ref(0); const timerInterval = ref(null); // History for undo const history = ref([]); // Progress State const totalCellsToFill = computed(() => { return solution.value.flat().filter(c => c === 1).length; }); const filledCorrectly = computed(() => { let count = 0; if (solution.value.length === 0 || playerGrid.value.length === 0) return 0; for (let r = 0; r < size.value; r++) { for (let c = 0; c < size.value; c++) { // Zliczamy tylko poprawne wypełnienia (czarne), // ale w nonogramach postęp to często: (poprawne_czarne - bledne_czarne) / total_czarne // Zróbmy prostą wersję: % poprawnie zaznaczonych czarnych - błędnie zaznaczone czarne if (playerGrid.value[r][c] === 1) { if (solution.value[r][c] === 1) count++; else count--; // kara za błąd } } } return Math.max(0, count); }); const progressPercentage = computed(() => { if (totalCellsToFill.value === 0) return 0; return Math.min(100, (filledCorrectly.value / totalCellsToFill.value) * 100); }); // Actions function initGame(levelId = 'easy') { stopTimer(); currentLevelId.value = levelId; let puzzle = PUZZLES[levelId]; if (!puzzle) { // Fallback or custom logic if needed, but for predefined levels: puzzle = PUZZLES['easy']; } size.value = puzzle.size; solution.value = puzzle.grid; resetGrid(); isGameWon.value = false; hasUsedGuide.value = false; guideUsageCount.value = 0; currentDensity.value = totalCellsToFill.value / (size.value * size.value); elapsedTime.value = 0; startTimer(); saveState(); } function initCustomGame(customSize, density = 0.5) { stopTimer(); currentLevelId.value = 'custom'; size.value = customSize; // Generate random grid solution.value = generateRandomGrid(customSize, density); resetGrid(); isGameWon.value = false; hasUsedGuide.value = false; guideUsageCount.value = 0; currentDensity.value = density; elapsedTime.value = 0; startTimer(); saveState(); } function resetGrid() { playerGrid.value = Array(size.value).fill().map(() => Array(size.value).fill(0)); moves.value = 0; history.value = []; } function pushHistory() { const gridCopy = playerGrid.value.map(row => [...row]); history.value.push(gridCopy); if (history.value.length > 50) history.value.shift(); } function undo() { if (history.value.length === 0 || isGameWon.value) return; const previousState = history.value.pop(); playerGrid.value = previousState; moves.value++; saveState(); } function toggleCell(r, c, isRightClick = false) { if (isGameWon.value) return; pushHistory(); const currentState = playerGrid.value[r][c]; let newState; if (isRightClick) { if (currentState === 1) return; // Don't override filled newState = currentState === 2 ? 0 : 2; } else { if (currentState === 2) return; // Don't override cross newState = currentState === 1 ? 0 : 1; } playerGrid.value[r][c] = newState; // This triggers reactivity moves.value++; checkWin(); saveState(); } function setCell(r, c, state) { if (isGameWon.value) return; if (playerGrid.value[r][c] !== state) { pushHistory(); playerGrid.value[r][c] = state; moves.value++; checkWin(); saveState(); } } function checkWin() { let correct = true; for (let r = 0; r < size.value; r++) { for (let c = 0; c < size.value; c++) { const playerCell = playerGrid.value[r][c]; const solutionCell = solution.value[r][c]; const isFilled = playerCell === 1; const shouldBeFilled = solutionCell === 1; if (isFilled !== shouldBeFilled) { correct = false; break; } } if (!correct) break; } if (correct) { isGameWon.value = true; stopTimer(); } } function startTimer() { if (timerInterval.value) clearInterval(timerInterval.value); startTime.value = Date.now() - (elapsedTime.value * 1000); // Adjust start time based on elapsed timerInterval.value = setInterval(() => { elapsedTime.value = Math.floor((Date.now() - startTime.value) / 1000); saveState(); }, 1000); } function stopTimer() { if (timerInterval.value) { clearInterval(timerInterval.value); timerInterval.value = null; } saveState(); } // Persistence const STORAGE_KEY = 'nonogram_state_v1'; function saveState() { const stateToSave = { currentLevelId: currentLevelId.value, size: size.value, solution: solution.value, playerGrid: playerGrid.value, isGameWon: isGameWon.value, hasUsedGuide: hasUsedGuide.value, guideUsageCount: guideUsageCount.value, currentDensity: currentDensity.value, elapsedTime: elapsedTime.value, moves: moves.value, history: history.value }; localStorage.setItem(STORAGE_KEY, JSON.stringify(stateToSave)); } function loadState() { const saved = localStorage.getItem(STORAGE_KEY); if (saved) { try { const parsed = JSON.parse(saved); currentLevelId.value = parsed.currentLevelId; size.value = parsed.size; solution.value = parsed.solution; playerGrid.value = parsed.playerGrid; isGameWon.value = parsed.isGameWon; hasUsedGuide.value = parsed.hasUsedGuide || false; guideUsageCount.value = parsed.guideUsageCount || 0; currentDensity.value = parsed.currentDensity || 0; elapsedTime.value = parsed.elapsedTime || 0; moves.value = parsed.moves || 0; history.value = parsed.history || []; if (!isGameWon.value) { startTimer(); } return true; } catch (e) { console.error('Failed to load save', e); return false; } } return false; } // Duplicate initGame removed // Duplicate initCustomGame removed // Duplicate toggleCell/setCell removed function resetGame() { if (currentLevelId.value === 'custom') { resetGrid(); isGameWon.value = false; hasUsedGuide.value = false; guideUsageCount.value = 0; elapsedTime.value = 0; startTimer(); saveState(); } else { initGame(currentLevelId.value); } } function markGuideUsed() { if (isGameWon.value) return; hasUsedGuide.value = true; guideUsageCount.value++; saveState(); } function closeWinModal() { if (!isGameWon.value) return; isGameWon.value = false; saveState(); } return { currentLevelId, solution, playerGrid, isGameWon, size, elapsedTime, progressPercentage, initGame, initCustomGame, toggleCell, setCell, resetGame, checkWin, loadState, // expose loadState moves, undo, closeWinModal, hasUsedGuide, guideUsageCount, currentDensity, markGuideUsed }; });