feat(hints): visualize completed rows and columns
This commit is contained in:
@@ -82,7 +82,7 @@ define(['./workbox-7a5e81cd'], (function (workbox) { 'use strict';
|
|||||||
*/
|
*/
|
||||||
workbox.precacheAndRoute([{
|
workbox.precacheAndRoute([{
|
||||||
"url": "index.html",
|
"url": "index.html",
|
||||||
"revision": "0.kkc80cp3p5o"
|
"revision": "0.n1n8rjsg38"
|
||||||
}], {});
|
}], {});
|
||||||
workbox.cleanupOutdatedCaches();
|
workbox.cleanupOutdatedCaches();
|
||||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||||
|
|||||||
@@ -247,10 +247,10 @@ watch(() => store.size, async () => {
|
|||||||
<div class="corner-spacer"></div>
|
<div class="corner-spacer"></div>
|
||||||
|
|
||||||
<!-- Column Hints -->
|
<!-- Column Hints -->
|
||||||
<Hints :hints="colHints" orientation="col" :size="gridCols" :activeIndex="activeCol" />
|
<Hints :hints="colHints" orientation="col" :size="gridCols" :activeIndex="activeCol" :completedLines="store.completedCols" />
|
||||||
|
|
||||||
<!-- Row Hints -->
|
<!-- Row Hints -->
|
||||||
<Hints ref="rowHintsRef" :hints="rowHints" orientation="row" :size="gridRows" :activeIndex="activeRow" />
|
<Hints ref="rowHintsRef" :hints="rowHints" orientation="row" :size="gridRows" :activeIndex="activeRow" :completedLines="store.completedRows" />
|
||||||
|
|
||||||
<!-- Grid -->
|
<!-- Grid -->
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ defineProps({
|
|||||||
activeIndex: {
|
activeIndex: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: null
|
default: null
|
||||||
|
},
|
||||||
|
completedLines: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -34,6 +38,7 @@ defineProps({
|
|||||||
class="hint-group"
|
class="hint-group"
|
||||||
:class="{
|
:class="{
|
||||||
'is-active': index === activeIndex,
|
'is-active': index === activeIndex,
|
||||||
|
'is-completed': completedLines[index],
|
||||||
'guide-right': orientation === 'col' && (index + 1) % 5 === 0 && index !== size - 1,
|
'guide-right': orientation === 'col' && (index + 1) % 5 === 0 && index !== size - 1,
|
||||||
'guide-bottom': orientation === 'row' && (index + 1) % 5 === 0 && index !== size - 1
|
'guide-bottom': orientation === 'row' && (index + 1) % 5 === 0 && index !== size - 1
|
||||||
}"
|
}"
|
||||||
@@ -81,6 +86,15 @@ defineProps({
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hint-group.is-completed {
|
||||||
|
opacity: 0.5;
|
||||||
|
background-color: var(--hint-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-group.is-completed .hint-num {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
.col .hint-group {
|
.col .hint-group {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 4px 2px;
|
padding: 4px 2px;
|
||||||
|
|||||||
@@ -56,6 +56,31 @@ export const usePuzzleStore = defineStore('puzzle', () => {
|
|||||||
return Math.min(100, (filledCorrectly.value / totalCellsToFill.value) * 100);
|
return Math.min(100, (filledCorrectly.value / totalCellsToFill.value) * 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const completedRows = computed(() => {
|
||||||
|
if (!solution.value.length || !playerGrid.value.length) return [];
|
||||||
|
const rows = solution.value.length;
|
||||||
|
return Array(rows).fill().map((_, r) => {
|
||||||
|
const targetHints = calculateLineHints(solution.value[r]);
|
||||||
|
const playerLine = playerGrid.value[r];
|
||||||
|
return validateLine(playerLine, targetHints);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const completedCols = computed(() => {
|
||||||
|
if (!solution.value.length || !playerGrid.value.length) return [];
|
||||||
|
const rows = solution.value.length;
|
||||||
|
const cols = solution.value[0].length;
|
||||||
|
return Array(cols).fill().map((_, c) => {
|
||||||
|
const col = [];
|
||||||
|
for (let r = 0; r < rows; r++) {
|
||||||
|
col.push(solution.value[r][c]);
|
||||||
|
}
|
||||||
|
const targetHints = calculateLineHints(col);
|
||||||
|
const playerLine = playerGrid.value.map(row => row[c]);
|
||||||
|
return validateLine(playerLine, targetHints);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
function initGame(levelId = 'easy') {
|
function initGame(levelId = 'easy') {
|
||||||
stopTimer();
|
stopTimer();
|
||||||
@@ -408,7 +433,9 @@ export const usePuzzleStore = defineStore('puzzle', () => {
|
|||||||
currentDensity,
|
currentDensity,
|
||||||
markGuideUsed,
|
markGuideUsed,
|
||||||
startInteraction,
|
startInteraction,
|
||||||
endInteraction
|
endInteraction,
|
||||||
|
completedRows,
|
||||||
|
completedCols
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
70
src/stores/puzzle_completion.test.js
Normal file
70
src/stores/puzzle_completion.test.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
|
||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { setActivePinia, createPinia } from 'pinia';
|
||||||
|
import { usePuzzleStore } from './puzzle';
|
||||||
|
|
||||||
|
describe('Puzzle Store - Completion Logic', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setActivePinia(createPinia());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly identify completed rows and columns', () => {
|
||||||
|
const store = usePuzzleStore();
|
||||||
|
|
||||||
|
// Setup a simple 2x2 puzzle
|
||||||
|
// Solution:
|
||||||
|
// 1 0
|
||||||
|
// 0 1
|
||||||
|
// Row Hints: [1], [1]
|
||||||
|
// Col Hints: [1], [1]
|
||||||
|
store.solution = [
|
||||||
|
[1, 0],
|
||||||
|
[0, 1]
|
||||||
|
];
|
||||||
|
store.playerGrid = [
|
||||||
|
[0, 0],
|
||||||
|
[0, 0]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initially nothing is completed
|
||||||
|
expect(store.completedRows).toEqual([false, false]);
|
||||||
|
expect(store.completedCols).toEqual([false, false]);
|
||||||
|
|
||||||
|
// Fill first row correctly: 1 0
|
||||||
|
store.playerGrid[0][0] = 1;
|
||||||
|
store.playerGrid[0][1] = 0;
|
||||||
|
|
||||||
|
expect(store.completedRows).toEqual([true, false]);
|
||||||
|
|
||||||
|
// Fill second row incorrectly (too many filled): 1 1
|
||||||
|
store.playerGrid[1][0] = 1;
|
||||||
|
store.playerGrid[1][1] = 1;
|
||||||
|
|
||||||
|
// Row 2 hint is [1], user has [2]. Should be false.
|
||||||
|
expect(store.completedRows).toEqual([true, false]);
|
||||||
|
|
||||||
|
// Fix second row: 0 1
|
||||||
|
store.playerGrid[1][0] = 0;
|
||||||
|
store.playerGrid[1][1] = 1;
|
||||||
|
|
||||||
|
expect(store.completedRows).toEqual([true, true]);
|
||||||
|
|
||||||
|
// Check columns
|
||||||
|
// Col 1: 1, 0 -> Hint [1]. Matches.
|
||||||
|
// Col 2: 0, 1 -> Hint [1]. Matches.
|
||||||
|
expect(store.completedCols).toEqual([true, true]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should mark row as completed if constraints are met even if wrong position', () => {
|
||||||
|
const store = usePuzzleStore();
|
||||||
|
// Solution: 1 0 0 (Hint 1)
|
||||||
|
store.solution = [[1, 0, 0]];
|
||||||
|
store.playerGrid = [[0, 0, 0]];
|
||||||
|
|
||||||
|
// User puts 0 0 1 (Hint 1)
|
||||||
|
store.playerGrid[0] = [0, 0, 1];
|
||||||
|
|
||||||
|
// Should be marked as completed because it satisfies the hint "1"
|
||||||
|
expect(store.completedRows).toEqual([true]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -39,6 +39,6 @@ describe('Large Grid Solver', () => {
|
|||||||
console.log('Result:', result);
|
console.log('Result:', result);
|
||||||
|
|
||||||
expect(result.percentSolved).toBeGreaterThan(0);
|
expect(result.percentSolved).toBeGreaterThan(0);
|
||||||
expect(result.difficulty).toBeDefined();
|
expect(result.difficultyScore).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user