-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Structure browser and recognizer (#1579)
Closes #1575 Implements structure recognition. ## Features * Structure browsing dialog (`F6`) that becomes available if a scenario declares any recognizable structures * Automatically recognizes statically-placed structures upon scenario initialization, accounting for occlusion by other entity/structure placement * New `structure` command for querying location of recognized structures (primarily intended for system robots and goal checking) * Efficiently recognizes structures immediately upon completion * Accounts for removal of structures * Several new integration tests * Structured web-interface log to help understand/debug the recognition process * Re-uses much of the functionality built previously for defining structures (#1332) Other changes: * Improved validation for static structure placement (ensure valid structure names instead of failing silently) * Moved a few functions (`getNeighborLocs`, `zoomWorld`, `entityAt`, `robotWithID`, `robotWithName`) out of `Step.Util` and into `State` so that recognizer initialization, which becomes a field in `GameState`, could use them * split `scenarioToGameState` into pure and non-pure functions ## Optimizations Scenarios that do not make use of structure recognition are entirely unaffected, performance-wise. Some optimizations include: * Structure definitions must "opt-in" to participate in automatic recognition * Aho-Corasick automatons optimized by: * only initiate structure search if a placed entity exists on a participating structure template * initializing different automatons for each type of "placed entity" * pruning inapplicable row candidates for 2-D search The row-level structure recognition cache described in #1575 was not implemented; it's probably not worth the complexity cost. # UI Demo scripts/play.sh -i scenarios/Testing/1575-structure-recognizer/1575-browse-structures.yaml --autoplay 1. Press `F6` for Structure Browser dialog 2. View http://localhost:5357/recognize/log and http://localhost:5357/recognize/found ![image](https://github.com/swarm-game/swarm/assets/261693/e32d3572-7e53-42d6-84cd-393c57a8aeac) # Future improvements * Refactor `State.hs` so that the new helper functions can live elsewhere * Support non-rectangular recognizable structures * Allow flip/rotate of recognizable structures * Structure ownership by robots * Consolidate code between the Goals and Structures modal dialogs, and the Achievements browser * Enforce minimum/maximum dimensions for structure definitions
- Loading branch information
Showing
63 changed files
with
2,559 additions
and
139 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
data/scenarios/Testing/1575-structure-recognizer/00-ORDER.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
1575-browse-structures.yaml | ||
1575-nested-structure-definition.yaml | ||
1575-construction-count.yaml | ||
1575-handle-overlapping.yaml | ||
1575-ensure-single-recognition.yaml | ||
1575-ensure-disjoint.yaml | ||
1575-overlapping-tiebreaker-by-largest.yaml | ||
1575-overlapping-tiebreaker-by-location.yaml | ||
1575-remove-structure.yaml | ||
1575-swap-structure.yaml | ||
1575-placement-occlusion.yaml |
131 changes: 131 additions & 0 deletions
131
data/scenarios/Testing/1575-structure-recognizer/1575-browse-structures.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
version: 1 | ||
name: Structure browser | ||
description: | | ||
Hit F6 to view the recognizable structures. | ||
Only the subset of the structures marked with | ||
"recognize: true" are browseable. | ||
In particular, the "donut" structure is placed | ||
in the map but not displayed in the F6 dialog. | ||
creative: false | ||
objectives: | ||
- teaser: Build structure | ||
goal: | ||
- | | ||
Build a "precious" structure | ||
condition: | | ||
foundStructure <- structure "precious" 0; | ||
return $ case foundStructure (\_. false) (\_. true); | ||
robots: | ||
- name: base | ||
dir: [1, 0] | ||
devices: | ||
- grabber | ||
- treads | ||
inventory: | ||
- [50, flower] | ||
- [50, log] | ||
- [50, rock] | ||
- [50, copper pipe] | ||
- [50, iron gear] | ||
- [50, quartz] | ||
- [50, gold] | ||
- [50, silver] | ||
- [50, mithril] | ||
- [50, cotton] | ||
solution: | | ||
move; | ||
place "quartz"; | ||
move; | ||
place "quartz"; | ||
move; | ||
place "mithril"; | ||
structures: | ||
- name: donut | ||
structure: | ||
palette: | ||
'@': [dirt, rock] | ||
mask: '.' | ||
map: | | ||
.@@@. | ||
@@@@@ | ||
@@.@@ | ||
@@@@@ | ||
.@@@. | ||
- name: diamond | ||
recognize: true | ||
description: "A diamond pattern of flowers" | ||
structure: | ||
mask: '.' | ||
palette: | ||
'x': [stone, flower] | ||
map: | | ||
...x... | ||
..xxx.. | ||
.xxxxx. | ||
xxxxxxx | ||
.xxxxx. | ||
..xxx.. | ||
...x... | ||
- name: contraption | ||
recognize: true | ||
description: "A device for assembling useful widgets" | ||
structure: | ||
mask: '.' | ||
palette: | ||
'r': [stone, log] | ||
'I': [stone, rock] | ||
'l': [stone, copper pipe] | ||
'g': [stone, iron gear] | ||
map: | | ||
rllllr | ||
lIIIIl | ||
lIIIgg | ||
rlllgg | ||
- name: precious | ||
recognize: true | ||
structure: | ||
mask: '.' | ||
palette: | ||
'q': [stone, quartz] | ||
'g': [stone, gold] | ||
's': [stone, silver] | ||
'm': [stone, mithril] | ||
map: | | ||
qgs | ||
gsq | ||
qqm | ||
- name: smallish | ||
recognize: true | ||
structure: | ||
mask: '.' | ||
palette: | ||
'q': [stone, quartz] | ||
'm': [stone, mithril] | ||
'c': [stone, cotton] | ||
map: | | ||
qqm | ||
cqq | ||
known: [flower, log, rock, copper pipe, iron plate] | ||
world: | ||
name: root | ||
dsl: | | ||
{blank} | ||
palette: | ||
'.': [grass] | ||
'q': [grass, quartz] | ||
'g': [grass, gold] | ||
's': [grass, silver] | ||
'm': [grass, mithril] | ||
'c': [grass, cotton] | ||
'B': [grass, null, base] | ||
upperleft: [0, 0] | ||
placements: | ||
- src: donut | ||
offset: [6, 0] | ||
map: | | ||
.qgs......... | ||
.gsq......... | ||
B............ | ||
.cqq......... | ||
............. |
69 changes: 69 additions & 0 deletions
69
data/scenarios/Testing/1575-structure-recognizer/1575-construction-count.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
version: 1 | ||
name: Structure recognizer - counting | ||
description: | | ||
Count the construction of several adjacent copies | ||
creative: false | ||
objectives: | ||
- teaser: Build 12 structures | ||
goal: | ||
- | | ||
Build 12 copies of the "green_jewel" structure | ||
condition: | | ||
foundGreen <- structure "green_jewel" 0; | ||
return $ case foundGreen (\_. false) (\x. fst x >= 12); | ||
robots: | ||
- name: base | ||
dir: [1, 0] | ||
devices: | ||
- ADT calculator | ||
- branch predictor | ||
- comparator | ||
- dictionary | ||
- grabber | ||
- lambda | ||
- logger | ||
- strange loop | ||
- treads | ||
inventory: | ||
- [108, pixel (G)] | ||
solution: | | ||
def doN = \n. \f. if (n > 0) {f; doN (n - 1) f} {}; end; | ||
doN 6 ( | ||
doN 9 (place "pixel (G)"; move;); | ||
doN 2 (turn right; move;); | ||
doN 9 (place "pixel (G)"; move;); | ||
doN 2 (turn left; move;); | ||
); | ||
structures: | ||
- name: green_jewel | ||
recognize: true | ||
structure: | ||
palette: | ||
'g': [stone, pixel (G)] | ||
map: | | ||
ggg | ||
ggg | ||
ggg | ||
known: [pixel (G)] | ||
world: | ||
name: root | ||
dsl: | | ||
{blank} | ||
palette: | ||
'.': [grass] | ||
'B': [grass, null, base] | ||
upperleft: [0, 0] | ||
map: | | ||
B........ | ||
......... | ||
......... | ||
......... | ||
......... | ||
......... | ||
......... | ||
......... | ||
......... | ||
......... | ||
......... | ||
......... |
84 changes: 84 additions & 0 deletions
84
data/scenarios/Testing/1575-structure-recognizer/1575-ensure-disjoint.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
version: 1 | ||
name: Structure recognizer - Disjoint recognitions | ||
description: | | ||
Ensure that the completion of a second structure | ||
template is not recognized if it overlaps | ||
with a previously completed structure. | ||
Player starts with 3 `silver`{=entity}. A win | ||
should not be counted until all three are placed. | ||
creative: false | ||
objectives: | ||
- teaser: Build 2 chessboards | ||
prerequisite: | ||
not: premature_win | ||
goal: | ||
- | | ||
Build 2 of the same structure | ||
condition: | | ||
foundStructure <- structure "chessboard" 0; | ||
return $ case foundStructure (\_. false) (\fs. | ||
let boardCount = fst fs in | ||
boardCount >= 2; | ||
); | ||
- id: premature_win | ||
teaser: Don't count win early | ||
optional: true | ||
goal: | ||
- | | ||
Two structures shouldn't be recognized | ||
while the bases still possesses `silver`{=entity} | ||
condition: | | ||
robotHasSilver <- as base {has "silver"}; | ||
foundStructure <- structure "chessboard" 0; | ||
return $ case foundStructure (\_. false) (\fs. | ||
let boardCount = fst fs in | ||
boardCount >= 2 && robotHasSilver; | ||
); | ||
robots: | ||
- name: base | ||
dir: [0, -1] | ||
devices: | ||
- grabber | ||
- treads | ||
inventory: | ||
- [3, silver] | ||
solution: | | ||
move; | ||
turn left; | ||
place "silver"; | ||
move; move; | ||
place "silver"; | ||
move; move; | ||
place "silver"; | ||
structures: | ||
- name: chessboard | ||
recognize: true | ||
structure: | ||
mask: '.' | ||
palette: | ||
'g': [stone, gold] | ||
's': [stone, silver] | ||
map: | | ||
gsgs | ||
sgsg | ||
gsgs | ||
sgsg | ||
world: | ||
name: root | ||
dsl: | | ||
{water} | ||
palette: | ||
'.': [grass, water] | ||
'x': [grass, erase] | ||
'g': [grass, gold] | ||
's': [grass, silver] | ||
'B': [grass, water, base] | ||
upperleft: [0, 0] | ||
map: | | ||
...B.... | ||
gsgxgxgx | ||
sgsgsgsg | ||
gsgsgsgs | ||
sgsgsgsg |
80 changes: 80 additions & 0 deletions
80
data/scenarios/Testing/1575-structure-recognizer/1575-ensure-single-recognition.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
version: 1 | ||
name: Structure recognizer - single recognition | ||
description: | | ||
Ensure that only a single structure is recognized | ||
when placing an entity would complete more than one | ||
structure template. | ||
creative: false | ||
objectives: | ||
- teaser: Build 2 chessboards | ||
prerequisite: | ||
not: premature_win | ||
goal: | ||
- | | ||
Build 2 of the same structure | ||
condition: | | ||
foundStructure <- structure "chessboard" 0; | ||
return $ case foundStructure (\_. false) (\fs. | ||
let boardCount = fst fs in | ||
boardCount >= 2; | ||
); | ||
- id: premature_win | ||
teaser: Don't count win early | ||
optional: true | ||
goal: | ||
- | | ||
Two structures shouldn't be recognized | ||
while the bases still possesses `gold`{=entity} | ||
condition: | | ||
robotHasGold <- as base {has "gold"}; | ||
foundStructure <- structure "chessboard" 0; | ||
return $ case foundStructure (\_. false) (\fs. | ||
let boardCount = fst fs in | ||
boardCount >= 2 && robotHasGold; | ||
); | ||
robots: | ||
- name: base | ||
dir: [0, -1] | ||
devices: | ||
- grabber | ||
- treads | ||
inventory: | ||
- [1, gold] | ||
- [1, silver] | ||
solution: | | ||
move; | ||
place "silver"; | ||
move; move; move; | ||
turn left; | ||
move; move; move; move; | ||
place "gold"; | ||
structures: | ||
- name: chessboard | ||
recognize: true | ||
structure: | ||
mask: '.' | ||
palette: | ||
'g': [stone, gold] | ||
's': [stone, silver] | ||
map: | | ||
gsgs | ||
sgsg | ||
gsgs | ||
sgsg | ||
world: | ||
name: root | ||
dsl: | | ||
{blank} | ||
palette: | ||
'.': [grass] | ||
'g': [grass, gold] | ||
's': [grass, silver] | ||
'B': [grass, null, base] | ||
upperleft: [0, 0] | ||
map: | | ||
...B.... | ||
gsg.gsgs | ||
sgsgsgsg | ||
gsgsgsgs | ||
sgsgsgs. |
Oops, something went wrong.