From 1292fdece0dee1847824d4af2ba310693cdacb08 Mon Sep 17 00:00:00 2001 From: Viresh Ratnakar <39968616+viresh-ratnakar@users.noreply.github.com> Date: Tue, 14 Sep 2021 13:25:13 -0700 Subject: [PATCH] v1.20 ### Version: Exolve v1.20 September 14 2021 - If a clue has multiple enum-like parts, use the *last* one. - However, override that if there is an earlier enum-like substring that is immediately (or with intervening spaces) followed by "[...]" (which is a tell-tale start of the annotation part). - Allow an empty "[]" to be placed to mark the end of the clue part for dealing with ambiguities and also for the corner case that the anno has to start with "[...]" without making the contents of the square brackets be treated like the solution. This used to require explicitly providing the solution (like "... clue (6) [WITTER] [t]WITTER") but can now also be tackled with "... clue (6) [] [t]WITTER". The empty "[]" is not shown in the clue or the anno. - Bugfix: in the case when an anno is provided in a crossword without solutions, "reveal this" was clearing the cells (now doesn't). --- CHANGELOG.md | 17 ++++ README.md | 29 ++++++- exolve-from-ipuz.js | 2 +- exolve-from-puz.js | 2 +- exolve-m-simple.html | 6 +- exolve-m.css | 2 +- exolve-m.html | 6 +- exolve-m.js | 129 +++++++++++++++++------------- exolve-player.html | 12 +-- exolve.html | 129 +++++++++++++++++------------- test-15x15-unsolved.html | 12 +-- test-3d-solved.html | 4 +- test-basic-solved.html | 4 +- test-basic-unsolved.html | 14 +++- test-big-grid.html | 5 +- test-color-scheme.html | 4 +- test-columnar.html | 4 +- test-completion-notice.html | 4 +- test-customize-puzzle.html | 4 +- test-deleted-clues-solved.html | 4 +- test-diagramless-solved.html | 4 +- test-diagramless-unsolved.html | 4 +- test-exolve-div.html | 4 +- test-hindi.html | 6 +- test-ipuz-solved.html | 6 +- test-ipuz-unsolved.html | 6 +- test-jigsaw-solved.html | 4 +- test-jigsaw-unsolved.html | 4 +- test-linked-solved.html | 4 +- test-linked-unsolved.html | 4 +- test-mixed-solved.html | 4 +- test-ninas-colours.html | 4 +- test-no-clues.html | 4 +- test-nonnum.html | 4 +- test-numeric.html | 4 +- test-oblongs.html | 4 +- test-partial-solved.html | 4 +- test-questions.html | 4 +- test-russian.html | 4 +- test-scroll.html | 4 +- test-show-cell-level-buttons.html | 4 +- test-skipped-numbers.html | 4 +- test-two-puzzles.html | 4 +- test-warnings.html | 4 +- 44 files changed, 294 insertions(+), 201 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ee322f5..147ce6c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +### Version: Exolve v1.20 September 14 2021 + +- If a clue has multiple enum-like parts, use the *last* one. +- However, override that if there is an earlier enum-like substring + that is immediately (or with intervening spaces) followed by "[...]" + (which is a tell-tale start of the annotation part). +- Allow an empty "[]" to be placed to mark the end of the clue part + for dealing with ambiguities and also for the corner case that + the anno has to start with "[...]" without making the contents of + the square brackets be treated like the solution. This used to + require explicitly providing the solution (like + "... clue (6) [WITTER] [t]WITTER") but can now also be tackled + with "... clue (6) [] [t]WITTER". The empty "[]" is not shown in the + clue or the anno. +- Bugfix: in the case when an anno is provided in a crossword + without solutions, "reveal this" was clearing the cells (now doesn't). + ### Version: Exolve v1.19 September 9 2021 - Bug-fix: blank lines in exolve specs mean grid height may be less than diff --git a/README.md b/README.md index 4816cb62..af526f09 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## An Easily Configurable Interactive Crossword Solver -### Version: Exolve v1.19 September 9 2021 +### Version: Exolve v1.20 September 14 2021 Exolve can help you create online interactively solvable crosswords (simple ones with blocks and/or bars as well as those that are jumbles or are @@ -478,7 +478,12 @@ of opening and closing parentheses, containing only numbers, hyphens, commas, apostrophes, and periods, starting with a number. The software also treats a pair of parentheses containing the text "word" or "letter" or "?" with anything before are after it as an enum (to allow the setter to specify the enum as -"(two words)" or "(?)", for example). +"(two words)" or "(?)", for example). But it looks for such enums only if a +normal enum is not present in the clue. + +In the rare case that there are multiple candidate enum parts in a clue, the +last one is used. However, this can be overridden by explicitly using "[]" +to mark the end of the clue (see [`Annotations`](#annotations) below). ### Suppressing enums or separators @@ -546,7 +551,25 @@ this option. If the leading part of the anno needs to be something in square brackets, such as "... (6) [t]WITTER ...," then setters should include the solution before that (even if it can be inferred from the grid), to avoid misinterpreting the -leading part as the solution, like "... (6) [WITTER] [t]WITTER ..." +leading part as the solution, like "... (6) [WITTER] [t]WITTER ..." Or, they +can use an empty pair of square brackets to mark the end of the clue, like +"... (6) [] [t]WITTER ..." The special "[]" clue-end marker string is also +useful if there is any ambiguity about where the clue ends (perhaps because of +multiple enum-like substrings) that cannot be resolved by providing the +solution in square brackets. + +Here are some more complex examples of enum/annotation parsing. +``` + 1 This (13) clue ends (word) here! (4) + 2 This (13) clue also ends (1 word) here! (4) Some annotation follows. + 3 This (13) clue also ends (2 letters) here! (8) [SOLUTION] Some annotation follows. + 4 This (13) clue also ends (words) here! (8) [] [t]WITTER The anno has (3) enum-like parts. + 5 This is an enum-less and anno-less clue that ends here! + 6 This is also an enum-less and anno-less clue that also ends here! (?)* + 7 This is also an enum-less but with-anno clue that also ends here! (?)* [] [t]WITTER Here is the anno. + 8 This clue, even though its anno contains an enum-like substring, ends here! (4) The (word) and (4 letters) enum-like parts here are not numeric. + 9 This clue (13) does not end now (4) as [square brackets do not follow immediately]; it ends here! (4) +``` #### In-clue annotations You can also decorate sub-phrases in the clue with underlines, different styles, diff --git a/exolve-from-ipuz.js b/exolve-from-ipuz.js index 46b15336..b654ad40 100644 --- a/exolve-from-ipuz.js +++ b/exolve-from-ipuz.js @@ -24,7 +24,7 @@ SOFTWARE. The latest code and documentation for Exolve can be found at: https://github.com/viresh-ratnakar/exolve -Version: Exolve v1.19 September 9 2021 +Version: Exolve v1.20 September 14 2021 */ /** diff --git a/exolve-from-puz.js b/exolve-from-puz.js index 8b27c921..ee408c77 100644 --- a/exolve-from-puz.js +++ b/exolve-from-puz.js @@ -24,7 +24,7 @@ SOFTWARE. The latest code and documentation for Exolve can be found at: https://github.com/viresh-ratnakar/exolve -Version: Exolve v1.19 September 9 2021 +Version: Exolve v1.20 September 14 2021 */ function exolveFromPuzNextNull(buffer, offset) { diff --git a/exolve-m-simple.html b/exolve-m-simple.html index 1492c29a..2dff7b32 100644 --- a/exolve-m-simple.html +++ b/exolve-m-simple.html @@ -14,11 +14,11 @@ See the full Exolve license notice in exolve-m.js. -Version: Exolve v1.19 September 9 2021 +Version: Exolve v1.20 September 14 2021 --> - - + + Exolve diff --git a/exolve-m.css b/exolve-m.css index f90dfef2..6f724a32 100644 --- a/exolve-m.css +++ b/exolve-m.css @@ -5,7 +5,7 @@ Copyright (c) 2019 Viresh Ratnakar See the full license notice in exolve-m.js. -Version: Exolve v1.19 September 9 2021 +Version: Exolve v1.20 September 14 2021 */ @media (max-width: 500px) { diff --git a/exolve-m.html b/exolve-m.html index bd6cb73c..e7ff5edc 100644 --- a/exolve-m.html +++ b/exolve-m.html @@ -10,10 +10,10 @@ See the full Exolve license notice in exolve-m.js. -Version: Exolve v1.19 September 9 2021 +Version: Exolve v1.20 September 14 2021 --> - - + + Exolve (replace with puzzle title) diff --git a/exolve-m.js b/exolve-m.js index eb1c5da9..59c50d31 100644 --- a/exolve-m.js +++ b/exolve-m.js @@ -79,7 +79,7 @@ function Exolve(puzzleSpec, visTop=0, maxDim=0, saveState=true) { - this.VERSION = 'Exolve v1.19 September 9 2021'; + this.VERSION = 'Exolve v1.20 September 14 2021'; this.puzzleText = puzzleSpec; this.containerId = containerId; @@ -1729,6 +1729,39 @@ Exolve.prototype.parseCellLocation = function(s) { return [row, col]; } +// Return [oparen, cparen, isNumeric] as the best indices of '(' and ')' for +// the enum in the clue. null if not found. +// We return the last matching enum part, unless we encounter an enum part +// that is immediately followed by something in square brackets, in which +// case we pass that enum part. +Exolve.prototype.findEnum = function(clueLine) { + let candidate = null; + let start = 0; + let cluePart = clueLine; + while (start < clueLine.length) { + let enumLocation = cluePart.search(/\([1-9]+[0-9\-,\.'’\s]*\)/); + let numeric = true; + if (enumLocation < 0 && !candidate) { + numeric = false; + // Look for the string 'word'/'letter'/? in parens. + enumLocation = cluePart.search(/\([^)]*(word|letter|\?)[^)]*\)/i); + } + if (enumLocation < 0) { + break; + } + const enumEndLocation = enumLocation + + cluePart.substr(enumLocation).indexOf(')'); + console.assert(enumEndLocation >= 0, cluePart); + candidate = [start + enumLocation, start + enumEndLocation, numeric]; + start += (enumEndLocation + 1); + cluePart = clueLine.substr(start); + if (cluePart.search(/[ ]*\[.*\]/) == 0) { + return candidate; + } + } + return candidate; +} + // Parse an enum like (4) or (4,5), or (5-2,4). // Return an object with the following properties: // enumLen @@ -1750,33 +1783,13 @@ Exolve.prototype.parseEnum = function(clueLine) { placeholder: '', enumStr: '', }; - let enumLocation = clueLine.search(/\([1-9]+[0-9\-,\.'’\s]*\)/) - if (enumLocation < 0) { - // Look for the string 'word'/'letter'/? in parens. - enumLocation = clueLine.search(/\([^)]*(word|letter|\?)[^)]*\)/i) - if (enumLocation >= 0) { - let enumEndLocation = - enumLocation + clueLine.substr(enumLocation).indexOf(')') - if (enumEndLocation <= enumLocation) { - return parse - } - parse.enumStr = clueLine.substring(enumLocation, enumEndLocation + 1) - if (clueLine.charAt(enumEndLocation + 1) == '*') { - parse.afterEnum = enumEndLocation + 2; - parse.afterClue = enumLocation; - parse.dontShow = true; - } else { - parse.afterEnum = this.adjustAfterEnum(clueLine, enumEndLocation + 1) - parse.afterClue = parse.afterEnum; - } - } - return parse - } - let enumEndLocation = - enumLocation + clueLine.substr(enumLocation).indexOf(')') - if (enumEndLocation <= enumLocation) { - return parse + const foundEnum = this.findEnum(clueLine); + if (!foundEnum) { + return parse; } + const enumLocation = foundEnum[0]; + const enumEndLocation = foundEnum[1]; + const isNumeric = foundEnum[2]; parse.enumStr = clueLine.substring(enumLocation, enumEndLocation + 1) if (clueLine.charAt(enumEndLocation + 1) == '*') { parse.afterEnum = enumEndLocation + 2; @@ -1786,38 +1799,41 @@ Exolve.prototype.parseEnum = function(clueLine) { parse.afterEnum = this.adjustAfterEnum(clueLine, enumEndLocation + 1) parse.afterClue = parse.afterEnum; } - let enumLeft = clueLine.substring(enumLocation + 1, enumEndLocation) - let nextPart + if (!isNumeric) { + return parse; + } + let enumLeft = clueLine.substring(enumLocation + 1, enumEndLocation); + let nextPart; while (enumLeft && (nextPart = parseInt(enumLeft)) && !isNaN(nextPart) && nextPart > 0) { for (let i = 0; i < nextPart; i++) { - parse.placeholder = parse.placeholder + '?' + parse.placeholder = parse.placeholder + '?'; } parse.enumLen = parse.enumLen + nextPart - enumLeft = enumLeft.replace(/\s*\d+\s*/, '') - let nextSymbol = enumLeft.substr(0, 1) + enumLeft = enumLeft.replace(/\s*\d+\s*/, ''); + let nextSymbol = enumLeft.substr(0, 1); if (nextSymbol == '-') { - parse.hyphenAfter.push(parse.enumLen - 1) - enumLeft = enumLeft.substr(1) + parse.hyphenAfter.push(parse.enumLen - 1); + enumLeft = enumLeft.substr(1); } else if (nextSymbol == ',') { - nextSymbol = ' ' - parse.wordEndAfter.push(parse.enumLen - 1) - enumLeft = enumLeft.substr(1) + nextSymbol = ' '; + parse.wordEndAfter.push(parse.enumLen - 1); + enumLeft = enumLeft.substr(1); } else if (nextSymbol == '.') { - parse.wordEndAfter.push(parse.enumLen - 1) - enumLeft = enumLeft.substr(1) + parse.wordEndAfter.push(parse.enumLen - 1); + enumLeft = enumLeft.substr(1); } else if (nextSymbol == '\'') { - enumLeft = enumLeft.substr(1) + enumLeft = enumLeft.substr(1); } else if (enumLeft.indexOf('’') == 0) { // Fancy apostrophe - nextSymbol = '\'' - enumLeft = enumLeft.substr('’'.length) + nextSymbol = '\''; + enumLeft = enumLeft.substr('’'.length); } else { break; } - parse.placeholder = parse.placeholder + nextSymbol + parse.placeholder = parse.placeholder + nextSymbol; } - return parse + return parse; } // Parse a clue label from the start of clueLine. @@ -2263,10 +2279,15 @@ Exolve.prototype.parseAnno = function(anno, clueIndex) { } else if (inBrac && !theClue.solution) { theClue.explicitSol = true theClue.solution = inBrac; + } else if (!inBrac) { + // Skip empty [] in anno, used to point at the start of the anno if + // there are multiple enum-like strings within the clue. + anno = anno.substr(indexOfBrac + 1).trim(); + break; } else { break; } - anno = anno.substr(indexOfBrac + 1).trim() + anno = anno.substr(indexOfBrac + 1).trim(); this.hasReveals = true } theClue.anno = anno; @@ -5179,20 +5200,20 @@ Exolve.prototype.toggleNinas = function() { } Exolve.prototype.clearCell = function(row, col) { - let gridCell = this.grid[row][col] - let oldLetter = gridCell.currLetter + let gridCell = this.grid[row][col]; + let oldLetter = gridCell.currLetter; if (oldLetter != '0') { - gridCell.currLetter = '0' - gridCell.textNode.nodeValue = '' + gridCell.currLetter = '0'; + gridCell.textNode.nodeValue = ''; if (this.atCurr(row, col)) { - this.gridInput.value = '' + this.gridInput.value = ''; } } if (oldLetter == '1') { - let gridSymCell = this.symCell(row, col) + let gridSymCell = this.symCell(row, col); if (gridSymCell.isDgmless) { - gridSymCell.currLetter = '0' - gridSymCell.textNode.nodeValue = '' + gridSymCell.currLetter = '0'; + gridSymCell.textNode.nodeValue = ''; } } } @@ -5598,7 +5619,7 @@ Exolve.prototype.revealCurr = function() { } let oldLetter = gridCell.currLetter; let letter = gridCell.solution; - if (letter && oldLetter != letter) { + if (letter && oldLetter != letter && letter != '0' && letter != '?') { gridCell.currLetter = letter; let revealedChar = this.stateToDisplayChar(letter); gridCell.textNode.nodeValue = revealedChar; diff --git a/exolve-player.html b/exolve-player.html index 1022f2f3..8a89f7bf 100644 --- a/exolve-player.html +++ b/exolve-player.html @@ -3,10 +3,10 @@ - - - - + + + +