diff options
author | pommicket <pommicket@gmail.com> | 2025-09-18 12:44:49 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2025-09-18 12:44:49 -0400 |
commit | 7e359e4c8888c0630c4c7dda709b3fc041a18235 (patch) | |
tree | 6dd12931ccf6dd1c6d699cdd2680bb47925e4ceb | |
parent | c728aae2527221032f47fded2fb697d95661977d (diff) |
Show solutions
-rw-r--r-- | pub/blankplays.js | 178 | ||||
-rw-r--r-- | pub/index.html | 21 |
2 files changed, 167 insertions, 32 deletions
diff --git a/pub/blankplays.js b/pub/blankplays.js index e58a4b2..d15bb81 100644 --- a/pub/blankplays.js +++ b/pub/blankplays.js @@ -5,15 +5,15 @@ const N = 15; // board size let lexicon = 'nwl23'; function updateBoardSize() { - let board = document.getElementById('board'); + let boardElem = document.getElementById('board'); // sucks for desktop zooming, but there's no way around it. let width = innerWidth; let height = innerHeight; let boardSize = Math.min(width - 20, Math.floor(height * 0.6)); let fontSize = (boardSize / N - 4) * 0.6; - board.style.fontSize = fontSize + 'px'; - board.style.width = boardSize + 'px'; - board.style.height = boardSize + 'px'; + boardElem.style.fontSize = fontSize + 'px'; + boardElem.style.width = boardSize + 'px'; + boardElem.style.height = boardSize + 'px'; let selectContainer = document.getElementById('select-container'); selectContainer.style.fontSize = fontSize + 'px'; selectContainer.style.width = boardSize + 'px'; @@ -55,18 +55,26 @@ function pointValue(letter) { let boardSquareElems = []; let currSolution = []; +let board = []; +let trueSolution = []; +let skipWordsOfLength = 1; +let alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; -function updatePossibilities(highlightElem, letters) { - let possibilitiesElem = highlightElem.querySelector('.possibilities'); - possibilitiesElem.innerText = letters.join(''); - let n = letters.length; - let fontSize = n === 1 ? 100 +function getFontSizeForPossibilities(n) { + return (n === 1 ? 100 : n < 5 ? 60 : n < 7 ? 48 : n < 12 ? 40 : n < 20 ? 32 - : 26; - possibilitiesElem.style.fontSize = fontSize + '%'; + : 26) + '%'; +} + +function updatePossibilities(highlightElem, letters) { + let possibilitiesElem = highlightElem.querySelector('.possibilities'); + possibilitiesElem.innerText = letters.join(''); + let n = letters.length; + let fontSize = getFontSizeForPossibilities(n); + possibilitiesElem.style.fontSize = fontSize; } function addToSolution(row, col, letter) { @@ -173,12 +181,29 @@ function clickedSquare(highlight, row, col) { }; } +function includeSquare(row, col) { + if (board[row][col] !== '.') return false; + let neighbours = []; + if (row > 0) + neighbours.push(board[row-1][col]); + if (row < N-1) + neighbours.push(board[row+1][col]); + if (col > 0) + neighbours.push(board[row][col-1]); + if (col < N-1) + neighbours.push(board[row][col+1]); + if (neighbours.filter((x) => x !== '.').length === 0) { + // not connected + return false; + } + return true; +} + // TODO : error handling async function loadChallenge(id) { let result = await fetch(`challenges-${lexicon}/${id}.txt`); let body = await result.text(); let lines = body.split('\n'); - let board = []; for (let row = 0; row < 15; row++) { board.push([]); for (let col = 0; col < 15; col++) { @@ -186,25 +211,33 @@ async function loadChallenge(id) { } } for (let row = 0; row < 15; row++) { + trueSolution.push([]); + for (let col = 0; col < 15; col++) { + trueSolution[row].push([]); + } + } + for (let i = 15; i < lines.length; i++) { + if (!lines[i]) continue; + let parts = lines[i].split(' '); + let square = parseInt(parts[0]); + let letter = parts[1]; + trueSolution[Math.floor(square / 15)][square % 15].push(letter); + } + for (let row = 0; row < 15; row++) + for (let col = 0; col < 15; col++) + trueSolution[row][col].sort(); + for (let highlight of document.querySelectorAll('.highlight')) { + highlight.remove(); + } + for (let row = 0; row < 15; row++) { for (let col = 0; col < 15; col++) { let letter = board[row][col]; if (letter !== '.') { putTile(row, col, letter); continue; } - let neighbours = []; - if (row > 0) - neighbours.push(board[row-1][col]); - if (row < N-1) - neighbours.push(board[row+1][col]); - if (col > 0) - neighbours.push(board[row][col-1]); - if (col < N-1) - neighbours.push(board[row][col+1]); - if (neighbours.filter((x) => x !== '.').length === 0) { - // not connected + if (!includeSquare(row, col)) continue; - } let highlight = document.createElement('div'); highlight.classList.add('highlight'); highlight.dataset.row = row; @@ -212,19 +245,102 @@ async function loadChallenge(id) { let possibilities = document.createElement('span'); possibilities.classList.add('possibilities'); highlight.appendChild(possibilities); + updatePossibilities(highlight, currSolution[row][col]); boardSquareElems[row][col].appendChild(highlight); highlight.addEventListener('contextmenu', (e) => e.preventDefault()); highlight.addEventListener('mousedown', clickedSquare(highlight, row, col)); } } + updateSkipWordsOfLength(); +} + +function updateSkipWordsOfLength() { + let skip2s = document.getElementById('skip-2s'); + let skip3s = document.getElementById('skip-3s'); + skipWordsOfLength = skip3s.checked ? 3 : skip2s.checked ? 2 : 1; + for (let row = 0; row < 15; row++) { + for (let col = 0; col < 15; col++) { + if (!includeSquare(row, col)) continue; + let i = row; + while (i > 0 && board[i-1][col] !== '.') + i -= 1; + let verticalWordLen = 1; + while (i < N-1 && (i+1 === row || board[i+1][col] !== '.')) { + verticalWordLen += 1; + i += 1; + } + i = col; + let horizontalWordLen = 1; + while (i > 0 && board[row][i-1] !== '.') + i -= 1; + while (i < N-1 && (i+1 === col || board[row][i+1] !== '.')) { + horizontalWordLen += 1; + i += 1; + } + let tooShort = Math.max(horizontalWordLen, verticalWordLen) <= skipWordsOfLength; + document.querySelector(`.highlight[data-row="${row}"][data-col="${col}"]`).style.visibility = + tooShort ? 'hidden' : 'visible'; + } + } +} + +function showSolution() { + document.getElementById('select').style.display = 'none'; + document.getElementById('place').style.display = 'none'; + for (let row = 0; row < 15; row++) { + for (let col = 0; col < 15; col++) { + if (!includeSquare(row, col)) + continue; + let guess = currSolution[row][col]; + let solution = trueSolution[row][col]; + let possibilitiesElem = document.querySelector(`[data-row="${row}"][data-col="${col}"] .possibilities`); + possibilitiesElem.innerHTML = ''; + let totalLength = 0; + for (let letter of alphabet) { + let inGuess = guess.indexOf(letter) !== -1; + let inSolution = solution.indexOf(letter) !== -1; + if (!inGuess && !inSolution) continue; + let span = document.createElement('span'); + span.innerText = letter; + totalLength += letter.length; + span.classList.add('solution-letter'); + if (!inGuess && inSolution) { + span.classList.add('missed'); + } else if (inGuess && !inSolution) { + span.classList.add('wrong'); + } else { + span.classList.add('correct'); + } + possibilitiesElem.appendChild(span); + } + let fontSize = getFontSizeForPossibilities(totalLength); + possibilitiesElem.style.fontSize = fontSize; + } + } } function startup() { + let boardElem = document.getElementById('board'); + let skip2s = document.getElementById('skip-2s'); + let skip3s = document.getElementById('skip-3s'); + skip2s.addEventListener('change', () => { + if (!skip2s.checked) + skip3s.checked = false; + updateSkipWordsOfLength(); + }); + skip3s.addEventListener('change', () => { + if (skip3s.checked) + skip2s.checked = true; + updateSkipWordsOfLength(); + }); + document.getElementById('submit').addEventListener('click', () => { + showSolution(); + }); updateBoardSize(); for (let row = 0; row < N; row++) { let rowElem = document.createElement('div'); rowElem.classList.add('board-row'); - board.appendChild(rowElem); + boardElem.appendChild(rowElem); boardSquareElems.push([]); currSolution.push([]); for (let col = 0; col < N; col++) { @@ -241,13 +357,13 @@ function startup() { for (let row = 0; row < 2; row++) { let rowContainer = selectContainer.querySelectorAll('.select-container-row')[row]; for (let i = row*N; i < (row+1)*N; i++) { - let tiles = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - if (i >= tiles.length) break; + if (i >= alphabet.length) break; let elem = document.createElement('span'); elem.classList.add('select-tile-container'); - makeTile(elem, tiles[i], false); + let letter = alphabet[i]; + makeTile(elem, letter, false); let tileElem = elem.querySelector('.tile'); - tileElem.dataset.letter = tiles[i]; + tileElem.dataset.letter = letter; elem.addEventListener('click', () => { let tileSelected = document.querySelector('.highlight.selected'); let className = tileSelected ? 'possible' : 'placing'; @@ -256,7 +372,7 @@ function startup() { if (tileSelected) { let row = parseInt(tileSelected.dataset.row); let col = parseInt(tileSelected.dataset.col); - removeFromSolution(row, col, tiles[i]); + removeFromSolution(row, col, letter); } } else { let placing = document.querySelector('.placing'); @@ -265,7 +381,7 @@ function startup() { if (tileSelected) { let row = parseInt(tileSelected.dataset.row); let col = parseInt(tileSelected.dataset.col); - addToSolution(row, col, tiles[i]); + addToSolution(row, col, letter); } } }); diff --git a/pub/index.html b/pub/index.html index aead4de..5b6236a 100644 --- a/pub/index.html +++ b/pub/index.html @@ -110,12 +110,31 @@ background-color: #88c; font-weight: bold; } + #submit { + outline: 0; + border: 2px solid #44a; + outline: 2px solid #00a; + box-shadow: 2px 2px 4px #00a; + margin: 4px; + border-radius: 5px; + padding: 2px 5px; + background: #ddf; + } + #submit:hover { + background: #bbf; + } + #submit:active { + transform: translate(2px, 2px); + box-shadow: 0 0 0; + } </style> <script src="/blankplays.js" async></script> </head> <body> Find all the possible plays with a single blank!<br> - <button>All done!</button> + <label><input type="checkbox" id="skip-2s"> Skip 2’s</label> + <label><input type="checkbox" id="skip-3s"> Skip 3’s</label> + <button id="submit">All done!</button> <div id="board"></div> <div id="select" style="display: none;"> Select which letters can go here: |