From 1e4a6b81ebc2bf27c4be4e4d795bd3c5b35c8bdf Mon Sep 17 00:00:00 2001 From: Karl Ostmo Date: Thu, 12 Jan 2023 01:13:33 -0800 Subject: [PATCH] more efficient solution checker --- .../Challenges/_word-search/solution.sw | 12 +- .../_word-search/verify-solution.sw | 187 ++++-------------- data/scenarios/Challenges/word-search.yaml | 122 +++++++++++- 3 files changed, 167 insertions(+), 154 deletions(-) diff --git a/data/scenarios/Challenges/_word-search/solution.sw b/data/scenarios/Challenges/_word-search/solution.sw index 4ac252cf16..a0c90ed354 100644 --- a/data/scenarios/Challenges/_word-search/solution.sw +++ b/data/scenarios/Challenges/_word-search/solution.sw @@ -1,5 +1,14 @@ def doN = \n. \f. if (n > 0) {f; doN (n - 1) f} {}; end; +def intersperse = \n. \f2. \f1. if (n > 0) { + f1; + if (n > 1) { + f2; + } {}; + intersperse (n - 1) f2 f1; + } {}; + end; + def waitUntilUnblocked = x <- blocked; if x { @@ -49,7 +58,8 @@ def traverseRow = \progress. \n. if (newProgress == 3) { turn back; - doN 3 (highlightLetter; move;); + + intersperse 3 move highlightLetter; } { move; traverseRow newProgress (n - 1); diff --git a/data/scenarios/Challenges/_word-search/verify-solution.sw b/data/scenarios/Challenges/_word-search/verify-solution.sw index 8bd5d4bccb..e77bbe5822 100644 --- a/data/scenarios/Challenges/_word-search/verify-solution.sw +++ b/data/scenarios/Challenges/_word-search/verify-solution.sw @@ -1,63 +1,9 @@ -// Traverse all rows, left-to-right, to see if the highlighted letters -// exist in the proper order. -// Fails if more than 3 letters are highlighted. - - -def doN = \n. \f. if (n > 0) {f; doN (n - 1) f} {}; end; - -def boolToNum = \b. - if b {return 1} {return 0}; - end; - -def resetBits = - countOnes <- count "bit (1)"; - doN countOnes $ make "bit (0)"; - end; - -// Counts how many times a predicate was true in a loop. -// NOT USED -def countInRow = \act. \pred. \n. - if (n > 0) { - isTrue <- pred; - if isTrue { - make "bit (1)"; - } {}; - - act; - countInRow act pred $ n - 1; - } {}; - end; - - -def chooseLetter = \i. - if (i == 0) { - return "lowercase c"; - } { - if (i == 1) { - return "lowercase o"; - } { - return "lowercase w"; - } - }; - end; - - -// NOT USED -def isHighlighted = - isC <- ishere "lowercase c"; - if (isC) { - return true; - } { - isO <- ishere "lowercase o"; - if (isO) { - return true; - } { - isW <- ishere "lowercase w"; - return isW; - } - } - end; +// Algorithm: +// We only need to check the base's +// current position: if we find three contiguous highlights, +// then we know that the player has just completed their +// third highlight. def whichOrdinal = isC <- ishere "lowercase c"; @@ -77,114 +23,61 @@ def whichOrdinal = } } end; + +def countConsecutive = \expectedOrdinal. \n. -// NOT USED -def countHighlighted = \n. - countInRow move isHighlighted n; - hCount <- count "bit (1)"; - return hCount; - end; - -def moveToRowBeginning = \r. - currentPos <- as r {whereami}; - teleport r (0, snd currentPos); - end; - - -/** Returns a tuple: -* (didFindWord, isSolutionInvalid) -* -* The necessary condition to win the scenario is -* [didFindWord == true] for ANY row, -* and -* [isSolutionInvalid == false] for ALL rows. -* -* We may need to check all of the rows, though -* we can abort early with failure if any row has -* [isSolutionInvalid == false] -*/ -def traverseRow = \isWordFound. \highlightCount. \expectedOrdinal. \n. - - if (n > 0) { + thisOrdinal <- whichOrdinal; - observedOrdinal <- whichOrdinal; - - let isHighlighted = observedOrdinal >= 0 in + nextOrdinal <- if (thisOrdinal == expectedOrdinal) { + return $ expectedOrdinal + 1; + } { + return 0; + }; - newHighlightCount <- if isHighlighted { - return $ highlightCount + 1; - } { - return highlightCount; - }; + if (nextOrdinal == 3) { + return true; + } { - nextExpectedOrdinal <- if (observedOrdinal == expectedOrdinal) { - return $ expectedOrdinal + 1; + if (n > 0) { + move; + countConsecutive nextOrdinal (n - 1); } { - // Zero progress. - - // TODO: we have the potential to short-circuit here, - // if (expectedOrdinal > 0). Resetting after having - // made some progress implies overall failure, - // because a valid solution has no discontinuities. - - // Note that examining the vertical direction (columns) - // could still yield a win. - return 0; + return false; }; + }; - // This boolean function argument is a latch. - let newIsWordFound = isWordFound || (nextExpectedOrdinal == 3) in + end; + - move; - traverseRow newIsWordFound newHighlightCount nextExpectedOrdinal (n - 1); +def checkBackAndForth = + foundBackward <- countConsecutive 0 3; + if (foundBackward) { + return true; } { - // Done recursing, have exhausted the row. - return (isWordFound, highlightCount); - }; + turn back; + countConsecutive 0 3; + } end; -def traverseAllRows = \r. \didFindWord. \highlightCount. \n. +def checkDirections = \n. if (n > 0) { - - foundWordAndHighlightCount <- traverseRow didFindWord highlightCount 0 25; - // first element: found word - // second element: highlight count - - let newDidFindWord = fst foundWordAndHighlightCount in - let newHighlightCount = snd foundWordAndHighlightCount in - - if (newHighlightCount > 3) { - return false; + wasFound <- checkBackAndForth; + if (wasFound) { + return true; } { - moveToRowBeginning r; - resetBits; - traverseAllRows r newDidFindWord newHighlightCount $ n - 1; - }; + turn left; + checkDirections $ n - 1; + } } { - return didFindWord; + return false; } end; def checkSoln = - - r <- robotnamed "lettersetter"; - - as r { - finishedPlacing <- has "boulder"; - - if (finishedPlacing) { - - teleport r (0, 0); - - traverseAllRows r false 0 15; - - } { - return false; - }; - }; + checkDirections 4; end; -checkSoln; +as base {checkSoln}; \ No newline at end of file diff --git a/data/scenarios/Challenges/word-search.yaml b/data/scenarios/Challenges/word-search.yaml index dab985a9d0..25d8635193 100644 --- a/data/scenarios/Challenges/word-search.yaml +++ b/data/scenarios/Challenges/word-search.yaml @@ -13,7 +13,84 @@ objectives: top-left to bottom-right. - No other letters may be highlighted. condition: | - as base {run "scenarios/Challenges/_word-search/verify-solution.sw"} + + def whichOrdinal = + isC <- ishere "lowercase c"; + if (isC) { + return 0; + } { + isO <- ishere "lowercase o"; + if (isO) { + return 1; + } { + isW <- ishere "lowercase w"; + if (isW) { + return 2; + } { + return (-1); + } + } + } + end; + + + def countConsecutive = \expectedOrdinal. \n. + + thisOrdinal <- whichOrdinal; + + nextOrdinal <- if (thisOrdinal == expectedOrdinal) { + return $ expectedOrdinal + 1; + } { + return 0; + }; + + if (nextOrdinal == 3) { + return true; + } { + + if (n > 0) { + move; + countConsecutive nextOrdinal (n - 1); + } { + return false; + }; + }; + + end; + + + def checkBackAndForth = + + foundBackward <- countConsecutive 0 3; + if (foundBackward) { + return true; + } { + turn back; + countConsecutive 0 3; + } + end; + + + def checkDirections = \n. + if (n > 0) { + wasFound <- checkBackAndForth; + if (wasFound) { + return true; + } { + turn left; + checkDirections $ n - 1; + } + } { + return false; + } + end; + + def checkSoln = + checkDirections 4; + end; + + as base {checkSoln}; + robots: - name: base dir: [-1, 0] @@ -37,22 +114,24 @@ robots: - string - workbench - highlighter + inventory: + - [3, ink] - name: lettersetter system: true dir: [1, 0] display: invisible: false - devices: - - lodestone inventory: - [600, capital C] - [600, capital O] - [600, capital W] - - [25, bit (0)] program: | run "scenarios/Challenges/_word-search/create-puzzle.sw" solution: | - run "scenarios/Challenges/_word-search/solution.sw" + // Hardcoded for seed=0: + move; move; turn left; move; drill down; move; drill down; move; drill down; + + // run "scenarios/Challenges/_word-search/solution.sw" entities: - name: capital C display: @@ -93,25 +172,56 @@ entities: attr: gold capabilities: [drill] description: - - Used for marking found words + - Instrument for marking found words + properties: [known, portable] + - name: ink + display: + char: 'K' + attr: gold + description: + - Ink for marking found words properties: [known, portable] recipes: - in: - [1, capital C] + - [1, ink] out: - [1, lowercase c] required: - [1, highlighter] - in: - [1, capital O] + - [1, ink] out: - [1, lowercase o] required: - [1, highlighter] - in: - [1, capital W] + - [1, ink] + out: + - [1, lowercase w] + required: + - [1, highlighter] + - in: + - [1, lowercase c] out: + - [1, capital C] + - [1, ink] + required: + - [1, highlighter] + - in: + - [1, lowercase o] + out: + - [1, capital O] + - [1, ink] + required: + - [1, highlighter] + - in: - [1, lowercase w] + out: + - [1, capital W] + - [1, ink] required: - [1, highlighter] known: [boulder, capital C, capital O, capital W]