Should add a solver Todo: https://github.com/jes/cntdn
A Pen by Tom Hermans on CodePen.
Should add a solver Todo: https://github.com/jes/cntdn
A Pen by Tom Hermans on CodePen.
| <section class="wrap"> | |
| <aside> | |
| <h1 class="pagename">Countdown Numbers Game</h1> | |
| <div class="controls"> | |
| <div class="input-section"> | |
| <div class="input-group"> | |
| <label for="largeCount">Large Numbers:</label> | |
| <input type="number" id="largeCount" min="0" max="4" value="2" /> | |
| <span>(0-4 numbers from: 25, 50, 75, 100)</span> | |
| </div> | |
| <div class="input-group"> | |
| <label for="smallCount">Small Numbers:</label> | |
| <input | |
| type="number" | |
| id="smallCount" | |
| min="2" | |
| max="6" | |
| value="4" | |
| readonly | |
| /> | |
| <span>(automatically calculated to total 6)</span> | |
| </div> | |
| <div id="status" class="status" style="display: none"></div> | |
| </div> | |
| <div class="buttons"> | |
| <button class="btn btn--large" id="drawNumbers" onclick="drawNumbers()"> | |
| Draw Numbers | |
| </button> | |
| <button class="btn btn--large" id="setTarget" onclick="setTarget()">Set Target</button> | |
| <button id="resetGame" class="btn btn--large reset-btn" onclick="resetGame()"> | |
| Reset Game | |
| </button> | |
| </div> | |
| </div> | |
| <div class="arrays-section"> | |
| <div class="array-display"> | |
| <div class="array-title">Large Numbers Available</div> | |
| <div id="largeArray" class="array-content">[25, 50, 75, 100]</div> | |
| </div> | |
| <div class="array-display"> | |
| <div class="array-title">Small Numbers Available</div> | |
| <div id="smallArray" class="array-content"> | |
| [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10] | |
| </div> | |
| </div> | |
| </div> | |
| </aside> | |
| <main> | |
| <div class="random-display"> | |
| <div id="randomTarget">???</div> | |
| </div> | |
| <div id="pickedSection" class="picked-numbers"> | |
| <h3>Picked Numbers for Game</h3> | |
| <div id="pickedDisplay" class="picked-display"> | |
| <div class="pickedNum"></div> | |
| <div class="pickedNum"></div> | |
| <div class="pickedNum"></div> | |
| <div class="pickedNum"></div> | |
| <div class="pickedNum"></div> | |
| <div class="pickedNum"></div> | |
| </div> | |
| </div> | |
| <div class="solver"> | |
| <button class="btn" id="getanswer" disabled>could it be done?</button> | |
| <div id="solver-result"></div> | |
| </div> | |
| </main> | |
| </section> |
| // Initialize the number stacks | |
| let NumbersLarge = [25, 50, 75, 100]; | |
| let NumbersSmall = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10]; | |
| let PickedNumbers = []; | |
| let randomTargetNum; | |
| // Get DOM elements | |
| const largeCountInput = document.getElementById('largeCount'); | |
| const smallCountInput = document.getElementById('smallCount'); | |
| const statusDiv = document.getElementById('status'); | |
| const largeArrayDiv = document.getElementById('largeArray'); | |
| const smallArrayDiv = document.getElementById('smallArray'); | |
| const pickedSection = document.getElementById('pickedSection'); | |
| const randomTarget = document.getElementById('randomTarget'); | |
| const pickedDisplay = document.getElementById('pickedDisplay'); | |
| const getanswerBtn = document.getElementById('getanswer'); | |
| const solverDiv = document.getElementById('solver-result'); | |
| // Update display of arrays | |
| function updateArrayDisplay() { | |
| largeArrayDiv.textContent = '[' + NumbersLarge.join(', ') + ']'; | |
| smallArrayDiv.textContent = '[' + NumbersSmall.join(', ') + ']'; | |
| } | |
| // Show status message | |
| function showStatus(message, isError = false) { | |
| statusDiv.textContent = message; | |
| statusDiv.className = isError ? 'status error' : 'status'; | |
| statusDiv.style.display = 'block'; | |
| } | |
| // Hide status message | |
| function hideStatus() { | |
| // statusDiv.style.display = 'none'; | |
| } | |
| // Update small count automatically when large count changes | |
| largeCountInput.addEventListener('input', function() { | |
| resetGame(); | |
| const LNumChoice = parseInt(this.value) || 0; | |
| const SNumChoice = 6 - LNumChoice; | |
| // Validate inputs | |
| if (LNumChoice < 0 || LNumChoice > 4) { | |
| showStatus('Large numbers must be between 0 and 4', true); | |
| return; | |
| } | |
| if (SNumChoice < 0 || SNumChoice > 6) { | |
| showStatus('Invalid combination - total must equal 6', true); | |
| return; | |
| } | |
| smallCountInput.value = SNumChoice; | |
| hideStatus(); | |
| }); | |
| function canSolverWork() { | |
| getanswerBtn.disabled = true; | |
| console.log('pickednums=', PickedNumbers); | |
| // check randomTarget.innerText | |
| console.log('target=', randomTargetNum); | |
| if (PickedNumbers.length != 0 && randomTargetNum != 0) { | |
| // console.log("success"); | |
| getanswerBtn.disabled = false; | |
| } else { | |
| console.log("not both true"); | |
| } | |
| } | |
| // Random draw function | |
| function drawNumbers() { | |
| resetGame(); | |
| const LNumChoice = parseInt(largeCountInput.value) || 0; | |
| const SNumChoice = parseInt(smallCountInput.value) || 0; | |
| // Validation | |
| if (LNumChoice + SNumChoice !== 6) { | |
| showStatus('Total numbers must equal 6!', true); | |
| return; | |
| } | |
| if (LNumChoice > NumbersLarge.length) { | |
| showStatus(`Only ${NumbersLarge.length} large numbers available!`, true); | |
| return; | |
| } | |
| if (SNumChoice > NumbersSmall.length) { | |
| showStatus(`Only ${NumbersSmall.length} small numbers available!`, true); | |
| return; | |
| } | |
| // Clear previous picks | |
| PickedNumbers = []; | |
| // Draw large numbers | |
| for (let i = 0; i < LNumChoice; i++) { | |
| const randomIndex = Math.floor(Math.random() * NumbersLarge.length); | |
| const pickedNumber = NumbersLarge.splice(randomIndex, 1)[0]; | |
| PickedNumbers.push(pickedNumber); | |
| } | |
| // Draw small numbers | |
| for (let i = 0; i < SNumChoice; i++) { | |
| const randomIndex = Math.floor(Math.random() * NumbersSmall.length); | |
| const pickedNumber = NumbersSmall.splice(randomIndex, 1)[0]; | |
| PickedNumbers.push(pickedNumber); | |
| } | |
| // Sort picked numbers for display (large numbers first, then small numbers sorted) | |
| PickedNumbers.sort((a, b) => { | |
| if (a >= 25 && b >= 25) return a - b; // Both large | |
| if (a >= 25 && b < 25) return -1; // a is large, b is small | |
| if (a < 25 && b >= 25) return 1; // a is small, b is large | |
| return a - b; // Both small | |
| }); | |
| // Update displays | |
| updateArrayDisplay(); | |
| // pickedDisplay.textContent = PickedNumbers.join(' '); | |
| // pickedSection.style.display = 'block'; | |
| // Clear any existing content | |
| pickedDisplay.innerHTML = '<div class="pickedNum">?</div><div class="pickedNum">?</div><div class="pickedNum">?</div><div class="pickedNum">?</div><div class="pickedNum">?</div><div class="pickedNum">?</div>'; | |
| // Loop through PickedNumbers and create a div for each | |
| pickedDisplay.innerHTML = ''; | |
| PickedNumbers.forEach(number => { | |
| const numberDiv = document.createElement('div'); | |
| numberDiv.className = 'pickedNum'; | |
| numberDiv.textContent = number; | |
| pickedDisplay.appendChild(numberDiv); | |
| }); | |
| pickedSection.style.display = 'block'; | |
| canSolverWork(); | |
| // showStatus(`Successfully drew ${LNumChoice} large and ${SNumChoice} small numbers!`); | |
| // Log to console for debugging | |
| console.log('Picked Numbers:', {PickedNumbers}); | |
| // console.log('Remaining Large:', {NumbersLarge}); | |
| // console.log('Remaining Small:', {NumbersSmall}); | |
| } | |
| // Reset game function | |
| function resetGame() { | |
| // delete solver-result div + disable solver button | |
| solverDiv.innerText = ''; | |
| getanswerBtn.disabled = true; | |
| randomTargetNum = 0; | |
| pickedDisplay.innerHTML = '<div class="pickedNum">?</div><div class="pickedNum">?</div><div class="pickedNum">?</div><div class="pickedNum">?</div><div class="pickedNum">?</div><div class="pickedNum">?</div>'; | |
| randomTarget.innerText = '---'; | |
| // Reset arrays to original state | |
| NumbersLarge = [25, 50, 75, 100]; | |
| NumbersSmall = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10]; | |
| PickedNumbers = []; | |
| // Reset inputs | |
| // largeCountInput.value = 2; | |
| // smallCountInput.value = 4; | |
| // Update displays | |
| updateArrayDisplay(); | |
| // pickedSection.style.display = 'none'; | |
| hideStatus(); | |
| canSolverWork(); | |
| console.log('Game reset!'); | |
| } | |
| // Generate a random target number between 100 and 999 (inclusive) | |
| function generateRandomTarget() { | |
| // Math.random() gives 0 to 0.999... | |
| // We want 100 to 999, so range is 900 numbers (999 - 100 + 1 = 900) | |
| const min = 100; | |
| const max = 999; | |
| const range = max - min + 1; // 900 | |
| randomTargetNum = Math.floor(Math.random() * range) + min; | |
| return randomTargetNum; | |
| } | |
| function setTarget() { | |
| // console.log('random number'); | |
| randomTarget.innerText = generateRandomTarget(); | |
| canSolverWork(); | |
| } | |
| // Initialize display | |
| updateArrayDisplay(); | |
| // SOLVER | |
| // SOLVER | |
| document.getElementById('getanswer').addEventListener('click', (e) => { | |
| // console.log('get results from solver for target: ', randomTargetNum); | |
| // console.log('these numbers: ', PickedNumbers); | |
| // console.log('print results in div with id #solver-result'); | |
| const result = solve_numbers(PickedNumbers.slice(), randomTargetNum, false); | |
| solverDiv.innerHTML = result; | |
| }); | |
| /* Solver lib code */ | |
| /* Javascript version of cntdn | |
| * | |
| * Countdown game solver | |
| * | |
| * James Stanley 2014 | |
| * https://incoherency.co.uk/countdown/cntdn.js | |
| */ | |
| function _recurse_solve_letters(letters, node, used_letter, cb, answer) { | |
| if (node[0]) | |
| cb(answer, node[0]); | |
| if (answer.length == letters.length) | |
| return; | |
| var done = {}; | |
| for (var i = 0; i < letters.length; i++) { | |
| var c = letters.charAt(i); | |
| if (used_letter[i] || done[c]) | |
| continue; | |
| if (node[c]) { | |
| used_letter[i] = true; | |
| done[c] = true; | |
| _recurse_solve_letters(letters, node[c], used_letter, cb, answer+c); | |
| used_letter[i] = false; | |
| } | |
| } | |
| } | |
| function solve_letters(letters, cb) { | |
| _recurse_solve_letters(letters, dictionary, {}, cb, ''); | |
| } | |
| function sufficient_letters(word, letters) { | |
| var count = {}; | |
| for (var i = 0; i < letters.length; i++) { | |
| if (!count[letters.charAt(i)]) | |
| count[letters.charAt(i)] = 0; | |
| count[letters.charAt(i)]++; | |
| } | |
| for (var i = 0; i < word.length; i++) { | |
| if (!count[word.charAt(i)]) | |
| return false; | |
| count[word.charAt(i)]--; | |
| if (count[word.charAt(i)] < 0) | |
| return false; | |
| } | |
| return true; | |
| } | |
| function word_in_dictionary(word) { | |
| var node = dictionary; | |
| var idx = 0; | |
| while (idx < word.length) { | |
| node = node[word.charAt(idx)]; | |
| idx++; | |
| if (!node) | |
| return false; | |
| } | |
| if (!node[0]) | |
| return false; | |
| return true; | |
| } | |
| var bestdiff; | |
| var bestvalsums; | |
| var allresults = []; | |
| var bestresult; | |
| var OPS = { | |
| "+": function(n1, n2) { if (n1 < 0 || n2 < 0) return false; return n1+n2; }, | |
| "-": function(n1, n2) { if (n2 >= n1) return false; return n1-n2; }, | |
| "_": function(n2, n1) { if (n2 >= n1) return false; return n1-n2; }, | |
| "*": function(n1, n2) { return n1*n2; }, | |
| "/": function(n1, n2) { if (n2 == 0 || n1%n2 != 0) return false; return n1/n2; }, | |
| "?": function(n2, n1) { if (n2 == 0 || n1%n2 != 0) return false; return n1/n2; }, | |
| }; | |
| var OPCOST = { | |
| "+": 1, | |
| "-": 1.05, | |
| "_": 1.05, | |
| "*": 1.2, | |
| "/": 1.3, | |
| "?": 1.3, | |
| }; | |
| function _recurse_solve_numbers(numbers, searchedi, was_generated, target, levels, valsums, trickshot) { | |
| levels--; | |
| for (var i = 0; i < numbers.length-1; i++) { | |
| var ni = numbers[i]; | |
| if (ni === false) | |
| continue; | |
| numbers[i] = false; | |
| for (var j = i+1; j < numbers.length; j++) { | |
| var nj = numbers[j]; | |
| if (nj === false) | |
| continue; | |
| if (i < searchedi && !was_generated[i] && !was_generated[j]) | |
| continue; | |
| for (var o in OPS) { | |
| var r = OPS[o](ni[0], nj[0]); | |
| if (r === false) | |
| continue; | |
| if (o == '/' && nj[0] == 1) | |
| continue; | |
| if (o == '?' && ni[0] == 1) | |
| continue; | |
| if (o == '*' && (ni[0] == 1 || nj[0] == 1)) | |
| continue; | |
| if (r == ni[0] || r == nj[0]) | |
| continue; | |
| var op_cost = Math.abs(r); | |
| while (op_cost % 10 == 0 && op_cost != 0) | |
| op_cost /= 10; | |
| if ((ni[0] == 10 || nj[0] == 10) && o == '*') // HACK: multiplication by 10 is cheap | |
| op_cost = 1; | |
| op_cost *= OPCOST[o]; | |
| var newvalsums = valsums + op_cost; | |
| if (allresults.length == 0 || Math.abs(r-target) < Math.abs(allresults[0].answer[0]-target)) | |
| allresults = []; | |
| if (allresults.length == 0 || Math.abs(r-target) <= Math.abs(allresults[0].answer[0]-target)) | |
| allresults.push(JSON.parse(JSON.stringify({valsums: valsums, answer: [r,o,ni,nj]}))); | |
| if ((Math.abs(r - target) < Math.abs(bestresult[0] - target)) | |
| || (Math.abs(r - target) == Math.abs(bestresult[0] - target) && (trickshot || newvalsums < bestvalsums))) { | |
| bestresult = [r,o,ni,nj]; | |
| bestvalsums = newvalsums; | |
| } | |
| numbers[j] = [r, o, ni, nj]; | |
| var old_was_gen = was_generated[j]; | |
| was_generated[j] = true; | |
| if (levels > 0) | |
| _recurse_solve_numbers(numbers, i+1, was_generated, target, levels, newvalsums, trickshot); | |
| was_generated[j] = old_was_gen; | |
| numbers[j] = nj; | |
| } | |
| } | |
| numbers[i] = ni; | |
| } | |
| } | |
| function tidyup_result(result) { | |
| var mapping = { | |
| "?": "/", "_": "-" | |
| }; | |
| var swappable = { | |
| "*": true, "+": true | |
| }; | |
| if (result.length < 4) | |
| return result; | |
| for (var i = 2; i < result.length; i++) { | |
| var child = result[i]; | |
| child = tidyup_result(child); | |
| if (child[1] == result[1] && swappable[result[1]]) { | |
| result.splice(i--, 1); | |
| result = result.concat(child.slice(2)); | |
| } else { | |
| result[i] = child; | |
| } | |
| } | |
| if (result[1] in mapping) { | |
| result[1] = mapping[result[1]]; | |
| var j = result[2]; | |
| result[2] = result[3]; | |
| result[3] = j; | |
| } else if (swappable[result[1]]) { | |
| childs = result.slice(2).sort(function(a,b) { return b[0] - a[0]; }); | |
| for (var i = 2; i < result.length; i++) | |
| result[i] = childs[i-2]; | |
| } | |
| return result; | |
| } | |
| function fullsize(array) { | |
| if (array.constructor != Array) | |
| return 0; | |
| var l = 0; | |
| for (var i = 0; i < array.length; i++) | |
| l += fullsize(array[i]); | |
| return l + array.length; | |
| } | |
| function serialise_result(result) { | |
| var childparts = []; | |
| for (var i = 2; i < result.length; i++) { | |
| var child = result[i]; | |
| if (child.length >= 4) | |
| childparts.push(serialise_result(child)); | |
| } | |
| childparts = childparts.sort(function(a,b) { return fullsize(b) - fullsize(a); }); | |
| var parts = []; | |
| for (var i = 0; i < childparts.length; i++) { | |
| parts = parts.concat(childparts[i]); | |
| } | |
| var sliced = result.slice(2).map(function(l) { return l[0]; }); | |
| var thispart = [result[0], result[1]].concat(sliced); | |
| return parts.concat([thispart]); | |
| } | |
| function stringify_result(serialised, target) { | |
| var output = ''; | |
| serialised = serialised.slice(0); | |
| for (var i = 0; i < serialised.length; i++) { | |
| var x = serialised[i]; | |
| var args = x.slice(2); | |
| output += args.join(' ' + x[1] + ' ') + ' = ' + x[0] + '\n'; | |
| } | |
| var result = serialised[serialised.length-1][0]; | |
| if (result != target) | |
| output += '(off by ' + (Math.abs(result - target)) + ')\n'; | |
| return output; | |
| } | |
| function _solve_numbers(numbers, target, trickshot) { | |
| numbers = numbers.map(function(x) { return [x, false] }); | |
| var was_generated = []; | |
| for (var i = 0; i < numbers.length; i++) | |
| was_generated.push(false); | |
| bestresult = [0, 0]; | |
| /* attempt to solve with dfs */ | |
| _recurse_solve_numbers(numbers, 0, was_generated, target, numbers.length, 0, trickshot); | |
| return bestresult; | |
| } | |
| function solve_numbers(numbers, target, trickshot) { | |
| numbers.sort(); | |
| bestresult = [numbers[0], numbers[0]]; | |
| /* see if one of these numbers is the answer; with trickshot you'd rather | |
| * have an interesting answer that's close than an exact answer | |
| */ | |
| if (!trickshot) { | |
| for (var i = 1; i < numbers.length; i++) { | |
| if (Math.abs(numbers[i] - target) < Math.abs(bestresult[0] - target)) { | |
| bestresult = [numbers[i], numbers[i]]; | |
| bestvalsums = numbers[i]; | |
| } | |
| } | |
| if (bestresult[0] == target) | |
| return target + " = " + target; | |
| } | |
| //return stringify_result(serialise_result(tidyup_result(_solve_numbers(numbers, target, trickshot))), target); | |
| allresults = []; | |
| _solve_numbers(numbers, target, trickshot); | |
| allresults.sort(function(a,b) { | |
| return a.valsums - b.valsums; | |
| }); | |
| var s = ''; | |
| var got = {}; | |
| for (var i = 0; i < allresults.length; i++) { | |
| var this_str = stringify_result(serialise_result(tidyup_result(allresults[i].answer)), target); | |
| if (!got[this_str]) { | |
| got[this_str] = true; | |
| const lines = this_str.trim().split('\n').filter(l => l.trim() !== ''); | |
| s += '<div class="solution">' + lines.map(l => '<span>' + l + '</span>').join('') + '</div>'; | |
| } | |
| } | |
| return s; | |
| } |
| :root { | |
| --surface1: #38669b; | |
| --surface2: #1d4789; | |
| --surface3: #2328c0; | |
| --font-size-4: clamp(1.131rem, 1.131rem + 0.14vw, 1.414rem); | |
| --font-size-5: clamp(1.600rem, 1.600rem + 0.20vw, 1.999rem); | |
| --font-size-6: clamp(2.262rem, 2.262rem + 0.28vw, 2.827rem); | |
| --font-size-7: clamp(3.198rem, 3.198rem + 0.40vw, 3.998rem); | |
| --font-size-8: clamp(4.522rem, 4.522rem + 0.57vw, 5.653rem); | |
| --font-size-9: clamp(6.394rem, 6.394rem + 0.80vw, 7.993rem); | |
| } | |
| body { | |
| font-family: Arial, sans-serif; | |
| // max-width: 800px; | |
| margin: 0 auto; | |
| // padding: 20px; | |
| background-color: #f5f5f5; | |
| } | |
| .wrap { | |
| display: flex; | |
| } | |
| aside { | |
| width: 30%; | |
| } | |
| main { | |
| // width: 70%; | |
| padding: 3vw; | |
| background: white; | |
| } | |
| // .container { | |
| // background: white; | |
| // padding: 30px; | |
| // border-radius: 10px; | |
| // box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
| // } | |
| .pagename { | |
| color: #2c3e50; | |
| text-align: center; | |
| margin: 1rem 0; | |
| } | |
| .input-section { | |
| background: #ecf0f1; | |
| padding: 1vw 3vw; | |
| border-radius: 8px; | |
| // margin-bottom: 20px; | |
| display: flex; | |
| gap: 1rem; | |
| } | |
| .controls { | |
| display: flex; | |
| flex-direction: column; | |
| > * { | |
| flex-grow: 1; | |
| justify-content: space-around; | |
| } | |
| } | |
| .buttons, | |
| .input-group { | |
| display: flex; | |
| flex-direction: column; | |
| // align-items: left; | |
| // margin-bottom: 15px; | |
| gap: 10px; | |
| span { | |
| display: none; | |
| font-size: 90%; | |
| } | |
| } | |
| .buttons { | |
| padding: 1rem; | |
| } | |
| label { | |
| font-weight: bold; | |
| min-width: 140px; | |
| } | |
| input[type="number"] { | |
| padding: 8px; | |
| border: 2px solid #bdc3c7; | |
| border-radius: 8px; | |
| // width: 80px; | |
| font-size: 2rem; | |
| font-weight: bold; | |
| text-align: center; | |
| } | |
| input[type="number"]:focus { | |
| border-color: #3498db; | |
| outline: none; | |
| } | |
| .btn { | |
| background: #3498db; | |
| color: white; | |
| border: none; | |
| padding: 6px 12px; | |
| border-radius: 6px; | |
| font-size: 1rem; | |
| line-height: 1.5; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: background 0.3s; | |
| } | |
| .btn--large { | |
| padding: 12px 24px; | |
| font-size: 1.3rem; | |
| } | |
| button:hover { | |
| background: #2980b9; | |
| } | |
| button:disabled { | |
| background: #95a5a6; | |
| cursor: not-allowed; | |
| } | |
| .status { | |
| background: #e8f5e8; | |
| border: 1px solid #27ae60; | |
| color: #27ae60; | |
| padding: 10px; | |
| border-radius: 4px; | |
| margin-bottom: 20px; | |
| } | |
| .error { | |
| background: #ffeaea; | |
| border: 1px solid #e74c3c; | |
| color: #e74c3c; | |
| } | |
| .arrays-section { | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: 20px; | |
| margin-bottom: 20px; | |
| } | |
| .array-display { | |
| background: #f8f9fa; | |
| padding: 15px; | |
| border-radius: 6px; | |
| border: 1px solid #dee2e6; | |
| } | |
| .array-title { | |
| font-weight: bold; | |
| margin-bottom: 10px; | |
| color: #495057; | |
| } | |
| .array-content { | |
| font-family: 'Courier New', monospace; | |
| background: white; | |
| padding: 10px; | |
| border-radius: 4px; | |
| border: 1px solid #ced4da; | |
| min-height: 40px; | |
| } | |
| .picked-numbers, | |
| .solver { | |
| // background: #fff3cd; | |
| border: 2px solid #ccc; | |
| padding: 10px; | |
| border-radius: 8px; | |
| margin-top: 20px; | |
| text-align: center; | |
| } | |
| .picked-numbers h3 { | |
| color: #856404; | |
| margin-top: 0; | |
| } | |
| .random-display, | |
| .picked-display { | |
| font-family: sans-serif; // 'Courier New', monospace; | |
| font-size: var(--font-size-6, 8vw); | |
| font-weight: bold; | |
| background: white; | |
| // padding: 15px; | |
| border-radius: 6px; | |
| text-align: center; | |
| // border: 1px solid #ffc107; | |
| // justify-content: space-around; | |
| gap: 10px; | |
| } | |
| .picked-display { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| } | |
| .pickedNum { | |
| background: var(--surface2); | |
| color: white; | |
| // width: 120px; | |
| // aspect-ratio: 2 / 3; | |
| display: grid; | |
| place-content: center; | |
| padding: 2vh 1vw; | |
| letter-spacing: -.2rem; | |
| // min-width: 40px; | |
| // min-height: 80px; | |
| } | |
| .random-display { | |
| background: var(--surface3); | |
| color: white; | |
| letter-spacing: .5rem; | |
| font-size: var(--font-size-8, 14vw); | |
| text-align: center; | |
| } | |
| .reset-btn { | |
| background: #6c757d; | |
| // margin-left: 10px; | |
| } | |
| .reset-btn:hover { | |
| background: #5a6268; | |
| } | |
| // adjustments | |
| .wrap { | |
| display: flex; | |
| flex-direction: column; /* Mobile first: stack vertically */ | |
| } | |
| aside { | |
| width: 100%; /* Full width on mobile */ | |
| order: 1; | |
| } | |
| main { | |
| // width: calc(100% - 4rem); /* Full width on mobile */ | |
| } | |
| .pagename { | |
| display: none; | |
| } | |
| @media (min-width: 508px) { | |
| .pagename { | |
| display: block; | |
| } | |
| .controls { | |
| flex-direction: row; | |
| } | |
| } | |
| /* Desktop styles */ | |
| @media (min-width: 768px) { | |
| .wrap { | |
| flex-direction: row; /* Side by side on desktop */ | |
| } | |
| aside { | |
| width: 200px; /* Fixed minimum width */ | |
| min-width: 200px; /* Ensure it doesn't shrink below 200px */ | |
| flex-shrink: 0; /* Prevent shrinking */ | |
| .controls, | |
| .input-section { | |
| flex-direction: column; | |
| } | |
| } | |
| .picked-display { | |
| grid-template-columns: repeat(6, 1fr); | |
| } | |
| .pickedNum { | |
| aspect-ratio: 2 / 3; | |
| padding: 0 1vw; | |
| } | |
| main { | |
| flex: 1; /* Takes up remaining space */ | |
| // width: auto; /* Let flex handle the width */ | |
| min-height: 100vh; | |
| order: 1; | |
| } | |
| } | |
| #solver-result { | |
| font-family: monospace; | |
| outline: 1px dashed #999; | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) | |
| ; | |
| .solution { | |
| padding: 1rem; | |
| span { | |
| display: block; | |
| } | |
| } | |
| } | |
| #solver-result:not:empty { | |
| padding: 1rem 1rem 0; | |
| } |