topography sublibrary (#1836)

Towards #1043.

The eventual goal of this sublibrary split is to have a self contained library that can compose 2D grids of arbitrary content (perhaps colored pixels, or boolean values).  This could be useful outside of the `swarm` game.

I would also like to write unit tests for the structure recognizer that are independent of the `Entity` type.

# Major Changes

## Direction module
* Moved `Swarm.Language.Syntax.Direction` to `swarm-util`, since both `swarm-lang` and `swarm-topology` depend on it, but not on each other.
* Removed the re-export of direction things from `Swarm.Language.Syntax`

## Structure module

The `Swarm.Game.Scenario.Topography.Structure` module has been split into two:
* `Swarm.Game.Scenario.Topography.Structure`
* `Swarm.Game.Scenario.Topography.Structure.Type`

The former retains the YAML parsing logic.  The latter is agnostic of `Enitiy` type and the palette .
At some future point, I might want to move the YAML parsing to this sublibrary while still retaining independence of `Entity` type.

## Structure recognizer

The structure recognizer is independent of the content of Cells (i.e. it does not need to know what an `Entity` is), except:
1. during initialization
2. when retrieving the original cell content after recognition

Type parameters for three kinds of data have been added to the recognizer:
1. `Cell`/`PCell`
2. `Entity`
3. `EntityName`

Eventually it may be possible to eliminate one or two of these type parameters, with some refactoring.
This commit is contained in:
Karl Ostmo 2024-06-02 13:53:34 -07:00 committed by GitHub
parent 01c45ab968
commit ca4a2b809d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 343 additions and 244 deletions

View File

@ -4,106 +4,124 @@
<!-- Generated by graphviz version 2.43.0 (0) <!-- Generated by graphviz version 2.43.0 (0)
--> -->
<!-- Title: plan Pages: 1 --> <!-- Title: plan Pages: 1 -->
<svg width="210pt" height="476pt" <svg width="278pt" height="476pt"
viewBox="0.00 0.00 209.50 476.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> viewBox="0.00 0.00 277.50 476.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 472)"> <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 472)">
<title>plan</title> <title>plan</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-472 205.5,-472 205.5,4 -4,4"/> <polygon fill="white" stroke="transparent" points="-4,4 -4,-472 273.5,-472 273.5,4 -4,4"/>
<!-- swarm --> <!-- swarm -->
<g id="node1" class="node"> <g id="node1" class="node">
<title>swarm</title> <title>swarm</title>
<path fill="none" stroke="brown" stroke-width="2" d="M122.5,-36C122.5,-36 81.5,-36 81.5,-36 75.5,-36 69.5,-30 69.5,-24 69.5,-24 69.5,-12 69.5,-12 69.5,-6 75.5,0 81.5,0 81.5,0 122.5,0 122.5,0 128.5,0 134.5,-6 134.5,-12 134.5,-12 134.5,-24 134.5,-24 134.5,-30 128.5,-36 122.5,-36"/> <path fill="none" stroke="brown" stroke-width="2" d="M168,-36C168,-36 127,-36 127,-36 121,-36 115,-30 115,-24 115,-24 115,-12 115,-12 115,-6 121,0 127,0 127,0 168,0 168,0 174,0 180,-6 180,-12 180,-12 180,-24 180,-24 180,-30 174,-36 168,-36"/>
<text text-anchor="middle" x="102" y="-14.3" font-family="Times,serif" font-size="14.00">swarm</text> <text text-anchor="middle" x="147.5" y="-14.3" font-family="Times,serif" font-size="14.00">swarm</text>
</g> </g>
<!-- swarm&#45;web --> <!-- swarm&#45;web -->
<g id="node2" class="node"> <g id="node2" class="node">
<title>swarm&#45;web</title> <title>swarm&#45;web</title>
<path fill="none" stroke="gray" stroke-width="2" d="M139,-108C139,-108 65,-108 65,-108 59,-108 53,-102 53,-96 53,-96 53,-84 53,-84 53,-78 59,-72 65,-72 65,-72 139,-72 139,-72 145,-72 151,-78 151,-84 151,-84 151,-96 151,-96 151,-102 145,-108 139,-108"/> <path fill="none" stroke="gray" stroke-width="2" d="M184.5,-108C184.5,-108 110.5,-108 110.5,-108 104.5,-108 98.5,-102 98.5,-96 98.5,-96 98.5,-84 98.5,-84 98.5,-78 104.5,-72 110.5,-72 110.5,-72 184.5,-72 184.5,-72 190.5,-72 196.5,-78 196.5,-84 196.5,-84 196.5,-96 196.5,-96 196.5,-102 190.5,-108 184.5,-108"/>
<text text-anchor="middle" x="102" y="-86.3" font-family="Times,serif" font-size="14.00">swarm&#45;web</text> <text text-anchor="middle" x="147.5" y="-86.3" font-family="Times,serif" font-size="14.00">swarm&#45;web</text>
</g> </g>
<!-- swarm&#45;&gt;swarm&#45;web --> <!-- swarm&#45;&gt;swarm&#45;web -->
<g id="edge1" class="edge"> <g id="edge1" class="edge">
<title>swarm&#45;&gt;swarm&#45;web</title> <title>swarm&#45;&gt;swarm&#45;web</title>
<path fill="none" stroke="brown" d="M102,-36.3C102,-44.02 102,-53.29 102,-61.89"/> <path fill="none" stroke="brown" d="M147.5,-36.3C147.5,-44.02 147.5,-53.29 147.5,-61.89"/>
<polygon fill="brown" stroke="brown" points="98.5,-61.9 102,-71.9 105.5,-61.9 98.5,-61.9"/> <polygon fill="brown" stroke="brown" points="144,-61.9 147.5,-71.9 151,-61.9 144,-61.9"/>
</g> </g>
<!-- swarm&#45;doc --> <!-- swarm&#45;doc -->
<g id="node3" class="node"> <g id="node3" class="node">
<title>swarm&#45;doc</title> <title>swarm&#45;doc</title>
<path fill="none" stroke="gray" stroke-width="2" d="M82,-180C82,-180 12,-180 12,-180 6,-180 0,-174 0,-168 0,-168 0,-156 0,-156 0,-150 6,-144 12,-144 12,-144 82,-144 82,-144 88,-144 94,-150 94,-156 94,-156 94,-168 94,-168 94,-174 88,-180 82,-180"/> <path fill="none" stroke="gray" stroke-width="2" d="M127.5,-180C127.5,-180 57.5,-180 57.5,-180 51.5,-180 45.5,-174 45.5,-168 45.5,-168 45.5,-156 45.5,-156 45.5,-150 51.5,-144 57.5,-144 57.5,-144 127.5,-144 127.5,-144 133.5,-144 139.5,-150 139.5,-156 139.5,-156 139.5,-168 139.5,-168 139.5,-174 133.5,-180 127.5,-180"/>
<text text-anchor="middle" x="47" y="-158.3" font-family="Times,serif" font-size="14.00">swarm&#45;doc</text> <text text-anchor="middle" x="92.5" y="-158.3" font-family="Times,serif" font-size="14.00">swarm&#45;doc</text>
</g> </g>
<!-- swarm&#45;web&#45;&gt;swarm&#45;doc --> <!-- swarm&#45;web&#45;&gt;swarm&#45;doc -->
<g id="edge2" class="edge"> <g id="edge2" class="edge">
<title>swarm&#45;web&#45;&gt;swarm&#45;doc</title> <title>swarm&#45;web&#45;&gt;swarm&#45;doc</title>
<path fill="none" stroke="gray" d="M88.4,-108.3C81.88,-116.61 73.93,-126.72 66.75,-135.86"/> <path fill="none" stroke="gray" d="M133.9,-108.3C127.38,-116.61 119.43,-126.72 112.25,-135.86"/>
<polygon fill="gray" stroke="gray" points="63.87,-133.87 60.44,-143.9 69.37,-138.19 63.87,-133.87"/> <polygon fill="gray" stroke="gray" points="109.37,-133.87 105.94,-143.9 114.87,-138.19 109.37,-133.87"/>
</g> </g>
<!-- swarm&#45;tui --> <!-- swarm&#45;tui -->
<g id="node4" class="node"> <g id="node4" class="node">
<title>swarm&#45;tui</title> <title>swarm&#45;tui</title>
<path fill="none" stroke="gray" stroke-width="2" d="M189.5,-180C189.5,-180 124.5,-180 124.5,-180 118.5,-180 112.5,-174 112.5,-168 112.5,-168 112.5,-156 112.5,-156 112.5,-150 118.5,-144 124.5,-144 124.5,-144 189.5,-144 189.5,-144 195.5,-144 201.5,-150 201.5,-156 201.5,-156 201.5,-168 201.5,-168 201.5,-174 195.5,-180 189.5,-180"/> <path fill="none" stroke="gray" stroke-width="2" d="M235,-180C235,-180 170,-180 170,-180 164,-180 158,-174 158,-168 158,-168 158,-156 158,-156 158,-150 164,-144 170,-144 170,-144 235,-144 235,-144 241,-144 247,-150 247,-156 247,-156 247,-168 247,-168 247,-174 241,-180 235,-180"/>
<text text-anchor="middle" x="157" y="-158.3" font-family="Times,serif" font-size="14.00">swarm&#45;tui</text> <text text-anchor="middle" x="202.5" y="-158.3" font-family="Times,serif" font-size="14.00">swarm&#45;tui</text>
</g> </g>
<!-- swarm&#45;web&#45;&gt;swarm&#45;tui --> <!-- swarm&#45;web&#45;&gt;swarm&#45;tui -->
<g id="edge3" class="edge"> <g id="edge3" class="edge">
<title>swarm&#45;web&#45;&gt;swarm&#45;tui</title> <title>swarm&#45;web&#45;&gt;swarm&#45;tui</title>
<path fill="none" stroke="gray" d="M115.6,-108.3C122.12,-116.61 130.07,-126.72 137.25,-135.86"/> <path fill="none" stroke="gray" d="M161.1,-108.3C167.62,-116.61 175.57,-126.72 182.75,-135.86"/>
<polygon fill="gray" stroke="gray" points="134.63,-138.19 143.56,-143.9 140.13,-133.87 134.63,-138.19"/> <polygon fill="gray" stroke="gray" points="180.13,-138.19 189.06,-143.9 185.63,-133.87 180.13,-138.19"/>
</g> </g>
<!-- swarm&#45;engine --> <!-- swarm&#45;engine -->
<g id="node5" class="node"> <g id="node5" class="node">
<title>swarm&#45;engine</title> <title>swarm&#45;engine</title>
<path fill="none" stroke="gray" stroke-width="2" d="M148.5,-252C148.5,-252 55.5,-252 55.5,-252 49.5,-252 43.5,-246 43.5,-240 43.5,-240 43.5,-228 43.5,-228 43.5,-222 49.5,-216 55.5,-216 55.5,-216 148.5,-216 148.5,-216 154.5,-216 160.5,-222 160.5,-228 160.5,-228 160.5,-240 160.5,-240 160.5,-246 154.5,-252 148.5,-252"/> <path fill="none" stroke="gray" stroke-width="2" d="M194,-252C194,-252 101,-252 101,-252 95,-252 89,-246 89,-240 89,-240 89,-228 89,-228 89,-222 95,-216 101,-216 101,-216 194,-216 194,-216 200,-216 206,-222 206,-228 206,-228 206,-240 206,-240 206,-246 200,-252 194,-252"/>
<text text-anchor="middle" x="102" y="-230.3" font-family="Times,serif" font-size="14.00">swarm&#45;engine</text> <text text-anchor="middle" x="147.5" y="-230.3" font-family="Times,serif" font-size="14.00">swarm&#45;engine</text>
</g> </g>
<!-- swarm&#45;doc&#45;&gt;swarm&#45;engine --> <!-- swarm&#45;doc&#45;&gt;swarm&#45;engine -->
<g id="edge4" class="edge"> <g id="edge4" class="edge">
<title>swarm&#45;doc&#45;&gt;swarm&#45;engine</title> <title>swarm&#45;doc&#45;&gt;swarm&#45;engine</title>
<path fill="none" stroke="gray" d="M60.6,-180.3C67.12,-188.61 75.07,-198.72 82.25,-207.86"/> <path fill="none" stroke="gray" d="M106.1,-180.3C112.62,-188.61 120.57,-198.72 127.75,-207.86"/>
<polygon fill="gray" stroke="gray" points="79.63,-210.19 88.56,-215.9 85.13,-205.87 79.63,-210.19"/> <polygon fill="gray" stroke="gray" points="125.13,-210.19 134.06,-215.9 130.63,-205.87 125.13,-210.19"/>
</g> </g>
<!-- swarm&#45;tui&#45;&gt;swarm&#45;engine --> <!-- swarm&#45;tui&#45;&gt;swarm&#45;engine -->
<g id="edge5" class="edge"> <g id="edge5" class="edge">
<title>swarm&#45;tui&#45;&gt;swarm&#45;engine</title> <title>swarm&#45;tui&#45;&gt;swarm&#45;engine</title>
<path fill="none" stroke="gray" d="M143.4,-180.3C136.88,-188.61 128.93,-198.72 121.75,-207.86"/> <path fill="none" stroke="gray" d="M188.9,-180.3C182.38,-188.61 174.43,-198.72 167.25,-207.86"/>
<polygon fill="gray" stroke="gray" points="118.87,-205.87 115.44,-215.9 124.37,-210.19 118.87,-205.87"/> <polygon fill="gray" stroke="gray" points="164.37,-205.87 160.94,-215.9 169.87,-210.19 164.37,-205.87"/>
</g> </g>
<!-- swarm&#45;scenario --> <!-- swarm&#45;scenario -->
<g id="node6" class="node"> <g id="node6" class="node">
<title>swarm&#45;scenario</title> <title>swarm&#45;scenario</title>
<path fill="none" stroke="gray" stroke-width="2" d="M154.5,-324C154.5,-324 49.5,-324 49.5,-324 43.5,-324 37.5,-318 37.5,-312 37.5,-312 37.5,-300 37.5,-300 37.5,-294 43.5,-288 49.5,-288 49.5,-288 154.5,-288 154.5,-288 160.5,-288 166.5,-294 166.5,-300 166.5,-300 166.5,-312 166.5,-312 166.5,-318 160.5,-324 154.5,-324"/> <path fill="none" stroke="gray" stroke-width="2" d="M200,-324C200,-324 95,-324 95,-324 89,-324 83,-318 83,-312 83,-312 83,-300 83,-300 83,-294 89,-288 95,-288 95,-288 200,-288 200,-288 206,-288 212,-294 212,-300 212,-300 212,-312 212,-312 212,-318 206,-324 200,-324"/>
<text text-anchor="middle" x="102" y="-302.3" font-family="Times,serif" font-size="14.00">swarm&#45;scenario</text> <text text-anchor="middle" x="147.5" y="-302.3" font-family="Times,serif" font-size="14.00">swarm&#45;scenario</text>
</g> </g>
<!-- swarm&#45;engine&#45;&gt;swarm&#45;scenario --> <!-- swarm&#45;engine&#45;&gt;swarm&#45;scenario -->
<g id="edge6" class="edge"> <g id="edge6" class="edge">
<title>swarm&#45;engine&#45;&gt;swarm&#45;scenario</title> <title>swarm&#45;engine&#45;&gt;swarm&#45;scenario</title>
<path fill="none" stroke="gray" d="M102,-252.3C102,-260.02 102,-269.29 102,-277.89"/> <path fill="none" stroke="gray" d="M147.5,-252.3C147.5,-260.02 147.5,-269.29 147.5,-277.89"/>
<polygon fill="gray" stroke="gray" points="98.5,-277.9 102,-287.9 105.5,-277.9 98.5,-277.9"/> <polygon fill="gray" stroke="gray" points="144,-277.9 147.5,-287.9 151,-277.9 144,-277.9"/>
</g>
<!-- swarm&#45;topography -->
<g id="node7" class="node">
<title>swarm&#45;topography</title>
<path fill="none" stroke="gray" stroke-width="2" d="M139,-396C139,-396 12,-396 12,-396 6,-396 0,-390 0,-384 0,-384 0,-372 0,-372 0,-366 6,-360 12,-360 12,-360 139,-360 139,-360 145,-360 151,-366 151,-372 151,-372 151,-384 151,-384 151,-390 145,-396 139,-396"/>
<text text-anchor="middle" x="75.5" y="-374.3" font-family="Times,serif" font-size="14.00">swarm&#45;topography</text>
</g>
<!-- swarm&#45;scenario&#45;&gt;swarm&#45;topography -->
<g id="edge7" class="edge">
<title>swarm&#45;scenario&#45;&gt;swarm&#45;topography</title>
<path fill="none" stroke="gray" d="M129.7,-324.3C120.9,-332.86 110.12,-343.34 100.5,-352.7"/>
<polygon fill="gray" stroke="gray" points="97.82,-350.42 93.09,-359.9 102.7,-355.43 97.82,-350.42"/>
</g> </g>
<!-- swarm&#45;lang --> <!-- swarm&#45;lang -->
<g id="node7" class="node"> <g id="node8" class="node">
<title>swarm&#45;lang</title> <title>swarm&#45;lang</title>
<path fill="none" stroke="gray" stroke-width="2" d="M140,-396C140,-396 64,-396 64,-396 58,-396 52,-390 52,-384 52,-384 52,-372 52,-372 52,-366 58,-360 64,-360 64,-360 140,-360 140,-360 146,-360 152,-366 152,-372 152,-372 152,-384 152,-384 152,-390 146,-396 140,-396"/> <path fill="none" stroke="gray" stroke-width="2" d="M257.5,-396C257.5,-396 181.5,-396 181.5,-396 175.5,-396 169.5,-390 169.5,-384 169.5,-384 169.5,-372 169.5,-372 169.5,-366 175.5,-360 181.5,-360 181.5,-360 257.5,-360 257.5,-360 263.5,-360 269.5,-366 269.5,-372 269.5,-372 269.5,-384 269.5,-384 269.5,-390 263.5,-396 257.5,-396"/>
<text text-anchor="middle" x="102" y="-374.3" font-family="Times,serif" font-size="14.00">swarm&#45;lang</text> <text text-anchor="middle" x="219.5" y="-374.3" font-family="Times,serif" font-size="14.00">swarm&#45;lang</text>
</g> </g>
<!-- swarm&#45;scenario&#45;&gt;swarm&#45;lang --> <!-- swarm&#45;scenario&#45;&gt;swarm&#45;lang -->
<g id="edge7" class="edge"> <g id="edge8" class="edge">
<title>swarm&#45;scenario&#45;&gt;swarm&#45;lang</title> <title>swarm&#45;scenario&#45;&gt;swarm&#45;lang</title>
<path fill="none" stroke="gray" d="M102,-324.3C102,-332.02 102,-341.29 102,-349.89"/> <path fill="none" stroke="gray" d="M165.3,-324.3C174.1,-332.86 184.88,-343.34 194.5,-352.7"/>
<polygon fill="gray" stroke="gray" points="98.5,-349.9 102,-359.9 105.5,-349.9 98.5,-349.9"/> <polygon fill="gray" stroke="gray" points="192.3,-355.43 201.91,-359.9 197.18,-350.42 192.3,-355.43"/>
</g> </g>
<!-- swarm&#45;util --> <!-- swarm&#45;util -->
<g id="node8" class="node"> <g id="node9" class="node">
<title>swarm&#45;util</title> <title>swarm&#45;util</title>
<path fill="none" stroke="gray" stroke-width="2" d="M136.5,-468C136.5,-468 67.5,-468 67.5,-468 61.5,-468 55.5,-462 55.5,-456 55.5,-456 55.5,-444 55.5,-444 55.5,-438 61.5,-432 67.5,-432 67.5,-432 136.5,-432 136.5,-432 142.5,-432 148.5,-438 148.5,-444 148.5,-444 148.5,-456 148.5,-456 148.5,-462 142.5,-468 136.5,-468"/> <path fill="none" stroke="gray" stroke-width="2" d="M182,-468C182,-468 113,-468 113,-468 107,-468 101,-462 101,-456 101,-456 101,-444 101,-444 101,-438 107,-432 113,-432 113,-432 182,-432 182,-432 188,-432 194,-438 194,-444 194,-444 194,-456 194,-456 194,-462 188,-468 182,-468"/>
<text text-anchor="middle" x="102" y="-446.3" font-family="Times,serif" font-size="14.00">swarm&#45;util</text> <text text-anchor="middle" x="147.5" y="-446.3" font-family="Times,serif" font-size="14.00">swarm&#45;util</text>
</g>
<!-- swarm&#45;topography&#45;&gt;swarm&#45;util -->
<g id="edge9" class="edge">
<title>swarm&#45;topography&#45;&gt;swarm&#45;util</title>
<path fill="none" stroke="gray" d="M93.3,-396.3C102.1,-404.86 112.88,-415.34 122.5,-424.7"/>
<polygon fill="gray" stroke="gray" points="120.3,-427.43 129.91,-431.9 125.18,-422.42 120.3,-427.43"/>
</g> </g>
<!-- swarm&#45;lang&#45;&gt;swarm&#45;util --> <!-- swarm&#45;lang&#45;&gt;swarm&#45;util -->
<g id="edge8" class="edge"> <g id="edge10" class="edge">
<title>swarm&#45;lang&#45;&gt;swarm&#45;util</title> <title>swarm&#45;lang&#45;&gt;swarm&#45;util</title>
<path fill="none" stroke="gray" d="M102,-396.3C102,-404.02 102,-413.29 102,-421.89"/> <path fill="none" stroke="gray" d="M201.7,-396.3C192.9,-404.86 182.12,-415.34 172.5,-424.7"/>
<polygon fill="gray" stroke="gray" points="98.5,-421.9 102,-431.9 105.5,-421.9 98.5,-421.9"/> <polygon fill="gray" stroke="gray" points="169.82,-422.42 165.09,-431.9 174.7,-427.43 169.82,-422.42"/>
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -1,7 +1,5 @@
#!/bin/bash -xe #!/bin/bash -xe
cd $(git rev-parse --show-toplevel)
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) cabal bench --benchmark-options "--color always $@"
cd $SCRIPT_DIR/..
STACK_WORK=.stack-work-bench stack bench swarm:benchmark --benchmark-arguments "--color always $@"

View File

@ -19,7 +19,7 @@ module Swarm.Doc.Keyword (
import Data.Text (Text) import Data.Text (Text)
import Data.Text qualified as T import Data.Text qualified as T
import Swarm.Doc.Util import Swarm.Doc.Util
import Swarm.Language.Syntax qualified as Syntax import Swarm.Language.Syntax.Direction
import Swarm.Util (quote) import Swarm.Util (quote)
-- | An enumeration of the editors supported by Swarm (currently, -- | An enumeration of the editors supported by Swarm (currently,
@ -42,7 +42,7 @@ keywordsCommands e = editorList e $ map constSyntax commands
-- | Get formatted list of directions. -- | Get formatted list of directions.
keywordsDirections :: EditorType -> Text keywordsDirections :: EditorType -> Text
keywordsDirections e = editorList e $ map Syntax.directionSyntax Syntax.allDirs keywordsDirections e = editorList e $ map directionSyntax allDirs
-- | A list of the names of all the operators in the language. -- | A list of the names of all the operators in the language.
operatorNames :: Text operatorNames :: Text

View File

@ -26,11 +26,12 @@ import Data.Set qualified as S
import Linear (V2 (..)) import Linear (V2 (..))
import Swarm.Game.Entity import Swarm.Game.Entity
import Swarm.Game.Location import Swarm.Game.Location
import Swarm.Game.Scenario.Topography.Structure qualified as Structure import Swarm.Game.Scenario (Cell)
import Swarm.Game.Scenario.Topography.Structure.Recognition import Swarm.Game.Scenario.Topography.Structure.Recognition
import Swarm.Game.Scenario.Topography.Structure.Recognition.Log import Swarm.Game.Scenario.Topography.Structure.Recognition.Log
import Swarm.Game.Scenario.Topography.Structure.Recognition.Registry import Swarm.Game.Scenario.Topography.Structure.Recognition.Registry
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type import Swarm.Game.Scenario.Topography.Structure.Recognition.Type
import Swarm.Game.Scenario.Topography.Structure.Type qualified as Structure
import Swarm.Game.State import Swarm.Game.State
import Swarm.Game.State.Substate import Swarm.Game.State.Substate
import Swarm.Game.Universe import Swarm.Game.Universe
@ -124,7 +125,7 @@ getWorldRow participatingEnts cLoc (InspectionOffsets (Min offsetLeft) (Max offs
registerRowMatches :: registerRowMatches ::
(Has (State GameState) sig m) => (Has (State GameState) sig m) =>
Cosmic Location -> Cosmic Location ->
AutomatonInfo AtomicKeySymbol StructureSearcher -> AutomatonInfo EntityName (AtomicKeySymbol Entity) (StructureSearcher Cell EntityName Entity) ->
m () m ()
registerRowMatches cLoc (AutomatonInfo participatingEnts horizontalOffsets sm) = do registerRowMatches cLoc (AutomatonInfo participatingEnts horizontalOffsets sm) = do
entitiesRow <- getWorldRow participatingEnts cLoc horizontalOffsets 0 entitiesRow <- getWorldRow participatingEnts cLoc horizontalOffsets 0
@ -150,8 +151,8 @@ checkVerticalMatch ::
Cosmic Location -> Cosmic Location ->
-- | Horizontal search offsets -- | Horizontal search offsets
InspectionOffsets -> InspectionOffsets ->
Position StructureSearcher -> Position (StructureSearcher Cell EntityName Entity) ->
m [FoundStructure] m [FoundStructure Cell Entity]
checkVerticalMatch cLoc (InspectionOffsets (Min searchOffsetLeft) _) foundRow = checkVerticalMatch cLoc (InspectionOffsets (Min searchOffsetLeft) _) foundRow =
getMatches2D cLoc horizontalFoundOffsets $ automaton2D $ pVal foundRow getMatches2D cLoc horizontalFoundOffsets $ automaton2D $ pVal foundRow
where where
@ -163,9 +164,9 @@ getFoundStructures ::
Hashable keySymb => Hashable keySymb =>
(Int32, Int32) -> (Int32, Int32) ->
Cosmic Location -> Cosmic Location ->
StateMachine keySymb StructureWithGrid -> StateMachine keySymb (StructureWithGrid Cell Entity) ->
[keySymb] -> [keySymb] ->
[FoundStructure] [FoundStructure Cell Entity]
getFoundStructures (offsetTop, offsetLeft) cLoc sm entityRows = getFoundStructures (offsetTop, offsetLeft) cLoc sm entityRows =
map mkFound candidates map mkFound candidates
where where
@ -181,8 +182,8 @@ getMatches2D ::
Cosmic Location -> Cosmic Location ->
-- | Horizontal found offsets (inclusive indices) -- | Horizontal found offsets (inclusive indices)
InspectionOffsets -> InspectionOffsets ->
AutomatonInfo SymbolSequence StructureWithGrid -> AutomatonInfo EntityName (SymbolSequence Entity) (StructureWithGrid Cell Entity) ->
m [FoundStructure] m [FoundStructure Cell Entity]
getMatches2D getMatches2D
cLoc cLoc
horizontalFoundOffsets@(InspectionOffsets (Min offsetLeft) _) horizontalFoundOffsets@(InspectionOffsets (Min offsetLeft) _)
@ -199,7 +200,7 @@ getMatches2D
-- The largest structure (by area) shall win. -- The largest structure (by area) shall win.
registerStructureMatches :: registerStructureMatches ::
(Has (State GameState) sig m) => (Has (State GameState) sig m) =>
[FoundStructure] -> [FoundStructure Cell Entity] ->
m () m ()
registerStructureMatches unrankedCandidates = do registerStructureMatches unrankedCandidates = do
discovery . structureRecognition . recognitionLog %= (newMsg :) discovery . structureRecognition . recognitionLog %= (newMsg :)

View File

@ -117,11 +117,11 @@ import Swarm.Game.Robot.Concrete
import Swarm.Game.Scenario import Swarm.Game.Scenario
import Swarm.Game.Scenario.Objective import Swarm.Game.Scenario.Objective
import Swarm.Game.Scenario.Status import Swarm.Game.Scenario.Status
import Swarm.Game.Scenario.Topography.Structure qualified as Structure
import Swarm.Game.Scenario.Topography.Structure.Recognition import Swarm.Game.Scenario.Topography.Structure.Recognition
import Swarm.Game.Scenario.Topography.Structure.Recognition.Log import Swarm.Game.Scenario.Topography.Structure.Recognition.Log
import Swarm.Game.Scenario.Topography.Structure.Recognition.Precompute import Swarm.Game.Scenario.Topography.Structure.Recognition.Precompute
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type import Swarm.Game.Scenario.Topography.Structure.Recognition.Type
import Swarm.Game.Scenario.Topography.Structure.Type qualified as Structure
import Swarm.Game.State.Landscape import Swarm.Game.State.Landscape
import Swarm.Game.State.Robot import Swarm.Game.State.Robot
import Swarm.Game.State.Substate import Swarm.Game.State.Substate
@ -525,7 +525,7 @@ zoomWorld swName n = do
-- cell is encountered. -- cell is encountered.
ensureStructureIntact :: ensureStructureIntact ::
(Has (State GameState) sig m) => (Has (State GameState) sig m) =>
FoundStructure -> FoundStructure Cell Entity ->
m Bool m Bool
ensureStructureIntact (FoundStructure (StructureWithGrid _ _ grid) upperLeft) = ensureStructureIntact (FoundStructure (StructureWithGrid _ _ grid) upperLeft) =
allM outer $ zip [0 ..] grid allM outer $ zip [0 ..] grid
@ -541,7 +541,7 @@ ensureStructureIntact (FoundStructure (StructureWithGrid _ _ grid) upperLeft) =
mkRecognizer :: mkRecognizer ::
(Has (State GameState) sig m) => (Has (State GameState) sig m) =>
StaticStructureInfo -> StaticStructureInfo ->
m StructureRecognizer m (StructureRecognizer Cell EntityName Entity)
mkRecognizer structInfo@(StaticStructureInfo structDefs _) = do mkRecognizer structInfo@(StaticStructureInfo structDefs _) = do
foundIntact <- mapM (sequenceA . (id &&& ensureStructureIntact)) allPlaced foundIntact <- mapM (sequenceA . (id &&& ensureStructureIntact)) allPlaced
let fs = populateStaticFoundStructures . map fst . filter snd $ foundIntact let fs = populateStaticFoundStructures . map fst . filter snd $ foundIntact

View File

@ -100,7 +100,7 @@ import Swarm.Game.Recipe (
outRecipeMap, outRecipeMap,
) )
import Swarm.Game.Robot import Swarm.Game.Robot
import Swarm.Game.Scenario (GameStateInputs (..)) import Swarm.Game.Scenario (Cell, GameStateInputs (..))
import Swarm.Game.Scenario.Objective import Swarm.Game.Scenario.Objective
import Swarm.Game.Scenario.Topography.Structure.Recognition import Swarm.Game.Scenario.Topography.Structure.Recognition
import Swarm.Game.Scenario.Topography.Structure.Recognition.Registry (emptyFoundStructures) import Swarm.Game.Scenario.Topography.Structure.Recognition.Registry (emptyFoundStructures)
@ -327,7 +327,7 @@ data Discovery = Discovery
, _availableCommands :: Notifications Const , _availableCommands :: Notifications Const
, _knownEntities :: S.Set EntityName , _knownEntities :: S.Set EntityName
, _gameAchievements :: Map GameplayAchievement Attainment , _gameAchievements :: Map GameplayAchievement Attainment
, _structureRecognition :: StructureRecognizer , _structureRecognition :: StructureRecognizer Cell EntityName Entity
, _tagMembers :: Map Text (NonEmpty EntityName) , _tagMembers :: Map Text (NonEmpty EntityName)
} }
@ -350,7 +350,7 @@ knownEntities :: Lens' Discovery (S.Set EntityName)
gameAchievements :: Lens' Discovery (Map GameplayAchievement Attainment) gameAchievements :: Lens' Discovery (Map GameplayAchievement Attainment)
-- | Recognizer for robot-constructed structures -- | Recognizer for robot-constructed structures
structureRecognition :: Lens' Discovery StructureRecognizer structureRecognition :: Lens' Discovery (StructureRecognizer Cell EntityName Entity)
-- | Map from tags to entities that possess that tag -- | Map from tags to entities that possess that tag
tagMembers :: Lens' Discovery (Map Text (NonEmpty EntityName)) tagMembers :: Lens' Discovery (Map Text (NonEmpty EntityName))

View File

@ -44,6 +44,7 @@ import Swarm.Language.Context (empty)
import Swarm.Language.Pipeline (ProcessedTerm) import Swarm.Language.Pipeline (ProcessedTerm)
import Swarm.Language.Pipeline.QQ (tmQ) import Swarm.Language.Pipeline.QQ (tmQ)
import Swarm.Language.Syntax import Swarm.Language.Syntax
import Swarm.Language.Syntax.Direction (Direction)
import Swarm.Language.Text.Markdown qualified as Markdown import Swarm.Language.Text.Markdown qualified as Markdown
import Swarm.Util hiding (both) import Swarm.Util hiding (both)
import System.Clock (TimeSpec) import System.Clock (TimeSpec)

View File

@ -94,6 +94,7 @@ import Swarm.Language.Pipeline
import Swarm.Language.Pretty (prettyText) import Swarm.Language.Pretty (prettyText)
import Swarm.Language.Requirement qualified as R import Swarm.Language.Requirement qualified as R
import Swarm.Language.Syntax import Swarm.Language.Syntax
import Swarm.Language.Syntax.Direction
import Swarm.Language.Text.Markdown qualified as Markdown import Swarm.Language.Text.Markdown qualified as Markdown
import Swarm.Language.Value import Swarm.Language.Value
import Swarm.Log import Swarm.Log

View File

@ -48,6 +48,7 @@ import Swarm.Game.Step.Util
import Swarm.Game.Step.Util.Inspect import Swarm.Game.Step.Util.Inspect
import Swarm.Game.Universe import Swarm.Game.Universe
import Swarm.Language.Syntax import Swarm.Language.Syntax
import Swarm.Language.Syntax.Direction
import Swarm.Util (hoistMaybe) import Swarm.Util (hoistMaybe)
-- | Swarm command arguments are converted to idiomatic Haskell -- | Swarm command arguments are converted to idiomatic Haskell

View File

@ -42,6 +42,7 @@ import Swarm.Game.World.Modify qualified as WM
import Swarm.Language.Capability import Swarm.Language.Capability
import Swarm.Language.Requirement qualified as R import Swarm.Language.Requirement qualified as R
import Swarm.Language.Syntax import Swarm.Language.Syntax
import Swarm.Language.Syntax.Direction (Direction)
import Swarm.Util hiding (both) import Swarm.Util hiding (both)
import System.Random (UniformRange, uniformR) import System.Random (UniformRange, uniformR)
import Prelude hiding (Applicative (..), lookup) import Prelude hiding (Applicative (..), lookup)

View File

@ -56,6 +56,7 @@ import Data.Text (Text)
import Data.Text qualified as T import Data.Text qualified as T
import Swarm.Language.Parser.Core import Swarm.Language.Parser.Core
import Swarm.Language.Syntax import Swarm.Language.Syntax
import Swarm.Language.Syntax.Direction
import Swarm.Language.Types (baseTyName) import Swarm.Language.Types (baseTyName)
import Swarm.Util (failT, listEnums, squote) import Swarm.Util (failT, listEnums, squote)
import Text.Megaparsec import Text.Megaparsec

View File

@ -21,6 +21,7 @@ import Swarm.Language.Parser.Lex
import Swarm.Language.Parser.Record (parseRecord) import Swarm.Language.Parser.Record (parseRecord)
import Swarm.Language.Parser.Type import Swarm.Language.Parser.Type
import Swarm.Language.Syntax import Swarm.Language.Syntax
import Swarm.Language.Syntax.Direction
import Swarm.Language.Types import Swarm.Language.Types
import Swarm.Util (failT, findDup) import Swarm.Util (failT, findDup)
import Text.Megaparsec hiding (runParser) import Text.Megaparsec hiding (runParser)

View File

@ -32,6 +32,7 @@ import Swarm.Language.Context
import Swarm.Language.Kindcheck (KindError (..)) import Swarm.Language.Kindcheck (KindError (..))
import Swarm.Language.Parser.Util (getLocRange) import Swarm.Language.Parser.Util (getLocRange)
import Swarm.Language.Syntax import Swarm.Language.Syntax
import Swarm.Language.Syntax.Direction
import Swarm.Language.Typecheck import Swarm.Language.Typecheck
import Swarm.Language.Types import Swarm.Language.Types
import Swarm.Util (number, showEnum, showLowT, unsnocNE) import Swarm.Util (number, showEnum, showLowT, unsnocNE)

View File

@ -37,6 +37,7 @@ import Swarm.Language.Capability (Capability (..), constCaps)
import Swarm.Language.Context (Ctx) import Swarm.Language.Context (Ctx)
import Swarm.Language.Context qualified as Ctx import Swarm.Language.Context qualified as Ctx
import Swarm.Language.Syntax import Swarm.Language.Syntax
import Swarm.Language.Syntax.Direction
-- | A /requirement/ is something a robot must have when it is -- | A /requirement/ is something a robot must have when it is
-- built. There are three types: -- built. There are three types:

View File

@ -5,15 +5,6 @@
-- --
-- Abstract syntax for terms of the Swarm programming language. -- Abstract syntax for terms of the Swarm programming language.
module Swarm.Language.Syntax ( module Swarm.Language.Syntax (
-- * Directions
Direction (..),
AbsoluteDir (..),
RelativeDir (..),
PlanarRelativeDir (..),
directionSyntax,
isCardinal,
allDirs,
-- * Constants -- * Constants
Const (..), Const (..),
allConst, allConst,
@ -104,7 +95,6 @@ module Swarm.Language.Syntax (
import Swarm.Language.Syntax.AST import Swarm.Language.Syntax.AST
import Swarm.Language.Syntax.Comments import Swarm.Language.Syntax.Comments
import Swarm.Language.Syntax.Constants import Swarm.Language.Syntax.Constants
import Swarm.Language.Syntax.Direction
import Swarm.Language.Syntax.Loc import Swarm.Language.Syntax.Loc
import Swarm.Language.Syntax.Pattern import Swarm.Language.Syntax.Pattern
import Swarm.Language.Syntax.Util import Swarm.Language.Syntax.Util

View File

@ -31,6 +31,7 @@ import Swarm.Language.Context
import Swarm.Language.Key (KeyCombo, prettyKeyCombo) import Swarm.Language.Key (KeyCombo, prettyKeyCombo)
import Swarm.Language.Pretty (prettyText) import Swarm.Language.Pretty (prettyText)
import Swarm.Language.Syntax import Swarm.Language.Syntax
import Swarm.Language.Syntax.Direction
-- | A /value/ is a term that cannot (or does not) take any more -- | A /value/ is a term that cannot (or does not) take any more
-- evaluation steps on its own. -- evaluation steps on its own.

View File

@ -48,7 +48,7 @@ import Data.Text qualified as T
import Data.Yaml import Data.Yaml
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Graphics.Text.Width import Graphics.Text.Width
import Swarm.Language.Syntax (AbsoluteDir (..), Direction (..)) import Swarm.Language.Syntax.Direction (AbsoluteDir (..), Direction (..))
import Swarm.Util (maxOn, quote) import Swarm.Util (maxOn, quote)
import Swarm.Util.Lens (makeLensesNoSigs) import Swarm.Util.Lens (makeLensesNoSigs)
import Swarm.Util.Yaml (FromJSONE (..), With (runE), getE, liftE, withObjectE) import Swarm.Util.Yaml (FromJSONE (..), With (runE), getE, liftE, withObjectE)

View File

@ -102,6 +102,7 @@ import Swarm.Game.Scenario.Topography.Structure.Assembly qualified as Assembly
import Swarm.Game.Scenario.Topography.Structure.Overlay import Swarm.Game.Scenario.Topography.Structure.Overlay
import Swarm.Game.Scenario.Topography.Structure.Recognition.Symmetry import Swarm.Game.Scenario.Topography.Structure.Recognition.Symmetry
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type (SymmetryAnnotatedGrid (..)) import Swarm.Game.Scenario.Topography.Structure.Recognition.Type (SymmetryAnnotatedGrid (..))
import Swarm.Game.Scenario.Topography.Structure.Type qualified as Structure
import Swarm.Game.Scenario.Topography.WorldDescription import Swarm.Game.Scenario.Topography.WorldDescription
import Swarm.Game.Terrain import Swarm.Game.Terrain
import Swarm.Game.Universe import Swarm.Game.Universe

View File

@ -1,5 +1,6 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- | -- |
-- SPDX-License-Identifier: BSD-3-Clause -- SPDX-License-Identifier: BSD-3-Clause
@ -11,45 +12,22 @@ module Swarm.Game.Scenario.Topography.Structure where
import Data.Aeson.Key qualified as Key import Data.Aeson.Key qualified as Key
import Data.Aeson.KeyMap qualified as KeyMap import Data.Aeson.KeyMap qualified as KeyMap
import Data.Maybe (catMaybes) import Data.Maybe (catMaybes)
import Data.Set (Set)
import Data.Text (Text) import Data.Text (Text)
import Data.Text qualified as T import Data.Text qualified as T
import Data.Yaml as Y import Data.Yaml as Y
import Swarm.Game.Land import Swarm.Game.Land
import Swarm.Game.Location import Swarm.Game.Location
import Swarm.Game.Scenario.RobotLookup import Swarm.Game.Scenario.RobotLookup (RobotMap)
import Swarm.Game.Scenario.Topography.Area import Swarm.Game.Scenario.Topography.Area
import Swarm.Game.Scenario.Topography.Cell import Swarm.Game.Scenario.Topography.Cell
import Swarm.Game.Scenario.Topography.Navigation.Waypoint import Swarm.Game.Scenario.Topography.Navigation.Waypoint
import Swarm.Game.Scenario.Topography.Placement
import Swarm.Game.Scenario.Topography.Structure.Overlay import Swarm.Game.Scenario.Topography.Structure.Overlay
import Swarm.Game.Scenario.Topography.Structure.Type
import Swarm.Game.Scenario.Topography.WorldPalette import Swarm.Game.Scenario.Topography.WorldPalette
import Swarm.Language.Syntax.Direction (AbsoluteDir)
import Swarm.Util (failT, showT) import Swarm.Util (failT, showT)
import Swarm.Util.Yaml import Swarm.Util.Yaml
import Witch (into) import Witch (into)
data NamedArea a = NamedArea
{ name :: StructureName
, recognize :: Set AbsoluteDir
-- ^ whether this structure should be registered for automatic recognition
-- and which orientations shall be recognized.
-- The supplied direction indicates which cardinal direction the
-- original map's "North" has been re-oriented to.
-- E.g., 'DWest' represents a rotation of 90 degrees counter-clockwise.
, description :: Maybe Text
-- ^ will be UI-facing only if this is a recognizable structure
, structure :: a
}
deriving (Eq, Show, Functor)
isRecognizable :: NamedArea a -> Bool
isRecognizable = not . null . recognize
type NamedGrid c = NamedArea (Grid c)
type NamedStructure c = NamedArea (PStructure c)
type InheritedStructureDefs = [NamedStructure (Maybe Cell)] type InheritedStructureDefs = [NamedStructure (Maybe Cell)]
instance FromJSONE (TerrainEntityMaps, RobotMap) (NamedArea (PStructure (Maybe Cell))) where instance FromJSONE (TerrainEntityMaps, RobotMap) (NamedArea (PStructure (Maybe Cell))) where
@ -61,33 +39,6 @@ instance FromJSONE (TerrainEntityMaps, RobotMap) (NamedArea (PStructure (Maybe C
<*> v <*> v
..: "structure" ..: "structure"
data PStructure c = Structure
{ area :: PositionedGrid c
, structures :: [NamedStructure c]
-- ^ structure definitions from parents shall be accessible by children
, placements :: [Placement]
-- ^ earlier placements will be overlaid on top of later placements in the YAML file
, waypoints :: [Waypoint]
}
deriving (Eq, Show)
data Placed c = Placed Placement (NamedStructure c)
deriving (Show)
-- | For use in registering recognizable pre-placed structures
data LocatedStructure = LocatedStructure
{ placedName :: StructureName
, upDirection :: AbsoluteDir
, cornerLoc :: Location
}
deriving (Show)
instance HasLocation LocatedStructure where
modifyLoc f (LocatedStructure x y originalLoc) =
LocatedStructure x y $ f originalLoc
data MergedStructure c = MergedStructure (PositionedGrid c) [LocatedStructure] [Originated Waypoint]
instance FromJSONE (TerrainEntityMaps, RobotMap) (PStructure (Maybe Cell)) where instance FromJSONE (TerrainEntityMaps, RobotMap) (PStructure (Maybe Cell)) where
parseJSONE = withObjectE "structure definition" $ \v -> do parseJSONE = withObjectE "structure definition" $ \v -> do
pal <- v ..:? "palette" ..!= WorldPalette mempty pal <- v ..:? "palette" ..!= WorldPalette mempty

View File

@ -43,6 +43,7 @@ module Swarm.Game.Scenario.Topography.Structure.Recognition.Precompute (
import Control.Arrow ((&&&)) import Control.Arrow ((&&&))
import Control.Lens (view) import Control.Lens (view)
import Data.Hashable (Hashable)
import Data.Int (Int32) import Data.Int (Int32)
import Data.List.NonEmpty qualified as NE import Data.List.NonEmpty qualified as NE
import Data.Map qualified as M import Data.Map qualified as M
@ -51,28 +52,28 @@ import Data.Semigroup (sconcat)
import Data.Set qualified as S import Data.Set qualified as S
import Data.Set qualified as Set import Data.Set qualified as Set
import Data.Tuple (swap) import Data.Tuple (swap)
import Swarm.Game.Entity (Entity, entityName) import Swarm.Game.Entity (Entity, EntityName, entityName)
import Swarm.Game.Scenario (StaticStructureInfo (..)) import Swarm.Game.Scenario (StaticStructureInfo (..))
import Swarm.Game.Scenario.Topography.Area (Grid (Grid)) import Swarm.Game.Scenario.Topography.Area (Grid (Grid))
import Swarm.Game.Scenario.Topography.Cell import Swarm.Game.Scenario.Topography.Cell (PCell, cellEntity)
import Swarm.Game.Scenario.Topography.Placement (Orientation (..), applyOrientationTransform) import Swarm.Game.Scenario.Topography.Placement (Orientation (..), applyOrientationTransform)
import Swarm.Game.Scenario.Topography.Structure
import Swarm.Game.Scenario.Topography.Structure.Recognition.Registry import Swarm.Game.Scenario.Topography.Structure.Recognition.Registry
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type import Swarm.Game.Scenario.Topography.Structure.Recognition.Type
import Swarm.Game.Scenario.Topography.Structure.Type
import Swarm.Game.Universe (Cosmic (..)) import Swarm.Game.Universe (Cosmic (..))
import Swarm.Language.Syntax.Direction (AbsoluteDir) import Swarm.Language.Syntax.Direction (AbsoluteDir)
import Swarm.Util (binTuples, histogram) import Swarm.Util (binTuples, histogram)
import Swarm.Util.Erasable (erasableToMaybe) import Swarm.Util.Erasable (erasableToMaybe)
import Text.AhoCorasick import Text.AhoCorasick
getEntityGrid :: Grid (Maybe Cell) -> [SymbolSequence] getEntityGrid :: Grid (Maybe (PCell Entity)) -> [SymbolSequence Entity]
getEntityGrid (Grid cells) = map (map ((erasableToMaybe . cellEntity) =<<)) cells getEntityGrid (Grid cells) = map (map ((erasableToMaybe . cellEntity) =<<)) cells
allStructureRows :: [StructureWithGrid] -> [StructureRow] allStructureRows :: [StructureWithGrid b a] -> [StructureRow b a]
allStructureRows = allStructureRows =
concatMap getRows concatMap getRows
where where
getRows :: StructureWithGrid -> [StructureRow] getRows :: StructureWithGrid b a -> [StructureRow b a]
getRows g = zipWith (StructureRow g) [0 ..] $ entityGrid g getRows g = zipWith (StructureRow g) [0 ..] $ entityGrid g
mkOffsets :: Foldable f => Int32 -> f a -> InspectionOffsets mkOffsets :: Foldable f => Int32 -> f a -> InspectionOffsets
@ -85,9 +86,11 @@ mkOffsets pos xs =
-- yield a searcher that can determine whether adjacent -- yield a searcher that can determine whether adjacent
-- rows constitute a complete structure. -- rows constitute a complete structure.
mkRowLookup :: mkRowLookup ::
NE.NonEmpty StructureRow -> (Hashable a, Ord en) =>
AutomatonInfo SymbolSequence StructureWithGrid (a -> en) ->
mkRowLookup neList = NE.NonEmpty (StructureRow b a) ->
AutomatonInfo en (SymbolSequence a) (StructureWithGrid b a)
mkRowLookup nameFunc neList =
AutomatonInfo participatingEnts bounds sm AutomatonInfo participatingEnts bounds sm
where where
mkSmTuple = entityGrid &&& id mkSmTuple = entityGrid &&& id
@ -96,10 +99,10 @@ mkRowLookup neList =
-- All of the unique entities across all of the full candidate structures -- All of the unique entities across all of the full candidate structures
participatingEnts = participatingEnts =
S.fromList $ S.fromList $
map (view entityName) $ map nameFunc $
concatMap (concatMap catMaybes . fst) tuples concatMap (concatMap catMaybes . fst) tuples
deriveRowOffsets :: StructureRow -> InspectionOffsets deriveRowOffsets :: StructureRow b a -> InspectionOffsets
deriveRowOffsets (StructureRow (StructureWithGrid _ _ g) rwIdx _) = deriveRowOffsets (StructureRow (StructureWithGrid _ _ g) rwIdx _) =
mkOffsets rwIdx g mkOffsets rwIdx g
@ -113,27 +116,27 @@ mkRowLookup neList =
-- underlying world row against all rows within all structures -- underlying world row against all rows within all structures
-- (so long as they contain the keyed entity). -- (so long as they contain the keyed entity).
mkEntityLookup :: mkEntityLookup ::
[StructureWithGrid] -> (Hashable a, Ord a, Ord en) =>
M.Map Entity (AutomatonInfo AtomicKeySymbol StructureSearcher) (a -> en) ->
mkEntityLookup grids = [StructureWithGrid b a] ->
M.Map a (AutomatonInfo en (AtomicKeySymbol a) (StructureSearcher b en a))
mkEntityLookup nameFunc grids =
M.map mkValues rowsByEntityParticipation M.map mkValues rowsByEntityParticipation
where where
rowsAcrossAllStructures = allStructureRows grids rowsAcrossAllStructures = allStructureRows grids
-- The input here are all rows across all structures -- The input here are all rows across all structures
-- that share the same entity sequence. -- that share the same entity sequence.
mkSmValue :: SymbolSequence -> NE.NonEmpty SingleRowEntityOccurrences -> StructureSearcher
mkSmValue ksms singleRows = mkSmValue ksms singleRows =
StructureSearcher sm2D ksms singleRows StructureSearcher sm2D ksms singleRows
where where
structureRowsNE = NE.map myRow singleRows structureRowsNE = NE.map myRow singleRows
sm2D = mkRowLookup structureRowsNE sm2D = mkRowLookup nameFunc structureRowsNE
mkValues :: NE.NonEmpty SingleRowEntityOccurrences -> AutomatonInfo AtomicKeySymbol StructureSearcher
mkValues neList = AutomatonInfo participatingEnts bounds sm mkValues neList = AutomatonInfo participatingEnts bounds sm
where where
participatingEnts = participatingEnts =
(S.fromList . map (view entityName)) (S.fromList . map nameFunc)
(concatMap (catMaybes . fst) tuples) (concatMap (catMaybes . fst) tuples)
tuples = M.toList $ M.mapWithKey mkSmValue groupedByUniqueRow tuples = M.toList $ M.mapWithKey mkSmValue groupedByUniqueRow
@ -144,19 +147,18 @@ mkEntityLookup grids =
-- The values of this map are guaranteed to contain only one -- The values of this map are guaranteed to contain only one
-- entry per row of a given structure. -- entry per row of a given structure.
rowsByEntityParticipation :: M.Map Entity (NE.NonEmpty SingleRowEntityOccurrences)
rowsByEntityParticipation = rowsByEntityParticipation =
binTuples $ binTuples $
map (myEntity &&& id) $ map (myEntity &&& id) $
concatMap explodeRowEntities rowsAcrossAllStructures concatMap explodeRowEntities rowsAcrossAllStructures
deriveEntityOffsets :: PositionWithinRow -> InspectionOffsets deriveEntityOffsets :: PositionWithinRow b a -> InspectionOffsets
deriveEntityOffsets (PositionWithinRow pos r) = deriveEntityOffsets (PositionWithinRow pos r) =
mkOffsets pos $ rowContent r mkOffsets pos $ rowContent r
-- The members of "rowMembers" are of 'Maybe' type; the 'Nothing's -- The members of "rowMembers" are of 'Maybe' type; the 'Nothing's
-- are dropped but accounted for when indexing the columns. -- are dropped but accounted for when indexing the columns.
explodeRowEntities :: StructureRow -> [SingleRowEntityOccurrences] explodeRowEntities :: Ord a => StructureRow b a -> [SingleRowEntityOccurrences b a]
explodeRowEntities r@(StructureRow _ _ rowMembers) = explodeRowEntities r@(StructureRow _ _ rowMembers) =
map f $ M.toList $ binTuples unconsolidated map f $ M.toList $ binTuples unconsolidated
where where
@ -171,18 +173,23 @@ mkEntityLookup grids =
-- | Create Aho-Corasick matchers that will recognize all of the -- | Create Aho-Corasick matchers that will recognize all of the
-- provided structure definitions -- provided structure definitions
mkAutomatons :: [SymmetryAnnotatedGrid (Maybe Cell)] -> RecognizerAutomatons mkAutomatons ::
[SymmetryAnnotatedGrid (Maybe (PCell Entity))] ->
RecognizerAutomatons (PCell Entity) EntityName Entity
mkAutomatons xs = mkAutomatons xs =
RecognizerAutomatons RecognizerAutomatons
infos infos
(mkEntityLookup rotatedGrids) (mkEntityLookup (view entityName) rotatedGrids)
where where
rotatedGrids = concatMap (extractGrids . namedGrid) xs rotatedGrids = concatMap (extractGrids . namedGrid) xs
process g = StructureInfo g (getEntityGrid $ structure $ namedGrid g) . histogram . concatMap catMaybes . getEntityGrid . structure $ namedGrid g process g = StructureInfo g (getEntityGrid $ structure $ namedGrid g) . histogram . concatMap catMaybes . getEntityGrid . structure $ namedGrid g
infos = M.fromList $ map (name . namedGrid &&& process) xs infos = M.fromList $ map (name . namedGrid &&& process) xs
extractOrientedGrid :: NamedGrid (Maybe Cell) -> AbsoluteDir -> StructureWithGrid extractOrientedGrid ::
NamedGrid (Maybe (PCell Entity)) ->
AbsoluteDir ->
StructureWithGrid (PCell Entity) Entity
extractOrientedGrid x d = StructureWithGrid x d $ getEntityGrid g' extractOrientedGrid x d = StructureWithGrid x d $ getEntityGrid g'
where where
Grid rows = structure x Grid rows = structure x
@ -191,13 +198,13 @@ extractOrientedGrid x d = StructureWithGrid x d $ getEntityGrid g'
-- | At this point, we have already ensured that orientations -- | At this point, we have already ensured that orientations
-- redundant by rotational symmetry have been excluded -- redundant by rotational symmetry have been excluded
-- (i.e. at Scenario validation time). -- (i.e. at Scenario validation time).
extractGrids :: NamedGrid (Maybe Cell) -> [StructureWithGrid] extractGrids :: NamedGrid (Maybe (PCell Entity)) -> [StructureWithGrid (PCell Entity) Entity]
extractGrids x = map (extractOrientedGrid x) $ Set.toList $ recognize x extractGrids x = map (extractOrientedGrid x) $ Set.toList $ recognize x
-- | The output list of 'FoundStructure' records is not yet -- | The output list of 'FoundStructure' records is not yet
-- vetted; the 'ensureStructureIntact' function will subsequently -- vetted; the 'ensureStructureIntact' function will subsequently
-- filter this list. -- filter this list.
lookupStaticPlacements :: StaticStructureInfo -> [FoundStructure] lookupStaticPlacements :: StaticStructureInfo -> [FoundStructure (PCell Entity) Entity]
lookupStaticPlacements (StaticStructureInfo structDefs thePlacements) = lookupStaticPlacements (StaticStructureInfo structDefs thePlacements) =
concatMap f $ M.toList thePlacements concatMap f $ M.toList thePlacements
where where

View File

@ -27,13 +27,15 @@ import Swarm.Game.Scenario.Topography.Navigation.Waypoint (
) )
import Swarm.Game.Scenario.Topography.Structure ( import Swarm.Game.Scenario.Topography.Structure (
InheritedStructureDefs, InheritedStructureDefs,
LocatedStructure,
MergedStructure (MergedStructure),
PStructure (Structure),
) )
import Swarm.Game.Scenario.Topography.Structure qualified as Structure import Swarm.Game.Scenario.Topography.Structure qualified as Structure
import Swarm.Game.Scenario.Topography.Structure.Assembly qualified as Assembly import Swarm.Game.Scenario.Topography.Structure.Assembly qualified as Assembly
import Swarm.Game.Scenario.Topography.Structure.Overlay import Swarm.Game.Scenario.Topography.Structure.Overlay
import Swarm.Game.Scenario.Topography.Structure.Type (
LocatedStructure,
MergedStructure (MergedStructure),
PStructure (Structure),
)
import Swarm.Game.Scenario.Topography.WorldPalette import Swarm.Game.Scenario.Topography.WorldPalette
import Swarm.Game.Universe import Swarm.Game.Universe
import Swarm.Game.World.Parse () import Swarm.Game.World.Parse ()

View File

@ -50,7 +50,7 @@ import Data.Map qualified as M
import Data.Yaml (FromJSON (parseJSON), ToJSON (toJSON)) import Data.Yaml (FromJSON (parseJSON), ToJSON (toJSON))
import Linear (Additive (..), V2 (..), negated, norm, perp, unangle) import Linear (Additive (..), V2 (..), negated, norm, perp, unangle)
import Linear.Affine (Affine (..), Point (..), origin) import Linear.Affine (Affine (..), Point (..), origin)
import Swarm.Language.Syntax (AbsoluteDir (..), Direction (..), PlanarRelativeDir (..), RelativeDir (..), isCardinal) import Swarm.Language.Syntax.Direction (AbsoluteDir (..), Direction (..), PlanarRelativeDir (..), RelativeDir (..), isCardinal)
import Swarm.Util qualified as Util import Swarm.Util qualified as Util
-- $setup -- $setup

View File

@ -25,8 +25,8 @@ import Swarm.Game.Location
import Swarm.Game.Scenario.Topography.Area import Swarm.Game.Scenario.Topography.Area
import Swarm.Game.Scenario.Topography.Navigation.Waypoint import Swarm.Game.Scenario.Topography.Navigation.Waypoint
import Swarm.Game.Scenario.Topography.Placement import Swarm.Game.Scenario.Topography.Placement
import Swarm.Game.Scenario.Topography.Structure
import Swarm.Game.Scenario.Topography.Structure.Overlay import Swarm.Game.Scenario.Topography.Structure.Overlay
import Swarm.Game.Scenario.Topography.Structure.Type
import Swarm.Language.Syntax.Direction (directionJsonModifier) import Swarm.Language.Syntax.Direction (directionJsonModifier)
import Swarm.Util (commaList, quote, showT) import Swarm.Util (commaList, quote, showT)

View File

@ -12,11 +12,14 @@ import Swarm.Game.Scenario.Topography.Structure.Recognition.Log
import Swarm.Game.Scenario.Topography.Structure.Recognition.Registry import Swarm.Game.Scenario.Topography.Structure.Recognition.Registry
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type import Swarm.Game.Scenario.Topography.Structure.Recognition.Type
data StructureRecognizer = StructureRecognizer -- |
{ _automatons :: RecognizerAutomatons -- The three type parameters, `b`, `en`, and `a`, correspond
, _foundStructures :: FoundRegistry -- to 'Cell', 'EntityName', and 'Entity', respectively.
data StructureRecognizer b en a = StructureRecognizer
{ _automatons :: RecognizerAutomatons b en a
, _foundStructures :: FoundRegistry b a
-- ^ Records the top-left corner of the found structure -- ^ Records the top-left corner of the found structure
, _recognitionLog :: [SearchLog] , _recognitionLog :: [SearchLog en]
} }
deriving (Generic) deriving (Generic)

View File

@ -9,14 +9,13 @@ import Data.Int (Int32)
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Servant.Docs (ToSample) import Servant.Docs (ToSample)
import Servant.Docs qualified as SD import Servant.Docs qualified as SD
import Swarm.Game.Entity (EntityName)
import Swarm.Game.Location (Location) import Swarm.Game.Location (Location)
import Swarm.Game.Scenario.Topography.Placement (StructureName) import Swarm.Game.Scenario.Topography.Placement (StructureName)
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type import Swarm.Game.Scenario.Topography.Structure.Recognition.Type
import Swarm.Game.Universe (Cosmic) import Swarm.Game.Universe (Cosmic)
type StructureRowContent = [Maybe EntityName] type StructureRowContent en = [Maybe en]
type WorldRowContent = [Maybe EntityName] type WorldRowContent en = [Maybe en]
data MatchingRowFrom = MatchingRowFrom data MatchingRowFrom = MatchingRowFrom
{ rowIdx :: Int32 { rowIdx :: Int32
@ -27,21 +26,21 @@ data MatchingRowFrom = MatchingRowFrom
newtype HaystackPosition = HaystackPosition Int newtype HaystackPosition = HaystackPosition Int
deriving (Generic, ToJSON) deriving (Generic, ToJSON)
data HaystackContext = HaystackContext data HaystackContext en = HaystackContext
{ worldRow :: WorldRowContent { worldRow :: WorldRowContent en
, haystackPosition :: HaystackPosition , haystackPosition :: HaystackPosition
} }
deriving (Generic, ToJSON) deriving (Generic, ToJSON)
data FoundRowCandidate = FoundRowCandidate data FoundRowCandidate en = FoundRowCandidate
{ haystackContext :: HaystackContext { haystackContext :: HaystackContext en
, structureContent :: StructureRowContent , structureContent :: StructureRowContent en
, rowCandidates :: [MatchingRowFrom] , rowCandidates :: [MatchingRowFrom]
} }
deriving (Generic, ToJSON) deriving (Generic, ToJSON)
data ParticipatingEntity = ParticipatingEntity data ParticipatingEntity en = ParticipatingEntity
{ entity :: EntityName { entity :: en
, searchOffsets :: InspectionOffsets , searchOffsets :: InspectionOffsets
} }
deriving (Generic, ToJSON) deriving (Generic, ToJSON)
@ -53,15 +52,15 @@ data IntactPlacementLog = IntactPlacementLog
} }
deriving (Generic, ToJSON) deriving (Generic, ToJSON)
data SearchLog data SearchLog en
= FoundParticipatingEntity ParticipatingEntity = FoundParticipatingEntity (ParticipatingEntity en)
| StructureRemoved StructureName | StructureRemoved StructureName
| FoundRowCandidates [FoundRowCandidate] | FoundRowCandidates [FoundRowCandidate en]
| FoundCompleteStructureCandidates [StructureName] | FoundCompleteStructureCandidates [StructureName]
| IntactStaticPlacement [IntactPlacementLog] | IntactStaticPlacement [IntactPlacementLog]
deriving (Generic) deriving (Generic)
instance ToJSON SearchLog where instance (ToJSON en) => ToJSON (SearchLog en) where
toJSON = genericToJSON searchLogOptions toJSON = genericToJSON searchLogOptions
searchLogOptions :: Options searchLogOptions :: Options
@ -70,7 +69,7 @@ searchLogOptions =
{ sumEncoding = ObjectWithSingleField { sumEncoding = ObjectWithSingleField
} }
instance ToSample SearchLog where instance ToSample (SearchLog en) where
toSamples _ = SD.noSamples toSamples _ = SD.noSamples
data StructureLocation = StructureLocation StructureName (Cosmic Location) data StructureLocation = StructureLocation StructureName (Cosmic Location)

View File

@ -29,24 +29,27 @@ import Data.Map.NonEmpty (NEMap)
import Data.Map.NonEmpty qualified as NEM import Data.Map.NonEmpty qualified as NEM
import Swarm.Game.Location (Location) import Swarm.Game.Location (Location)
import Swarm.Game.Scenario.Topography.Placement (StructureName) import Swarm.Game.Scenario.Topography.Placement (StructureName)
import Swarm.Game.Scenario.Topography.Structure qualified as Structure
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type import Swarm.Game.Scenario.Topography.Structure.Recognition.Type
import Swarm.Game.Scenario.Topography.Structure.Type qualified as Structure
import Swarm.Game.Universe (Cosmic) import Swarm.Game.Universe (Cosmic)
import Swarm.Util (binTuples, deleteKeys) import Swarm.Util (binTuples, deleteKeys)
-- | The authoritative source of which built structures currently exist. -- | The authoritative source of which built structures currently exist.
data FoundRegistry = FoundRegistry --
{ _foundByName :: Map StructureName (NEMap (Cosmic Location) StructureWithGrid) -- The two type parameters, `b` and `a`, correspond
, _foundByLocation :: Map (Cosmic Location) FoundStructure -- to 'Cell' and 'Entity', respectively.
data FoundRegistry b a = FoundRegistry
{ _foundByName :: Map StructureName (NEMap (Cosmic Location) (StructureWithGrid b a))
, _foundByLocation :: Map (Cosmic Location) (FoundStructure b a)
} }
emptyFoundStructures :: FoundRegistry emptyFoundStructures :: FoundRegistry b a
emptyFoundStructures = FoundRegistry mempty mempty emptyFoundStructures = FoundRegistry mempty mempty
-- | We use a 'NEMap' here so that we can use the -- | We use a 'NEMap' here so that we can use the
-- safe-indexing function 'indexWrapNonEmpty' in the implementation -- safe-indexing function 'indexWrapNonEmpty' in the implementation
-- of the @structure@ command. -- of the @structure@ command.
foundByName :: FoundRegistry -> Map StructureName (NEMap (Cosmic Location) StructureWithGrid) foundByName :: FoundRegistry b a -> Map StructureName (NEMap (Cosmic Location) (StructureWithGrid b a))
foundByName = _foundByName foundByName = _foundByName
-- | This is a worldwide "mask" that prevents members of placed -- | This is a worldwide "mask" that prevents members of placed
@ -54,10 +57,10 @@ foundByName = _foundByName
-- deletion of structures when their elements are removed from the world. -- deletion of structures when their elements are removed from the world.
-- --
-- Each recognized structure instance will have @MxN@ entries in this map. -- Each recognized structure instance will have @MxN@ entries in this map.
foundByLocation :: FoundRegistry -> Map (Cosmic Location) FoundStructure foundByLocation :: FoundRegistry b a -> Map (Cosmic Location) (FoundStructure b a)
foundByLocation = _foundByLocation foundByLocation = _foundByLocation
removeStructure :: FoundStructure -> FoundRegistry -> FoundRegistry removeStructure :: FoundStructure b a -> FoundRegistry b a -> FoundRegistry b a
removeStructure fs (FoundRegistry byName byLoc) = removeStructure fs (FoundRegistry byName byLoc) =
FoundRegistry FoundRegistry
(M.update tidyDelete structureName byName) (M.update tidyDelete structureName byName)
@ -71,7 +74,7 @@ removeStructure fs (FoundRegistry byName byLoc) =
-- Swarm.Game.State.removeRobotFromLocationMap -- Swarm.Game.State.removeRobotFromLocationMap
tidyDelete = NEM.nonEmptyMap . NEM.delete upperLeft tidyDelete = NEM.nonEmptyMap . NEM.delete upperLeft
addFound :: FoundStructure -> FoundRegistry -> FoundRegistry addFound :: FoundStructure b a -> FoundRegistry b a -> FoundRegistry b a
addFound fs@(FoundStructure swg loc) (FoundRegistry byName byLoc) = addFound fs@(FoundStructure swg loc) (FoundRegistry byName byLoc) =
FoundRegistry FoundRegistry
(M.insertWith (<>) k (NEM.singleton loc swg) byName) (M.insertWith (<>) k (NEM.singleton loc swg) byName)
@ -84,7 +87,7 @@ addFound fs@(FoundStructure swg loc) (FoundRegistry byName byLoc) =
-- --
-- Each of these shall have been re-checked in case -- Each of these shall have been re-checked in case
-- a subsequent placement occludes them. -- a subsequent placement occludes them.
populateStaticFoundStructures :: [FoundStructure] -> FoundRegistry populateStaticFoundStructures :: [FoundStructure b a] -> FoundRegistry b a
populateStaticFoundStructures allFound = populateStaticFoundStructures allFound =
FoundRegistry byName byLocation FoundRegistry byName byLocation
where where

View File

@ -12,8 +12,8 @@ import Data.Set qualified as Set
import Data.Text qualified as T import Data.Text qualified as T
import Swarm.Game.Scenario.Topography.Area (Grid (Grid)) import Swarm.Game.Scenario.Topography.Area (Grid (Grid))
import Swarm.Game.Scenario.Topography.Placement (Orientation (..), applyOrientationTransform) import Swarm.Game.Scenario.Topography.Placement (Orientation (..), applyOrientationTransform)
import Swarm.Game.Scenario.Topography.Structure qualified as Structure
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type (RotationalSymmetry (..), SymmetryAnnotatedGrid (..)) import Swarm.Game.Scenario.Topography.Structure.Recognition.Type (RotationalSymmetry (..), SymmetryAnnotatedGrid (..))
import Swarm.Game.Scenario.Topography.Structure.Type qualified as Structure
import Swarm.Language.Syntax.Direction (AbsoluteDir (DSouth, DWest), getCoordinateOrientation) import Swarm.Language.Syntax.Direction (AbsoluteDir (DSouth, DWest), getCoordinateOrientation)
import Swarm.Util (commaList, failT, histogram, showT) import Swarm.Util (commaList, failT, histogram, showT)

View File

@ -31,14 +31,12 @@ import Data.Semigroup (Max, Min)
import Data.Set (Set) import Data.Set (Set)
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Linear (V2 (..)) import Linear (V2 (..))
import Swarm.Game.Entity (Entity, EntityName)
import Swarm.Game.Location (Location) import Swarm.Game.Location (Location)
import Swarm.Game.Scenario.Topography.Area import Swarm.Game.Scenario.Topography.Area
import Swarm.Game.Scenario.Topography.Cell
import Swarm.Game.Scenario.Topography.Placement (StructureName) import Swarm.Game.Scenario.Topography.Placement (StructureName)
import Swarm.Game.Scenario.Topography.Structure (NamedGrid) import Swarm.Game.Scenario.Topography.Structure.Type (NamedGrid)
import Swarm.Game.Universe (Cosmic, offsetBy) import Swarm.Game.Universe (Cosmic, offsetBy)
import Swarm.Language.Syntax (AbsoluteDir) import Swarm.Language.Syntax.Direction (AbsoluteDir)
import Text.AhoCorasick (StateMachine) import Text.AhoCorasick (StateMachine)
-- | A "needle" consisting of a single cell within -- | A "needle" consisting of a single cell within
@ -50,7 +48,7 @@ import Text.AhoCorasick (StateMachine)
-- @ -- @
-- aab -- aab
-- @ -- @
type AtomicKeySymbol = Maybe Entity type AtomicKeySymbol a = Maybe a
-- | A "needle" consisting row of cells within the haystack -- | A "needle" consisting row of cells within the haystack
-- (a sequence of rows) to be searched. -- (a sequence of rows) to be searched.
@ -61,15 +59,15 @@ type AtomicKeySymbol = Maybe Entity
-- @ -- @
-- aab -- aab
-- @ -- @
type SymbolSequence = [AtomicKeySymbol] type SymbolSequence a = [AtomicKeySymbol a]
-- | This is returned as a value of the 1-D searcher. -- | This is returned as a value of the 1-D searcher.
-- It contains search automatons customized to the 2-D structures -- It contains search automatons customized to the 2-D structures
-- that may possibly contain the row found by the 1-D searcher. -- that may possibly contain the row found by the 1-D searcher.
data StructureSearcher = StructureSearcher data StructureSearcher b en a = StructureSearcher
{ automaton2D :: AutomatonInfo SymbolSequence StructureWithGrid { automaton2D :: AutomatonInfo en (SymbolSequence a) (StructureWithGrid b a)
, needleContent :: SymbolSequence , needleContent :: SymbolSequence a
, singleRowItems :: NE.NonEmpty SingleRowEntityOccurrences , singleRowItems :: NE.NonEmpty (SingleRowEntityOccurrences b a)
} }
-- | -- |
@ -83,10 +81,10 @@ data StructureSearcher = StructureSearcher
-- @ -- @
-- --
-- Its '_position' is @2@. -- Its '_position' is @2@.
data PositionWithinRow = PositionWithinRow data PositionWithinRow b a = PositionWithinRow
{ _position :: Int32 { _position :: Int32
-- ^ horizontal index of the entity within the row -- ^ horizontal index of the entity within the row
, structureRow :: StructureRow , structureRow :: StructureRow b a
} }
-- Represents all of the locations that particular entity -- Represents all of the locations that particular entity
@ -100,10 +98,10 @@ data PositionWithinRow = PositionWithinRow
-- @ -- @
-- --
-- this record will contain two entries in its 'entityOccurrences' field. -- this record will contain two entries in its 'entityOccurrences' field.
data SingleRowEntityOccurrences = SingleRowEntityOccurrences data SingleRowEntityOccurrences b a = SingleRowEntityOccurrences
{ myRow :: StructureRow { myRow :: StructureRow b a
, myEntity :: Entity , myEntity :: a
, entityOccurrences :: NE.NonEmpty PositionWithinRow , entityOccurrences :: NE.NonEmpty (PositionWithinRow b a)
, expandedOffsets :: InspectionOffsets , expandedOffsets :: InspectionOffsets
} }
@ -119,19 +117,25 @@ data SingleRowEntityOccurrences = SingleRowEntityOccurrences
-- @ -- @
-- --
-- it's 'rowIndex' is @2@. -- it's 'rowIndex' is @2@.
data StructureRow = StructureRow --
{ wholeStructure :: StructureWithGrid -- The two type parameters, `b` and `a`, correspond
-- to 'Cell' and 'Entity', respectively.
data StructureRow b a = StructureRow
{ wholeStructure :: StructureWithGrid b a
, rowIndex :: Int32 , rowIndex :: Int32
-- ^ vertical index of the row within the structure -- ^ vertical index of the row within the structure
, rowContent :: SymbolSequence , rowContent :: SymbolSequence a
} }
-- | The original definition of a structure, bundled -- | The original definition of a structure, bundled
-- with its grid of cells having been extracted for convenience. -- with its grid of cells having been extracted for convenience.
data StructureWithGrid = StructureWithGrid --
{ originalDefinition :: NamedGrid (Maybe Cell) -- The two type parameters, `b` and `a`, correspond
-- to 'Cell' and 'Entity', respectively.
data StructureWithGrid b a = StructureWithGrid
{ originalDefinition :: NamedGrid (Maybe b)
, rotatedTo :: AbsoluteDir , rotatedTo :: AbsoluteDir
, entityGrid :: [SymbolSequence] , entityGrid :: [SymbolSequence a]
} }
deriving (Eq) deriving (Eq)
@ -151,10 +155,10 @@ data SymmetryAnnotatedGrid a = SymmetryAnnotatedGrid
deriving (Show) deriving (Show)
-- | Structure definitions with precomputed metadata for consumption by the UI -- | Structure definitions with precomputed metadata for consumption by the UI
data StructureInfo = StructureInfo data StructureInfo b a = StructureInfo
{ annotatedGrid :: SymmetryAnnotatedGrid (Maybe Cell) { annotatedGrid :: SymmetryAnnotatedGrid (Maybe b)
, entityProcessedGrid :: [SymbolSequence] , entityProcessedGrid :: [SymbolSequence a]
, entityCounts :: Map Entity Int , entityCounts :: Map a Int
} }
-- | For all of the rows that contain a given entity -- | For all of the rows that contain a given entity
@ -187,8 +191,8 @@ instance Semigroup InspectionOffsets where
-- | Each automaton shall be initialized to recognize -- | Each automaton shall be initialized to recognize
-- a certain subset of structure rows, that may either -- a certain subset of structure rows, that may either
-- all be within one structure, or span multiple structures. -- all be within one structure, or span multiple structures.
data AutomatonInfo k v = AutomatonInfo data AutomatonInfo en k v = AutomatonInfo
{ _participatingEntities :: Set EntityName { _participatingEntities :: Set en
, _inspectionOffsets :: InspectionOffsets , _inspectionOffsets :: InspectionOffsets
, _automaton :: StateMachine k v , _automaton :: StateMachine k v
} }
@ -198,11 +202,11 @@ makeLenses ''AutomatonInfo
-- | The complete set of data needed to identify applicable -- | The complete set of data needed to identify applicable
-- structures, based on a just-placed entity. -- structures, based on a just-placed entity.
data RecognizerAutomatons = RecognizerAutomatons data RecognizerAutomatons b en a = RecognizerAutomatons
{ _originalStructureDefinitions :: Map StructureName StructureInfo { _originalStructureDefinitions :: Map StructureName (StructureInfo b a)
-- ^ all of the structures that shall participate in automatic recognition. -- ^ all of the structures that shall participate in automatic recognition.
-- This list is used only by the UI and by the 'Floorplan' command. -- This list is used only by the UI and by the 'Floorplan' command.
, _automatonsByEntity :: Map Entity (AutomatonInfo AtomicKeySymbol StructureSearcher) , _automatonsByEntity :: Map a (AutomatonInfo en (AtomicKeySymbol a) (StructureSearcher b en a))
} }
deriving (Generic) deriving (Generic)
@ -210,8 +214,11 @@ makeLenses ''RecognizerAutomatons
-- | Final output of the search process. -- | Final output of the search process.
-- These are the elements that are stored in the 'FoundRegistry'. -- These are the elements that are stored in the 'FoundRegistry'.
data FoundStructure = FoundStructure --
{ structureWithGrid :: StructureWithGrid -- The two type parameters, `b` and `a`, correspond
-- to 'Cell' and 'Entity', respectively.
data FoundStructure b a = FoundStructure
{ structureWithGrid :: StructureWithGrid b a
, upperLeftCorner :: Cosmic Location , upperLeftCorner :: Cosmic Location
} }
deriving (Eq) deriving (Eq)
@ -226,7 +233,7 @@ data FoundStructure = FoundStructure
-- Since the natural order of coordinates increases as described, -- Since the natural order of coordinates increases as described,
-- we need to invert it with 'Down' so that this ordering is by -- we need to invert it with 'Down' so that this ordering is by
-- increasing preference. -- increasing preference.
instance Ord FoundStructure where instance (Eq b, Eq a) => Ord (FoundStructure b a) where
compare = compare `on` (f1 &&& f2) compare = compare `on` (f1 &&& f2)
where where
f1 = computeArea . getAreaDimensions . entityGrid . structureWithGrid f1 = computeArea . getAreaDimensions . entityGrid . structureWithGrid
@ -235,7 +242,7 @@ instance Ord FoundStructure where
-- | Yields coordinates that are occupied by an entity of a placed structure. -- | Yields coordinates that are occupied by an entity of a placed structure.
-- Cells within the rectangular bounds of the structure that are unoccupied -- Cells within the rectangular bounds of the structure that are unoccupied
-- are not included. -- are not included.
genOccupiedCoords :: FoundStructure -> [Cosmic Location] genOccupiedCoords :: FoundStructure b a -> [Cosmic Location]
genOccupiedCoords (FoundStructure swg loc) = genOccupiedCoords (FoundStructure swg loc) =
concatMap catMaybes . zipWith mkRow [0 ..] $ entityGrid swg concatMap catMaybes . zipWith mkRow [0 ..] $ entityGrid swg
where where

View File

@ -0,0 +1,63 @@
-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Definitions of "structures" for use within a map
-- as well as logic for combining them.
module Swarm.Game.Scenario.Topography.Structure.Type where
import Data.Set (Set)
import Data.Text (Text)
import Swarm.Game.Location
import Swarm.Game.Scenario.Topography.Area
import Swarm.Game.Scenario.Topography.Navigation.Waypoint
import Swarm.Game.Scenario.Topography.Placement
import Swarm.Game.Scenario.Topography.Structure.Overlay
import Swarm.Language.Syntax.Direction (AbsoluteDir)
data NamedArea a = NamedArea
{ name :: StructureName
, recognize :: Set AbsoluteDir
-- ^ whether this structure should be registered for automatic recognition
-- and which orientations shall be recognized.
-- The supplied direction indicates which cardinal direction the
-- original map's "North" has been re-oriented to.
-- E.g., 'DWest' represents a rotation of 90 degrees counter-clockwise.
, description :: Maybe Text
-- ^ will be UI-facing only if this is a recognizable structure
, structure :: a
}
deriving (Eq, Show, Functor)
isRecognizable :: NamedArea a -> Bool
isRecognizable = not . null . recognize
type NamedGrid c = NamedArea (Grid c)
type NamedStructure c = NamedArea (PStructure c)
data PStructure c = Structure
{ area :: PositionedGrid c
, structures :: [NamedStructure c]
-- ^ structure definitions from parents shall be accessible by children
, placements :: [Placement]
-- ^ earlier placements will be overlaid on top of later placements in the YAML file
, waypoints :: [Waypoint]
}
deriving (Eq, Show)
data Placed c = Placed Placement (NamedStructure c)
deriving (Show)
-- | For use in registering recognizable pre-placed structures
data LocatedStructure = LocatedStructure
{ placedName :: StructureName
, upDirection :: AbsoluteDir
, cornerLoc :: Location
}
deriving (Show)
instance HasLocation LocatedStructure where
modifyLoc f (LocatedStructure x y originalLoc) =
LocatedStructure x y $ f originalLoc
data MergedStructure c = MergedStructure (PositionedGrid c) [LocatedStructure] [Originated Waypoint]

View File

@ -10,12 +10,14 @@ module Swarm.TUI.Model.Structure where
import Brick.Focus import Brick.Focus
import Brick.Widgets.List qualified as BL import Brick.Widgets.List qualified as BL
import Control.Lens (makeLenses) import Control.Lens (makeLenses)
import Swarm.Game.Entity (Entity)
import Swarm.Game.Scenario (Cell)
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type import Swarm.Game.Scenario.Topography.Structure.Recognition.Type
import Swarm.TUI.Model.Name import Swarm.TUI.Model.Name
import Swarm.Util (listEnums) import Swarm.Util (listEnums)
data StructureDisplay = StructureDisplay data StructureDisplay = StructureDisplay
{ _structurePanelListWidget :: BL.List Name StructureInfo { _structurePanelListWidget :: BL.List Name (StructureInfo Cell Entity)
-- ^ required for maintaining the selection/navigation -- ^ required for maintaining the selection/navigation
-- state among list items -- state among list items
, _structurePanelFocus :: FocusRing Name , _structurePanelFocus :: FocusRing Name

View File

@ -19,14 +19,15 @@ import Data.Map.Strict qualified as M
import Data.Set qualified as Set import Data.Set qualified as Set
import Data.Text qualified as T import Data.Text qualified as T
import Data.Vector qualified as V import Data.Vector qualified as V
import Swarm.Game.Entity (entityDisplay) import Swarm.Game.Entity (Entity, entityDisplay)
import Swarm.Game.Scenario (Cell)
import Swarm.Game.Scenario.Topography.Area import Swarm.Game.Scenario.Topography.Area
import Swarm.Game.Scenario.Topography.Placement import Swarm.Game.Scenario.Topography.Placement
import Swarm.Game.Scenario.Topography.Structure qualified as Structure
import Swarm.Game.Scenario.Topography.Structure.Recognition (foundStructures) import Swarm.Game.Scenario.Topography.Structure.Recognition (foundStructures)
import Swarm.Game.Scenario.Topography.Structure.Recognition.Precompute (getEntityGrid) import Swarm.Game.Scenario.Topography.Structure.Recognition.Precompute (getEntityGrid)
import Swarm.Game.Scenario.Topography.Structure.Recognition.Registry (foundByName) import Swarm.Game.Scenario.Topography.Structure.Recognition.Registry (foundByName)
import Swarm.Game.Scenario.Topography.Structure.Recognition.Type import Swarm.Game.Scenario.Topography.Structure.Recognition.Type
import Swarm.Game.Scenario.Topography.Structure.Type qualified as Structure
import Swarm.Game.State import Swarm.Game.State
import Swarm.Game.State.Substate import Swarm.Game.State.Substate
import Swarm.Language.Syntax.Direction (directionJsonModifier) import Swarm.Language.Syntax.Direction (directionJsonModifier)
@ -39,7 +40,7 @@ import Swarm.Util (commaList)
-- | Render a two-pane widget with structure selection on the left -- | Render a two-pane widget with structure selection on the left
-- and single-structure details on the right. -- and single-structure details on the right.
structureWidget :: GameState -> StructureInfo -> Widget n structureWidget :: GameState -> StructureInfo Cell Entity -> Widget n
structureWidget gs s = structureWidget gs s =
vBox vBox
[ hBox [ hBox
@ -121,7 +122,7 @@ structureWidget gs s =
cells = getEntityGrid $ Structure.structure d cells = getEntityGrid $ Structure.structure d
renderOneCell = maybe (txt " ") (renderDisplay . view entityDisplay) renderOneCell = maybe (txt " ") (renderDisplay . view entityDisplay)
makeListWidget :: [StructureInfo] -> BL.List Name StructureInfo makeListWidget :: [StructureInfo Cell Entity] -> BL.List Name (StructureInfo Cell Entity)
makeListWidget structureDefinitions = makeListWidget structureDefinitions =
BL.listMoveTo 0 $ BL.list (StructureWidgets StructuresList) (V.fromList structureDefinitions) 1 BL.listMoveTo 0 $ BL.list (StructureWidgets StructuresList) (V.fromList structureDefinitions) 1
@ -163,7 +164,7 @@ renderStructuresDisplay gs structureDisplay =
drawSidebarListItem :: drawSidebarListItem ::
Bool -> Bool ->
StructureInfo -> StructureInfo Cell Entity ->
Widget Name Widget Name
drawSidebarListItem _isSelected (StructureInfo annotated _ _) = drawSidebarListItem _isSelected (StructureInfo annotated _ _) =
txt . getStructureName . Structure.name $ namedGrid annotated txt . getStructureName . Structure.name $ namedGrid annotated

View File

@ -64,6 +64,7 @@ import Servant.Docs (ToCapture)
import Servant.Docs qualified as SD import Servant.Docs qualified as SD
import Servant.Docs.Internal qualified as SD (renderCurlBasePath) import Servant.Docs.Internal qualified as SD (renderCurlBasePath)
import Swarm.Doc.Command import Swarm.Doc.Command
import Swarm.Game.Entity (EntityName)
import Swarm.Game.Robot import Swarm.Game.Robot
import Swarm.Game.Scenario.Objective import Swarm.Game.Scenario.Objective
import Swarm.Game.Scenario.Objective.Graph import Swarm.Game.Scenario.Objective.Graph
@ -104,7 +105,7 @@ type SwarmAPI =
:<|> "goals" :> "graph" :> Get '[JSON] (Maybe GraphInfo) :<|> "goals" :> "graph" :> Get '[JSON] (Maybe GraphInfo)
:<|> "goals" :> "uigoal" :> Get '[JSON] GoalTracking :<|> "goals" :> "uigoal" :> Get '[JSON] GoalTracking
:<|> "goals" :> Get '[JSON] WinCondition :<|> "goals" :> Get '[JSON] WinCondition
:<|> "recognize" :> "log" :> Get '[JSON] [SearchLog] :<|> "recognize" :> "log" :> Get '[JSON] [SearchLog EntityName]
:<|> "recognize" :> "found" :> Get '[JSON] [StructureLocation] :<|> "recognize" :> "found" :> Get '[JSON] [StructureLocation]
:<|> "code" :> "render" :> ReqBody '[PlainText] T.Text :> Post '[PlainText] T.Text :<|> "code" :> "render" :> ReqBody '[PlainText] T.Text :> Post '[PlainText] T.Text
:<|> "code" :> "run" :> ReqBody '[PlainText] T.Text :> Post '[PlainText] T.Text :<|> "code" :> "run" :> ReqBody '[PlainText] T.Text :> Post '[PlainText] T.Text
@ -210,7 +211,7 @@ goalsHandler appStateRef = do
appState <- liftIO (readIORef appStateRef) appState <- liftIO (readIORef appStateRef)
return $ appState ^. gameState . winCondition return $ appState ^. gameState . winCondition
recogLogHandler :: ReadableIORef AppState -> Handler [SearchLog] recogLogHandler :: ReadableIORef AppState -> Handler [SearchLog EntityName]
recogLogHandler appStateRef = do recogLogHandler appStateRef = do
appState <- liftIO (readIORef appStateRef) appState <- liftIO (readIORef appStateRef)
return $ appState ^. gameState . discovery . structureRecognition . recognitionLog return $ appState ^. gameState . discovery . structureRecognition . recognitionLog

View File

@ -152,7 +152,6 @@ library swarm-lang
Swarm.Language.Syntax.CommandMetadata Swarm.Language.Syntax.CommandMetadata
Swarm.Language.Syntax.Comments Swarm.Language.Syntax.Comments
Swarm.Language.Syntax.Constants Swarm.Language.Syntax.Constants
Swarm.Language.Syntax.Direction
Swarm.Language.Syntax.Loc Swarm.Language.Syntax.Loc
Swarm.Language.Syntax.Pattern Swarm.Language.Syntax.Pattern
Swarm.Language.Syntax.Util Swarm.Language.Syntax.Util
@ -201,6 +200,50 @@ library swarm-lang
-- See discussion in #415 -- See discussion in #415
StrictData StrictData
library swarm-topography
import: stan-config, common, ghc2021-extensions
visibility: public
-- cabal-gild: discover src/swarm-topography
exposed-modules:
Swarm.Game.Location
Swarm.Game.Scenario.Topography.Area
Swarm.Game.Scenario.Topography.Navigation.Waypoint
Swarm.Game.Scenario.Topography.Placement
Swarm.Game.Scenario.Topography.Structure.Assembly
Swarm.Game.Scenario.Topography.Structure.Overlay
Swarm.Game.Scenario.Topography.Structure.Recognition
Swarm.Game.Scenario.Topography.Structure.Recognition.Log
Swarm.Game.Scenario.Topography.Structure.Recognition.Registry
Swarm.Game.Scenario.Topography.Structure.Recognition.Symmetry
Swarm.Game.Scenario.Topography.Structure.Recognition.Type
Swarm.Game.Scenario.Topography.Structure.Type
Swarm.Game.Universe
other-modules: Paths_swarm
autogen-modules: Paths_swarm
build-depends:
AhoCorasick >=0.0.4 && <0.0.5,
aeson >=2.2 && <2.3,
base >=4.14 && <4.20,
containers >=0.6.2 && <0.8,
extra >=1.7 && <1.8,
lens,
linear >=1.21.6 && <1.24,
nonempty-containers >=0.3.4 && <0.3.5,
servant-docs >=0.12 && <0.14,
text >=1.2.4 && <2.2,
yaml >=0.11 && <0.11.12.0,
build-depends:
swarm:swarm-util
hs-source-dirs: src/swarm-topography
default-language: Haskell2010
default-extensions:
-- Avoid unexpected unevaluated thunk buildup
-- See discussion in #415
StrictData
library swarm-scenario library swarm-scenario
import: stan-config, common, ghc2021-extensions import: stan-config, common, ghc2021-extensions
visibility: public visibility: public
@ -216,7 +259,6 @@ library swarm-scenario
Swarm.Game.Failure Swarm.Game.Failure
Swarm.Game.Ingredients Swarm.Game.Ingredients
Swarm.Game.Land Swarm.Game.Land
Swarm.Game.Location
Swarm.Game.Recipe Swarm.Game.Recipe
Swarm.Game.ResourceLoading Swarm.Game.ResourceLoading
Swarm.Game.Robot Swarm.Game.Robot
@ -228,28 +270,17 @@ library swarm-scenario
Swarm.Game.Scenario.Objective.Validation Swarm.Game.Scenario.Objective.Validation
Swarm.Game.Scenario.RobotLookup Swarm.Game.Scenario.RobotLookup
Swarm.Game.Scenario.Style Swarm.Game.Scenario.Style
Swarm.Game.Scenario.Topography.Area
Swarm.Game.Scenario.Topography.Cell Swarm.Game.Scenario.Topography.Cell
Swarm.Game.Scenario.Topography.Center Swarm.Game.Scenario.Topography.Center
Swarm.Game.Scenario.Topography.EntityFacade Swarm.Game.Scenario.Topography.EntityFacade
Swarm.Game.Scenario.Topography.Navigation.Portal Swarm.Game.Scenario.Topography.Navigation.Portal
Swarm.Game.Scenario.Topography.Navigation.Waypoint
Swarm.Game.Scenario.Topography.Placement
Swarm.Game.Scenario.Topography.Structure Swarm.Game.Scenario.Topography.Structure
Swarm.Game.Scenario.Topography.Structure.Assembly
Swarm.Game.Scenario.Topography.Structure.Overlay
Swarm.Game.Scenario.Topography.Structure.Recognition
Swarm.Game.Scenario.Topography.Structure.Recognition.Log
Swarm.Game.Scenario.Topography.Structure.Recognition.Precompute Swarm.Game.Scenario.Topography.Structure.Recognition.Precompute
Swarm.Game.Scenario.Topography.Structure.Recognition.Registry
Swarm.Game.Scenario.Topography.Structure.Recognition.Symmetry
Swarm.Game.Scenario.Topography.Structure.Recognition.Type
Swarm.Game.Scenario.Topography.WorldDescription Swarm.Game.Scenario.Topography.WorldDescription
Swarm.Game.Scenario.Topography.WorldPalette Swarm.Game.Scenario.Topography.WorldPalette
Swarm.Game.State.Config Swarm.Game.State.Config
Swarm.Game.State.Landscape Swarm.Game.State.Landscape
Swarm.Game.Terrain Swarm.Game.Terrain
Swarm.Game.Universe
Swarm.Game.World Swarm.Game.World
Swarm.Game.World.Abstract Swarm.Game.World.Abstract
Swarm.Game.World.Compile Swarm.Game.World.Compile
@ -289,7 +320,6 @@ library swarm-scenario
linear >=1.21.6 && <1.24, linear >=1.21.6 && <1.24,
megaparsec >=9.6.1 && <9.7, megaparsec >=9.6.1 && <9.7,
murmur3 >=1.0.4 && <1.1, murmur3 >=1.0.4 && <1.1,
nonempty-containers >=0.3.4 && <0.3.5,
palette >=0.3 && <0.4, palette >=0.3 && <0.4,
parser-combinators >=1.2 && <1.4, parser-combinators >=1.2 && <1.4,
prettyprinter >=1.7.0 && <1.8, prettyprinter >=1.7.0 && <1.8,
@ -305,6 +335,7 @@ library swarm-scenario
build-depends: build-depends:
swarm:swarm-lang, swarm:swarm-lang,
swarm:swarm-topography,
swarm:swarm-util, swarm:swarm-util,
hs-source-dirs: src/swarm-scenario hs-source-dirs: src/swarm-scenario
@ -405,6 +436,7 @@ library swarm-engine
build-depends: build-depends:
swarm:swarm-lang, swarm:swarm-lang,
swarm:swarm-scenario, swarm:swarm-scenario,
swarm:swarm-topography,
swarm:swarm-util, swarm:swarm-util,
hs-source-dirs: src/swarm-engine hs-source-dirs: src/swarm-engine
@ -449,6 +481,7 @@ library swarm-web
swarm:swarm-engine, swarm:swarm-engine,
swarm:swarm-lang, swarm:swarm-lang,
swarm:swarm-scenario, swarm:swarm-scenario,
swarm:swarm-topography,
swarm:swarm-tui, swarm:swarm-tui,
swarm:swarm-util, swarm:swarm-util,
@ -520,6 +553,7 @@ library swarm-util
exposed-modules: exposed-modules:
Control.Carrier.Accum.FixedStrict Control.Carrier.Accum.FixedStrict
Data.BoolExpr.Simplify Data.BoolExpr.Simplify
Swarm.Language.Syntax.Direction
Swarm.Util Swarm.Util
Swarm.Util.Effect Swarm.Util.Effect
Swarm.Util.Erasable Swarm.Util.Erasable
@ -545,6 +579,7 @@ library swarm-util
extra >=1.7 && <1.8, extra >=1.7 && <1.8,
filepath >=1.4 && <1.5, filepath >=1.4 && <1.5,
fused-effects >=1.1.1.1 && <1.2, fused-effects >=1.1.1.1 && <1.2,
hashable >=1.3.4 && <1.5,
lens >=4.19 && <5.4, lens >=4.19 && <5.4,
minimorph >=0.3 && <0.4, minimorph >=0.3 && <0.4,
mtl >=2.2.2 && <2.4, mtl >=2.2.2 && <2.4,
@ -699,6 +734,7 @@ library swarm-tui
swarm:swarm-engine, swarm:swarm-engine,
swarm:swarm-lang, swarm:swarm-lang,
swarm:swarm-scenario, swarm:swarm-scenario,
swarm:swarm-topography,
swarm:swarm-util, swarm:swarm-util,
hs-source-dirs: src/swarm-tui hs-source-dirs: src/swarm-tui
@ -745,7 +781,10 @@ executable swarm-scene
build-depends: build-depends:
base, base,
optparse-applicative >=0.16 && <0.19, optparse-applicative >=0.16 && <0.19,
build-depends:
swarm:swarm-scenario, swarm:swarm-scenario,
swarm:swarm-topography,
hs-source-dirs: app/scene hs-source-dirs: app/scene
default-language: Haskell2010 default-language: Haskell2010
@ -836,6 +875,7 @@ test-suite swarm-unit
swarm:swarm-engine, swarm:swarm-engine,
swarm:swarm-lang, swarm:swarm-lang,
swarm:swarm-scenario, swarm:swarm-scenario,
swarm:swarm-topography,
swarm:swarm-tui, swarm:swarm-tui,
swarm:swarm-util, swarm:swarm-util,
@ -907,12 +947,15 @@ benchmark benchmark
extra, extra,
lens, lens,
mtl, mtl,
tasty-bench >=0.3.1 && <0.4,
text,
build-depends:
swarm:swarm-engine, swarm:swarm-engine,
swarm:swarm-lang, swarm:swarm-lang,
swarm:swarm-scenario, swarm:swarm-scenario,
swarm:swarm-topography,
swarm:swarm-util, swarm:swarm-util,
tasty-bench >=0.3.1 && <0.4,
text,
default-language: Haskell2010 default-language: Haskell2010
ghc-options: ghc-options:

View File

@ -12,7 +12,7 @@ import Data.Text (Text)
import Graphics.Vty.Input.Events qualified as V import Graphics.Vty.Input.Events qualified as V
import Swarm.Game.Location import Swarm.Game.Location
import Swarm.Language.Key import Swarm.Language.Key
import Swarm.Language.Syntax import Swarm.Language.Syntax.Direction
import Test.QuickCheck qualified as QC import Test.QuickCheck qualified as QC
import Test.Tasty import Test.Tasty
import Test.Tasty.HUnit import Test.Tasty.HUnit