Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
18568fa4ae
|
|||
|
c3188bb740
|
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-nonograms-solid",
|
"name": "vue-nonograms-solid",
|
||||||
"version": "1.14.1",
|
"version": "1.14.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "vue-nonograms-solid",
|
"name": "vue-nonograms-solid",
|
||||||
"version": "1.14.1",
|
"version": "1.14.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fireworks-js": "^2.10.8",
|
"fireworks-js": "^2.10.8",
|
||||||
"flag-icons": "^7.5.0",
|
"flag-icons": "^7.5.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-nonograms-solid",
|
"name": "vue-nonograms-solid",
|
||||||
"version": "1.14.1",
|
"version": "1.14.2",
|
||||||
"homepage": "https://nonograms.7u.pl/",
|
"homepage": "https://nonograms.7u.pl/",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import { useI18n } from '@/composables/useI18n';
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
isPlaying,
|
isPlaying,
|
||||||
|
isStuck,
|
||||||
speedLabel,
|
speedLabel,
|
||||||
statusText,
|
statusText,
|
||||||
step,
|
step,
|
||||||
togglePlay,
|
togglePlay,
|
||||||
changeSpeed
|
changeSpeed,
|
||||||
|
boost
|
||||||
} = useSolver();
|
} = useSolver();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
</script>
|
</script>
|
||||||
@@ -29,6 +31,10 @@ const { t } = useI18n();
|
|||||||
<button class="btn-neon small" @click="changeSpeed">
|
<button class="btn-neon small" @click="changeSpeed">
|
||||||
{{ t('guide.speed') }}: {{ speedLabel }}
|
{{ t('guide.speed') }}: {{ speedLabel }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button v-if="isStuck" class="btn-neon small boost-btn" @click="boost">
|
||||||
|
⚡ Boost (DFS)
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -68,4 +74,15 @@ const { t } = useI18n();
|
|||||||
padding: 5px 15px;
|
padding: 5px 15px;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.boost-btn {
|
||||||
|
border-color: #ffd700;
|
||||||
|
color: #ffd700;
|
||||||
|
box-shadow: 0 0 10px rgba(255, 215, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.boost-btn:hover {
|
||||||
|
background: rgba(255, 215, 0, 0.1);
|
||||||
|
box-shadow: 0 0 15px rgba(255, 215, 0, 0.4);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -47,7 +47,9 @@ const getShareData = () => ({
|
|||||||
grid: store.playerGrid,
|
grid: store.playerGrid,
|
||||||
size: store.size,
|
size: store.size,
|
||||||
currentDensity: store.currentDensity,
|
currentDensity: store.currentDensity,
|
||||||
guideUsageCount: store.guideUsageCount
|
guideUsageCount: store.guideUsageCount,
|
||||||
|
hasUsedBoost: store.hasUsedBoost,
|
||||||
|
boostUsageCount: store.boostUsageCount
|
||||||
});
|
});
|
||||||
|
|
||||||
const downloadShareSVG = () => {
|
const downloadShareSVG = () => {
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ const messages = {
|
|||||||
'win.shareDownload': 'Pobierz zrzut',
|
'win.shareDownload': 'Pobierz zrzut',
|
||||||
'win.difficulty': 'Poziom:',
|
'win.difficulty': 'Poziom:',
|
||||||
'win.usedGuide': 'Podpowiedzi: {percent}% ({count})',
|
'win.usedGuide': 'Podpowiedzi: {percent}% ({count})',
|
||||||
|
'win.boosted': 'Wspomagany (DFS)',
|
||||||
'pwa.installTitle': 'Zainstaluj aplikację i graj offline',
|
'pwa.installTitle': 'Zainstaluj aplikację i graj offline',
|
||||||
'pwa.installMobile': 'Dodaj do ekranu głównego',
|
'pwa.installMobile': 'Dodaj do ekranu głównego',
|
||||||
'pwa.installDesktop': 'Zainstaluj na komputerze',
|
'pwa.installDesktop': 'Zainstaluj na komputerze',
|
||||||
@@ -244,6 +245,7 @@ const messages = {
|
|||||||
'win.shareDownload': 'Download screenshot',
|
'win.shareDownload': 'Download screenshot',
|
||||||
'win.difficulty': 'Difficulty:',
|
'win.difficulty': 'Difficulty:',
|
||||||
'win.usedGuide': 'Hints: {percent}% ({count})',
|
'win.usedGuide': 'Hints: {percent}% ({count})',
|
||||||
|
'win.boosted': 'Boosted (DFS)',
|
||||||
'pwa.installTitle': 'Install the app and play offline',
|
'pwa.installTitle': 'Install the app and play offline',
|
||||||
'pwa.installMobile': 'Add to home screen',
|
'pwa.installMobile': 'Add to home screen',
|
||||||
'pwa.installDesktop': 'Install on desktop',
|
'pwa.installDesktop': 'Install on desktop',
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export function useSolver() {
|
|||||||
|
|
||||||
const isPlaying = ref(false);
|
const isPlaying = ref(false);
|
||||||
const isProcessing = ref(false);
|
const isProcessing = ref(false);
|
||||||
|
const isStuck = ref(false);
|
||||||
const speedIndex = ref(0);
|
const speedIndex = ref(0);
|
||||||
const speeds = [1000, 500, 250, 125, 62, 31, 16];
|
const speeds = [1000, 500, 250, 125, 62, 31, 16];
|
||||||
const speedLabels = ['x1', 'x2', 'x4', 'x8', 'x16', 'x32', 'x64'];
|
const speedLabels = ['x1', 'x2', 'x4', 'x8', 'x16', 'x32', 'x64'];
|
||||||
@@ -25,6 +26,7 @@ export function useSolver() {
|
|||||||
}
|
}
|
||||||
if (isProcessing.value) return;
|
if (isProcessing.value) return;
|
||||||
store.markGuideUsed();
|
store.markGuideUsed();
|
||||||
|
isStuck.value = false;
|
||||||
ensureWorker();
|
ensureWorker();
|
||||||
isProcessing.value = true;
|
isProcessing.value = true;
|
||||||
|
|
||||||
@@ -62,6 +64,19 @@ export function useSolver() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function boost() {
|
||||||
|
if (store.isGameWon || isProcessing.value) return;
|
||||||
|
store.markBoostUsed();
|
||||||
|
isStuck.value = false;
|
||||||
|
ensureWorker();
|
||||||
|
isProcessing.value = true;
|
||||||
|
|
||||||
|
const playerGrid = store.playerGrid.map(row => row.slice());
|
||||||
|
const solution = store.solution.map(row => row.slice());
|
||||||
|
const id = ++requestId;
|
||||||
|
worker.postMessage({ id, playerGrid, solution, locale: locale.value, action: 'boost' });
|
||||||
|
}
|
||||||
|
|
||||||
function ensureWorker() {
|
function ensureWorker() {
|
||||||
if (worker) return;
|
if (worker) return;
|
||||||
worker = new Worker(new URL('../workers/solverWorker.js', import.meta.url), { type: 'module' });
|
worker = new Worker(new URL('../workers/solverWorker.js', import.meta.url), { type: 'module' });
|
||||||
@@ -71,15 +86,18 @@ export function useSolver() {
|
|||||||
if (type === 'move') {
|
if (type === 'move') {
|
||||||
store.setCell(r, c, state);
|
store.setCell(r, c, state);
|
||||||
isProcessing.value = false;
|
isProcessing.value = false;
|
||||||
|
isStuck.value = false;
|
||||||
if (store.isGameWon) {
|
if (store.isGameWon) {
|
||||||
pause();
|
pause();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (type === 'done') {
|
} else if (type === 'done') {
|
||||||
isProcessing.value = false;
|
isProcessing.value = false;
|
||||||
|
isStuck.value = false;
|
||||||
pause();
|
pause();
|
||||||
} else if (type === 'stuck') {
|
} else if (type === 'stuck') {
|
||||||
isProcessing.value = false;
|
isProcessing.value = false;
|
||||||
|
isStuck.value = true;
|
||||||
pause();
|
pause();
|
||||||
} else {
|
} else {
|
||||||
isProcessing.value = false;
|
isProcessing.value = false;
|
||||||
@@ -95,11 +113,13 @@ export function useSolver() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
isPlaying,
|
isPlaying,
|
||||||
|
isStuck,
|
||||||
speedIndex,
|
speedIndex,
|
||||||
speedLabel: computed(() => speedLabels[speedIndex.value]),
|
speedLabel: computed(() => speedLabels[speedIndex.value]),
|
||||||
statusText,
|
statusText,
|
||||||
step,
|
step,
|
||||||
togglePlay,
|
togglePlay,
|
||||||
changeSpeed
|
changeSpeed,
|
||||||
|
boost
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export const usePuzzleStore = defineStore('puzzle', () => {
|
|||||||
const isGameWon = ref(false);
|
const isGameWon = ref(false);
|
||||||
const hasUsedGuide = ref(false);
|
const hasUsedGuide = ref(false);
|
||||||
const guideUsageCount = ref(0);
|
const guideUsageCount = ref(0);
|
||||||
|
const hasUsedBoost = ref(false);
|
||||||
|
const boostUsageCount = ref(0);
|
||||||
const currentDifficulty = ref(null); // 'easy', 'medium', 'hard', 'custom' or object { density: 0.5 }
|
const currentDifficulty = ref(null); // 'easy', 'medium', 'hard', 'custom' or object { density: 0.5 }
|
||||||
const currentDensity = ref(0);
|
const currentDensity = ref(0);
|
||||||
const size = ref(5);
|
const size = ref(5);
|
||||||
@@ -72,6 +74,8 @@ export const usePuzzleStore = defineStore('puzzle', () => {
|
|||||||
isGameWon.value = false;
|
isGameWon.value = false;
|
||||||
hasUsedGuide.value = false;
|
hasUsedGuide.value = false;
|
||||||
guideUsageCount.value = 0;
|
guideUsageCount.value = 0;
|
||||||
|
hasUsedBoost.value = false;
|
||||||
|
boostUsageCount.value = 0;
|
||||||
currentDensity.value = totalCellsToFill.value / (size.value * size.value);
|
currentDensity.value = totalCellsToFill.value / (size.value * size.value);
|
||||||
elapsedTime.value = 0;
|
elapsedTime.value = 0;
|
||||||
startTimer();
|
startTimer();
|
||||||
@@ -90,6 +94,8 @@ export const usePuzzleStore = defineStore('puzzle', () => {
|
|||||||
isGameWon.value = false;
|
isGameWon.value = false;
|
||||||
hasUsedGuide.value = false;
|
hasUsedGuide.value = false;
|
||||||
guideUsageCount.value = 0;
|
guideUsageCount.value = 0;
|
||||||
|
hasUsedBoost.value = false;
|
||||||
|
boostUsageCount.value = 0;
|
||||||
currentDensity.value = density;
|
currentDensity.value = density;
|
||||||
elapsedTime.value = 0;
|
elapsedTime.value = 0;
|
||||||
startTimer();
|
startTimer();
|
||||||
@@ -110,6 +116,8 @@ export const usePuzzleStore = defineStore('puzzle', () => {
|
|||||||
isGameWon.value = false;
|
isGameWon.value = false;
|
||||||
hasUsedGuide.value = false;
|
hasUsedGuide.value = false;
|
||||||
guideUsageCount.value = 0;
|
guideUsageCount.value = 0;
|
||||||
|
hasUsedBoost.value = false;
|
||||||
|
boostUsageCount.value = 0;
|
||||||
|
|
||||||
// Calculate density
|
// Calculate density
|
||||||
const totalFilled = grid.flat().filter(c => c === 1).length;
|
const totalFilled = grid.flat().filter(c => c === 1).length;
|
||||||
@@ -256,6 +264,17 @@ export const usePuzzleStore = defineStore('puzzle', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (correct) {
|
if (correct) {
|
||||||
|
// Auto-fill remaining empty cells with X (2)
|
||||||
|
const rows = solution.value.length;
|
||||||
|
const cols = solution.value[0].length;
|
||||||
|
for (let r = 0; r < rows; r++) {
|
||||||
|
for (let c = 0; c < cols; c++) {
|
||||||
|
if (playerGrid.value[r][c] === 0) {
|
||||||
|
playerGrid.value[r][c] = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isGameWon.value = true;
|
isGameWon.value = true;
|
||||||
stopTimer();
|
stopTimer();
|
||||||
}
|
}
|
||||||
@@ -290,6 +309,8 @@ export const usePuzzleStore = defineStore('puzzle', () => {
|
|||||||
isGameWon: isGameWon.value,
|
isGameWon: isGameWon.value,
|
||||||
hasUsedGuide: hasUsedGuide.value,
|
hasUsedGuide: hasUsedGuide.value,
|
||||||
guideUsageCount: guideUsageCount.value,
|
guideUsageCount: guideUsageCount.value,
|
||||||
|
hasUsedBoost: hasUsedBoost.value,
|
||||||
|
boostUsageCount: boostUsageCount.value,
|
||||||
currentDensity: currentDensity.value,
|
currentDensity: currentDensity.value,
|
||||||
elapsedTime: elapsedTime.value,
|
elapsedTime: elapsedTime.value,
|
||||||
moves: moves.value,
|
moves: moves.value,
|
||||||
@@ -310,6 +331,8 @@ export const usePuzzleStore = defineStore('puzzle', () => {
|
|||||||
isGameWon.value = parsed.isGameWon;
|
isGameWon.value = parsed.isGameWon;
|
||||||
hasUsedGuide.value = parsed.hasUsedGuide || false;
|
hasUsedGuide.value = parsed.hasUsedGuide || false;
|
||||||
guideUsageCount.value = parsed.guideUsageCount || 0;
|
guideUsageCount.value = parsed.guideUsageCount || 0;
|
||||||
|
hasUsedBoost.value = parsed.hasUsedBoost || false;
|
||||||
|
boostUsageCount.value = parsed.boostUsageCount || 0;
|
||||||
currentDensity.value = parsed.currentDensity || 0;
|
currentDensity.value = parsed.currentDensity || 0;
|
||||||
elapsedTime.value = parsed.elapsedTime || 0;
|
elapsedTime.value = parsed.elapsedTime || 0;
|
||||||
moves.value = parsed.moves || 0;
|
moves.value = parsed.moves || 0;
|
||||||
@@ -348,6 +371,13 @@ export const usePuzzleStore = defineStore('puzzle', () => {
|
|||||||
saveState();
|
saveState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function markBoostUsed() {
|
||||||
|
if (isGameWon.value) return;
|
||||||
|
hasUsedBoost.value = true;
|
||||||
|
boostUsageCount.value++;
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
|
||||||
function closeWinModal() {
|
function closeWinModal() {
|
||||||
if (!isGameWon.value) return;
|
if (!isGameWon.value) return;
|
||||||
isGameWon.value = false;
|
isGameWon.value = false;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { calculateDifficulty } from '@/utils/puzzleUtils';
|
import { calculateDifficulty } from '@/utils/puzzleUtils';
|
||||||
|
|
||||||
export function buildShareCanvas(data, t, formattedTime) {
|
export function buildShareCanvas(data, t, formattedTime) {
|
||||||
const { grid, size, currentDensity, guideUsageCount } = data;
|
const { grid, size, currentDensity, guideUsageCount, hasUsedBoost } = data;
|
||||||
if (!grid || !grid.length) return null;
|
if (!grid || !grid.length) return null;
|
||||||
|
|
||||||
const appUrl = typeof __APP_HOMEPAGE__ !== 'undefined' ? __APP_HOMEPAGE__ : '';
|
const appUrl = typeof __APP_HOMEPAGE__ !== 'undefined' ? __APP_HOMEPAGE__ : '';
|
||||||
@@ -11,7 +11,7 @@ export function buildShareCanvas(data, t, formattedTime) {
|
|||||||
const padding = 28;
|
const padding = 28;
|
||||||
const headerHeight = 64;
|
const headerHeight = 64;
|
||||||
const footerHeight = 28;
|
const footerHeight = 28;
|
||||||
const infoHeight = 40; // New space for difficulty/guide info
|
const infoHeight = (guideUsageCount > 0 && hasUsedBoost) ? 65 : 40;
|
||||||
const width = boardSize + padding * 2;
|
const width = boardSize + padding * 2;
|
||||||
const height = boardSize + padding * 2 + headerHeight + footerHeight + infoHeight;
|
const height = boardSize + padding * 2 + headerHeight + footerHeight + infoHeight;
|
||||||
const scale = window.devicePixelRatio || 1;
|
const scale = window.devicePixelRatio || 1;
|
||||||
@@ -97,7 +97,12 @@ export function buildShareCanvas(data, t, formattedTime) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guide Usage Info (Dirty Flag)
|
// Guide Usage & Boost Info
|
||||||
|
let infoY = height - padding - footerHeight + 10;
|
||||||
|
if (guideUsageCount > 0 && hasUsedBoost) {
|
||||||
|
infoY -= 25;
|
||||||
|
}
|
||||||
|
|
||||||
if (guideUsageCount > 0) {
|
if (guideUsageCount > 0) {
|
||||||
ctx.fillStyle = '#ff4d4d';
|
ctx.fillStyle = '#ff4d4d';
|
||||||
ctx.font = '600 14px "Segoe UI", sans-serif';
|
ctx.font = '600 14px "Segoe UI", sans-serif';
|
||||||
@@ -106,7 +111,15 @@ export function buildShareCanvas(data, t, formattedTime) {
|
|||||||
const percent = Math.min(100, Math.round((guideUsageCount / totalCells) * 100));
|
const percent = Math.min(100, Math.round((guideUsageCount / totalCells) * 100));
|
||||||
const guideText = t('win.usedGuide', { count: guideUsageCount, percent });
|
const guideText = t('win.usedGuide', { count: guideUsageCount, percent });
|
||||||
|
|
||||||
ctx.fillText(`⚠️ ${guideText}`, padding, height - padding - footerHeight + 10);
|
ctx.fillText(`⚠️ ${guideText}`, padding, infoY);
|
||||||
|
if (hasUsedBoost) infoY += 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasUsedBoost) {
|
||||||
|
ctx.fillStyle = '#ffd700';
|
||||||
|
ctx.font = '600 14px "Segoe UI", sans-serif';
|
||||||
|
const boostText = t('win.boosted');
|
||||||
|
ctx.fillText(`⚡ ${boostText}`, padding, infoY);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.75)';
|
ctx.fillStyle = 'rgba(255, 255, 255, 0.75)';
|
||||||
@@ -116,7 +129,7 @@ export function buildShareCanvas(data, t, formattedTime) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function buildShareSVG(data, t, formattedTime) {
|
export function buildShareSVG(data, t, formattedTime) {
|
||||||
const { grid, size, currentDensity, guideUsageCount } = data;
|
const { grid, size, currentDensity, guideUsageCount, hasUsedBoost } = data;
|
||||||
if (!grid || !grid.length) return null;
|
if (!grid || !grid.length) return null;
|
||||||
|
|
||||||
const appUrl = typeof __APP_HOMEPAGE__ !== 'undefined' ? __APP_HOMEPAGE__ : '';
|
const appUrl = typeof __APP_HOMEPAGE__ !== 'undefined' ? __APP_HOMEPAGE__ : '';
|
||||||
@@ -126,7 +139,7 @@ export function buildShareSVG(data, t, formattedTime) {
|
|||||||
const padding = 28;
|
const padding = 28;
|
||||||
const headerHeight = 64;
|
const headerHeight = 64;
|
||||||
const footerHeight = 28;
|
const footerHeight = 28;
|
||||||
const infoHeight = 40;
|
const infoHeight = (guideUsageCount > 0 && hasUsedBoost) ? 65 : 40;
|
||||||
const width = boardSize + padding * 2;
|
const width = boardSize + padding * 2;
|
||||||
const height = boardSize + padding * 2 + headerHeight + footerHeight + infoHeight;
|
const height = boardSize + padding * 2 + headerHeight + footerHeight + infoHeight;
|
||||||
|
|
||||||
@@ -218,12 +231,23 @@ export function buildShareSVG(data, t, formattedTime) {
|
|||||||
}
|
}
|
||||||
svgContent += cells;
|
svgContent += cells;
|
||||||
|
|
||||||
// Guide Usage
|
// Guide Usage & Boost Info
|
||||||
|
let infoY = height - padding - footerHeight + 10;
|
||||||
|
if (guideUsageCount > 0 && hasUsedBoost) {
|
||||||
|
infoY -= 25;
|
||||||
|
}
|
||||||
|
|
||||||
if (guideUsageCount > 0) {
|
if (guideUsageCount > 0) {
|
||||||
const totalCells = size * size;
|
const totalCells = size * size;
|
||||||
const percent = Math.min(100, Math.round((guideUsageCount / totalCells) * 100));
|
const percent = Math.min(100, Math.round((guideUsageCount / totalCells) * 100));
|
||||||
const guideText = t('win.usedGuide', { count: guideUsageCount, percent });
|
const guideText = t('win.usedGuide', { count: guideUsageCount, percent });
|
||||||
svgContent += `<text x="${padding}" y="${height - padding - footerHeight + 10}" font-family="Segoe UI, sans-serif" font-weight="600" font-size="14" fill="#ff4d4d">⚠️ ${guideText}</text>`;
|
svgContent += `<text x="${padding}" y="${infoY}" font-family="Segoe UI, sans-serif" font-weight="600" font-size="14" fill="#ff4d4d">⚠️ ${guideText}</text>`;
|
||||||
|
if (hasUsedBoost) infoY += 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasUsedBoost) {
|
||||||
|
const boostText = t('win.boosted');
|
||||||
|
svgContent += `<text x="${padding}" y="${infoY}" font-family="Segoe UI, sans-serif" font-weight="600" font-size="14" fill="#ffd700">⚡ ${boostText}</text>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL
|
// URL
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const messages = {
|
|||||||
'worker.logicRow': 'Logika: Wiersz {row}, Kolumna {col} -> {state}',
|
'worker.logicRow': 'Logika: Wiersz {row}, Kolumna {col} -> {state}',
|
||||||
'worker.logicCol': 'Logika: Kolumna {col}, Wiersz {row} -> {state}',
|
'worker.logicCol': 'Logika: Kolumna {col}, Wiersz {row} -> {state}',
|
||||||
'worker.stuck': 'Brak logicznego ruchu. Spróbuj zgadnąć lub cofnąć.',
|
'worker.stuck': 'Brak logicznego ruchu. Spróbuj zgadnąć lub cofnąć.',
|
||||||
|
'worker.boosted': 'Boost (DFS): Wiersz {row}, Kolumna {col} -> {state}',
|
||||||
'worker.done': 'Koniec!',
|
'worker.done': 'Koniec!',
|
||||||
'worker.state.filled': 'Pełne',
|
'worker.state.filled': 'Pełne',
|
||||||
'worker.state.empty': 'Puste'
|
'worker.state.empty': 'Puste'
|
||||||
@@ -16,6 +17,7 @@ const messages = {
|
|||||||
'worker.logicRow': 'Logic: Row {row}, Column {col} -> {state}',
|
'worker.logicRow': 'Logic: Row {row}, Column {col} -> {state}',
|
||||||
'worker.logicCol': 'Logic: Column {col}, Row {row} -> {state}',
|
'worker.logicCol': 'Logic: Column {col}, Row {row} -> {state}',
|
||||||
'worker.stuck': 'No logical move found. Try guessing or undoing.',
|
'worker.stuck': 'No logical move found. Try guessing or undoing.',
|
||||||
|
'worker.boosted': 'Boost (DFS): Row {row}, Column {col} -> {state}',
|
||||||
'worker.done': 'Done!',
|
'worker.done': 'Done!',
|
||||||
'worker.state.filled': 'Filled',
|
'worker.state.filled': 'Filled',
|
||||||
'worker.state.empty': 'Empty'
|
'worker.state.empty': 'Empty'
|
||||||
@@ -79,7 +81,12 @@ const isSolved = (grid, solution) => {
|
|||||||
const solutionCell = solution[r][c];
|
const solutionCell = solution[r][c];
|
||||||
const isFilled = playerCell === 1;
|
const isFilled = playerCell === 1;
|
||||||
const shouldBeFilled = solutionCell === 1;
|
const shouldBeFilled = solutionCell === 1;
|
||||||
|
|
||||||
|
// Check correctness
|
||||||
if (isFilled !== shouldBeFilled) return false;
|
if (isFilled !== shouldBeFilled) return false;
|
||||||
|
|
||||||
|
// Check completeness (must be fully resolved to 1 or 2)
|
||||||
|
if (playerCell === 0) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -131,9 +138,36 @@ const handleStep = (playerGrid, solution, locale) => {
|
|||||||
return { type: 'stuck', statusText: t(locale, 'worker.stuck') };
|
return { type: 'stuck', statusText: t(locale, 'worker.stuck') };
|
||||||
};
|
};
|
||||||
|
|
||||||
self.onmessage = (event) => {
|
const handleBoost = (playerGrid, solution, locale) => {
|
||||||
const { id, playerGrid, solution, locale } = event.data;
|
const size = solution.length;
|
||||||
const resolved = resolveLocale(locale);
|
// Find first unknown cell and reveal it
|
||||||
const result = handleStep(playerGrid, solution, resolved);
|
for (let r = 0; r < size; r++) {
|
||||||
self.postMessage({ id, ...result });
|
for (let c = 0; c < size; c++) {
|
||||||
|
if (playerGrid[r][c] === 0) {
|
||||||
|
const correctState = solution[r][c] === 1 ? 1 : 2;
|
||||||
|
const stateLabel = t(locale, correctState === 1 ? 'worker.state.filled' : 'worker.state.empty');
|
||||||
|
return {
|
||||||
|
type: 'move',
|
||||||
|
r,
|
||||||
|
c,
|
||||||
|
state: correctState,
|
||||||
|
statusText: t(locale, 'worker.boosted', { row: r + 1, col: c + 1, state: stateLabel })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { type: 'done', statusText: t(locale, 'worker.solved') };
|
||||||
|
};
|
||||||
|
|
||||||
|
self.onmessage = (event) => {
|
||||||
|
const { id, playerGrid, solution, locale, action } = event.data;
|
||||||
|
const resolved = resolveLocale(locale);
|
||||||
|
|
||||||
|
if (action === 'boost') {
|
||||||
|
const result = handleBoost(playerGrid, solution, resolved);
|
||||||
|
self.postMessage({ id, ...result });
|
||||||
|
} else {
|
||||||
|
const result = handleStep(playerGrid, solution, resolved);
|
||||||
|
self.postMessage({ id, ...result });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user