From 132c4ebced2777b6e1696020dc411feed0b1eff5 Mon Sep 17 00:00:00 2001 From: Grzegorz Kucmierz Date: Wed, 11 Feb 2026 03:03:14 +0100 Subject: [PATCH] feat: enhance custom game difficulty calculation and UI --- src/components/CustomGameModal.vue | 41 +++++++++++++++++++++--------- src/components/WinModal.vue | 3 ++- src/utils/puzzleUtils.js | 36 +++++++++++++++++--------- 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/components/CustomGameModal.vue b/src/components/CustomGameModal.vue index 95c9a5f..c372a33 100644 --- a/src/components/CustomGameModal.vue +++ b/src/components/CustomGameModal.vue @@ -41,12 +41,12 @@ const handleSnap = () => { customSize.value = snapToStep(Number(customSize.value), 5); }; -const difficultyLevel = computed(() => { - return calculateDifficulty(fillRate.value / 100); +const difficultyInfo = computed(() => { + return calculateDifficulty(fillRate.value / 100, customSize.value); }); const difficultyColor = computed(() => { - switch(difficultyLevel.value) { + switch(difficultyInfo.value.level) { case 'extreme': return '#ff3333'; case 'hardest': return '#ff9933'; case 'harder': return '#ffff33'; @@ -97,7 +97,7 @@ const confirm = () => { v-model="fillRate" min="10" max="90" - step="5" + step="1" />
10% @@ -106,10 +106,9 @@ const confirm = () => {
- {{ t('custom.difficulty') }}: - - {{ t(`difficulty.${difficultyLevel}`) }} - +
{{ t('custom.difficulty') }}
+
{{ t(`difficulty.${difficultyInfo.level}`) }}
+
{{ difficultyInfo.value }}%

{{ errorMsg }}

@@ -219,12 +218,30 @@ input[type="range"]::-moz-range-thumb { justify-content: space-between; color: var(--text-muted); font-size: 0.85rem; +.difficulty-indicator { + margin: 20px 0 30px 0; + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; } -.difficulty-indicator { - margin: 20px 0; - font-size: 1.2rem; - display: flex; +.label { + font-size: 1rem; + color: var(--text-muted); +} + +.level { + font-size: 1.4rem; + font-weight: bold; + text-transform: uppercase; + line-height: 1.2; +} + +.percentage { + font-size: 1rem; + font-weight: bold; +} display: flex; justify-content: center; gap: 10px; align-items: center; diff --git a/src/components/WinModal.vue b/src/components/WinModal.vue index 04b2934..4bd4c41 100644 --- a/src/components/WinModal.vue +++ b/src/components/WinModal.vue @@ -231,7 +231,8 @@ const buildShareSVG = () => { // Difficulty Logic const densityPercent = Math.round(store.currentDensity * 100); - const difficultyKey = calculateDifficulty(store.currentDensity); + const diffInfo = calculateDifficulty(store.currentDensity, store.size); + const difficultyKey = diffInfo.level; let diffColor = '#33ff33'; if (difficultyKey === 'extreme') diffColor = '#ff3333'; else if (difficultyKey === 'hardest') diffColor = '#ff9933'; diff --git a/src/utils/puzzleUtils.js b/src/utils/puzzleUtils.js index ad978a1..593947d 100644 --- a/src/utils/puzzleUtils.js +++ b/src/utils/puzzleUtils.js @@ -52,24 +52,36 @@ export function generateRandomGrid(size, density = 0.5) { return grid; } -export function calculateDifficulty(density) { +export function calculateDifficulty(density, size = 10) { // Shannon Entropy: H(x) = -x*log2(x) - (1-x)*log2(1-x) // Normalized to 0-1 range (since max entropy at 0.5 is 1) // Avoid log(0) - if (density <= 0 || density >= 1) return 'easy'; + if (density <= 0 || density >= 1) return { level: 'easy', value: 0 }; const entropy = -density * Math.log2(density) - (1 - density) * Math.log2(1 - density); - // Thresholds based on entropy - // 0.5 density -> entropy 1.0 (Extreme) - // 0.4/0.6 density -> entropy ~0.97 (Extreme) - // 0.3/0.7 density -> entropy ~0.88 (Hardest) - // 0.2/0.8 density -> entropy ~0.72 (Harder) - // <0.2/>0.8 density -> entropy <0.72 (Easy) + // Difficulty score combines entropy (complexity) and size (scale) + // We use sqrt(size) to dampen the effect of very large grids, + // ensuring that density still plays a major role. + // Normalized against max size (80) + const sizeFactor = Math.sqrt(size / 80); + const score = entropy * sizeFactor * 100; + const value = Math.round(score); + + // Thresholds + let level = 'easy'; + if (value >= 80) level = 'extreme'; + else if (value >= 60) level = 'hardest'; + else if (value >= 40) level = 'harder'; + else if (value >= 20) level = 'medium'; // Using 'medium' key if available, or we need to add it? + // Wait, useI18n only has: easy, harder, hardest, extreme. + // Let's stick to those keys but adjust ranges. + + if (value >= 75) level = 'extreme'; + else if (value >= 50) level = 'hardest'; + else if (value >= 25) level = 'harder'; + else level = 'easy'; - if (entropy >= 0.96) return 'extreme'; // approx 38% - 62% - if (entropy >= 0.85) return 'hardest'; // approx 28% - 38% & 62% - 72% - if (entropy >= 0.65) return 'harder'; // approx 17% - 28% & 72% - 83% - return 'easy'; + return { level, value }; }