4 Commits

Author SHA1 Message Date
a926727b51 Merge branch 'main' of gitea.7u.pl:gkucmierz/nonograms
Some checks failed
Deploy to Production / deploy (push) Failing after 22s
2026-02-19 06:56:10 +01:00
4782d20493 chore: bump version and fix boost button 2026-02-19 06:54:25 +01:00
182658774e Delete index.html
Some checks failed
Deploy to Production / deploy (push) Failing after 4s
2026-02-19 05:50:00 +00:00
635fdb089d Delete update_i18n_guide.cjs
All checks were successful
Deploy to Production / deploy (push) Successful in 7s
2026-02-19 05:49:09 +00:00
8 changed files with 76 additions and 82 deletions

View File

@@ -1,15 +0,0 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/nonograms.svg" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="mask-icon" href="/nonograms.svg" color="#00f2fe" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<title>Nonograms Pro - Vue 3 SOLID</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "vue-nonograms-solid", "name": "vue-nonograms-solid",
"version": "1.15.0", "version": "1.15.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "vue-nonograms-solid", "name": "vue-nonograms-solid",
"version": "1.15.0", "version": "1.15.1",
"dependencies": { "dependencies": {
"@capacitor/android": "^8.1.0", "@capacitor/android": "^8.1.0",
"@capacitor/cli": "^8.1.0", "@capacitor/cli": "^8.1.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "vue-nonograms-solid", "name": "vue-nonograms-solid",
"version": "1.15.0", "version": "1.15.1",
"homepage": "https://nonograms.7u.pl/", "homepage": "https://nonograms.7u.pl/",
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@@ -1,4 +1,4 @@
import { ref, computed, onUnmounted } from 'vue'; import { ref, computed, onUnmounted, watch } from 'vue';
import { usePuzzleStore } from '@/stores/puzzle'; import { usePuzzleStore } from '@/stores/puzzle';
import { useI18n } from '@/composables/useI18n'; import { useI18n } from '@/composables/useI18n';
@@ -18,6 +18,24 @@ export function useSolver() {
let worker = null; let worker = null;
let requestId = 0; let requestId = 0;
// Reset solver state when game resets or changes
watch(() => store.currentLevelId, () => {
resetSolverState();
});
watch(() => store.moves, (newVal) => {
if (newVal === 0) {
resetSolverState();
}
});
function resetSolverState() {
pause();
isStuck.value = false;
statusText.value = t('guide.waiting');
isProcessing.value = false;
}
function step() { function step() {
if (store.isGameWon) { if (store.isGameWon) {
pause(); pause();

View File

@@ -432,6 +432,9 @@ export const usePuzzleStore = defineStore('puzzle', () => {
guideUsageCount, guideUsageCount,
currentDensity, currentDensity,
markGuideUsed, markGuideUsed,
markBoostUsed,
hasUsedBoost,
boostUsageCount,
startInteraction, startInteraction,
endInteraction, endInteraction,
completedRows, completedRows,

View File

@@ -446,6 +446,7 @@ export function solvePuzzle(rowHints, colHints, onProgress, initialGrid = null,
lookaheadUsed: maxDepth > 0, lookaheadUsed: maxDepth > 0,
iterations, iterations,
maxDepth, maxDepth,
backtracks backtracks,
solution: grid
}; };
} }

View File

@@ -1,5 +1,5 @@
import { calculateHints } from '../utils/puzzleUtils.js'; import { calculateHints } from '../utils/puzzleUtils.js';
import { solveLine } from '../utils/solver.js'; import { solveLine, solvePuzzle } from '../utils/solver.js';
const messages = { const messages = {
pl: { pl: {
@@ -140,7 +140,54 @@ const handleStep = (playerGrid, solution, locale) => {
const handleBoost = (playerGrid, solution, locale) => { const handleBoost = (playerGrid, solution, locale) => {
const size = solution.length; const size = solution.length;
// Find first unknown cell and reveal it
// 1. Try to use the Solver (DFS) to find a logical move
try {
const { rowHints, colHints } = calculateHints(solution);
// Map Store format (0=Unk, 1=Fill, 2=Cross) to Solver format (-1=Unk, 1=Fill, 0=Empty)
const solverGrid = playerGrid.map(row => row.map(cell => {
if (cell === 0) return -1;
if (cell === 1) return 1;
if (cell === 2) return 0;
return -1;
}));
// Run full solver (logicOnly=false allows DFS/guessing)
// We pass solverGrid as initial state to respect user's moves
const result = solvePuzzle(rowHints, colHints, null, solverGrid, false);
if (result && result.solution) {
const solvedGrid = result.solution;
// Find the first cell that is Unknown in playerGrid but Known in solvedGrid
for (let r = 0; r < size; r++) {
for (let c = 0; c < size; c++) {
if (playerGrid[r][c] === 0) { // Unknown in Player
const solvedVal = solvedGrid[r][c]; // -1=Unk, 0=Empty, 1=Filled
if (solvedVal !== -1) {
// Found a logical deduction!
const newState = solvedVal === 1 ? 1 : 2; // 1->Filled, 0->Cross
const stateLabel = t(locale, newState === 1 ? 'worker.state.filled' : 'worker.state.empty');
return {
type: 'move',
r,
c,
state: newState,
statusText: t(locale, 'worker.boosted', { row: r + 1, col: c + 1, state: stateLabel })
};
}
}
}
}
}
} catch (e) {
console.warn('Boost Solver failed, falling back to simple reveal:', e);
}
// 2. Fallback: If solver failed (e.g. contradiction due to user error),
// or no new info found, use the "Cheat" method (reveal from true solution).
for (let r = 0; r < size; r++) { for (let r = 0; r < size; r++) {
for (let c = 0; c < size; c++) { for (let c = 0; c < size; c++) {
if (playerGrid[r][c] === 0) { if (playerGrid[r][c] === 0) {

View File

@@ -1,60 +0,0 @@
const fs = require('fs');
const filePath = 'src/composables/useI18n.js';
let content = fs.readFileSync(filePath, 'utf8');
// 1. Add key to all language objects
const lines = content.split('\n');
const newLines = [];
let insideLang = false;
let currentLang = null;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const langStartMatch = line.match(/^\s{2}(['"]?[\w-]+['"]?): \{/);
if (langStartMatch) {
insideLang = true;
currentLang = langStartMatch[1].replace(/['"]/g, '');
}
if (insideLang && (line.trim() === '},' || line.trim() === '}')) {
let translation = 'GUIDE';
if (currentLang === 'pl') translation = 'PRZEWODNIK';
if (currentLang === 'es') translation = 'GUÍA';
if (currentLang === 'fr') translation = 'GUIDE';
if (currentLang === 'de') translation = 'ANLEITUNG';
if (currentLang === 'it') translation = 'GUIDA';
if (currentLang === 'pt' || currentLang === 'pt-br') translation = 'GUIA';
if (currentLang === 'ru') translation = 'РУКОВОДСТВО';
if (currentLang === 'zh') translation = '指南';
// Ensure previous line has comma
if (newLines.length > 0) {
const lastLine = newLines[newLines.length - 1];
if (!lastLine.trim().endsWith(',') && !lastLine.trim().endsWith('{')) {
newLines[newLines.length - 1] = lastLine + ',';
}
}
newLines.push(` 'nav.guide': '${translation}'`);
insideLang = false;
currentLang = null;
}
newLines.push(line);
}
content = newLines.join('\n');
// 2. Add to requiredKeys
// Find "const requiredKeys = ["
// We know it ends with 'nav.newGame' now.
content = content.replace(
"'nav.newGame'",
"'nav.newGame','nav.guide'"
);
fs.writeFileSync(filePath, content);
console.log('Updated useI18n.js with nav.guide');