Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong device reported missing #397

Closed
byorgey opened this issue Jun 14, 2022 · 7 comments · Fixed by #959
Closed

Wrong device reported missing #397

byorgey opened this issue Jun 14, 2022 · 7 comments · Fixed by #959
Assignees
Labels
Bug The observed behaviour is incorrect or unexpected. L-Capability checking Capability checking determines which capabilities are required by a given piece of code. S-Critical This is an issue that seriously affects playability or user experience.

Comments

@byorgey
Copy link
Member

byorgey commented Jun 14, 2022

Describe the bug
In certain situations with devices that don't exist, an error is generated saying that some other device is missing (even though it is not).

To Reproduce

  • Start a classic mode game with --cheat
  • Switch into creative mode and create "branch predictor"
  • Switch back out of creative mode
  • Type build {move; if (1 > 2) {} {}}
    • As expected we get an error saying no device provides the compare capability.
  • Now type build {move; turn left; if (1 > 2) {} {}}
    • All we have done is add turn left; we would expect to get the same error. Instead we get an error like You do not have the devices required for: 'move; turn left; if (1 > 2) {noop} {noop}' please obtain: - branch predictor
    • But we do have a branch predictor, so this error is bogus. Not sure yet whether the capability checking or error reporting is to blame.

Additional context
First noticed this in #373 (comment) . Note that once #395 is merged then we will have a comparator device so the above example won't work any more.

@byorgey byorgey added Bug The observed behaviour is incorrect or unexpected. S-Critical This is an issue that seriously affects playability or user experience. L-Capability checking Capability checking determines which capabilities are required by a given piece of code. labels Jun 14, 2022
@byorgey
Copy link
Member Author

byorgey commented Aug 14, 2022

An example that still shows this problem now is build {move; teleport base (2,3)} vs build {move; turn left; teleport base (2,3)}. The first gives an appropriate error, whereas the second says we are missing treads.

@byorgey
Copy link
Member Author

byorgey commented Aug 14, 2022

I've traced this as far as seeing that the Incapable exception given to formatExn is already bogus. formatExn seems to be doing its job properly, the problem is with wherever the Incapable exception is generated (probably the checkRequirements function).

@byorgey
Copy link
Member Author

byorgey commented Nov 30, 2022

The example with teleport no longer works since we get an error about needing an ADT calculator instead. However, the problem still exists and can be illustrated by the difference between build {move; x <- whereami} (correct) vs build {move; turn left; x <- whereami} (incorrect).

@byorgey
Copy link
Member Author

byorgey commented Nov 30, 2022

Strangely both move and turn left are required in the examples here. Omitting either one leads to the correct output. Including a single one of those commands twice (e.g. move; move) also leads to correct output. I've tried mixing other commands and the only thing so far that seems to trigger the incorrect output is to have both a call to move and a call to turn left before invoking a command without a device, such as whereami.

Hmm, breakthrough? Maybe it has to do with the fact that move; turn left requires two capabilities (CMove and CTurn) which are both provided by the same device (treads). Maybe there's a bug in checkRequirements related to that scenario. That would explain why it seems like the bug is only triggered when both of those specific commands are used.

I can also trigger the bug with build {give base "tree"; place "tree"; x <- whereami}, which seems to confirm my hypothesis (the give and place capabilities are both provided by a grabber or fast grabber).

However, build {grab; harvest; x <- whereami} does not trigger the bug. In this case grab and harvest capabilities are both provided by a harvester, but that's the only device that provides both. So perhaps the bug is only triggered when there are multiple devices that provide both capabilities...?

@byorgey
Copy link
Member Author

byorgey commented Dec 3, 2022

I think the problem has to do with the fact that in this code:

swarm/src/Swarm/Game/Step.hs

Lines 1746 to 1752 in d62d8c5

(deviceSets, missingDeviceSets) =
Lens.over both (nubOrd . map S.fromList) . unzip $
map (ignoreOK . L.partition deviceOK) capDevices
formatDevices = T.intercalate " or " . map (^. entityName) . S.toList
-- capabilities not provided by any device in inventory
missingCaps = S.fromList . map fst . filter (null . snd) $ zip caps deviceSets

deviceSets is defined via nubOrd (hence it does not necessarily have the same length as the original capDevices) but yet we later do zip caps deviceSets, expecting the entries of deviceSets to still line up with caps.

I think this code

swarm/src/Swarm/Game/Step.hs

Lines 1729 to 1734 in d62d8c5

let -- List of possible devices per requirement. Devices for
-- required capabilities come first, then singleton devices
-- that are required directly. This order is important since
-- later we zip required capabilities with this list to figure
-- out which capabilities are missing.
capDevices = map (`deviceForCap` em) caps ++ map (: []) devs

is a bad idea: it creates an easy-to-get-wrong invariant that anything coming from that list must be kept in the same order and no duplicates dropped, since later it will get zipped with something. We should instead do the zip right here and carry along the capabilities through the rest of the process, safely paired up with their corresponding devices.

@xsebek
Copy link
Member

xsebek commented Jan 4, 2023

@byorgey the lines have shifted a bit it seems - if you use a link with specific commit GitHub should even show a preview. 😉

swarm/src/Swarm/Game/Step.hs

Lines 1600 to 1606 in 1930fe6

(deviceSets, missingDeviceSets) =
Lens.over both (nubOrd . map S.fromList) . unzip $
map (ignoreOK . L.partition deviceOK) capDevices
formatDevices = T.intercalate " or " . map (^. entityName) . S.toList
-- capabilities not provided by any device in inventory
missingCaps = S.fromList . map fst . filter (null . snd) $ zip caps deviceSets

@byorgey
Copy link
Member Author

byorgey commented Jan 4, 2023

@xsebek Thanks! I knew about that but forgot when I made those links. Pro tip (tm) for anyone reading this later --- when viewing some code you can hit the y key to turn the URL into a commit-specific URL.

@mergify mergify bot closed this as completed in #959 Jan 5, 2023
mergify bot pushed a commit that referenced this issue Jan 5, 2023
Fixes #397.  The only way I could understand this in order to fix it was to totally refactor the code and add lots of comments as I went.  I feel like this is some of the most difficult code to wrap one's head around in the codebase.  Hopefully now it's a bit easier to understand (though still not easy, I imagine).
mergify bot pushed a commit that referenced this issue Jan 5, 2023
Add a new `GPS receiver` device which enables the `senseloc` capability (and hence the `whereami` command).  Towards #26 .

- My immediate motivation is that I want to be able to define `excursion : cmd unit -> cmd unit` which executes the given command and then returns to the same location and orientation as before.  Being able to use the `whereami` command is one of the last missing pieces of machinery necessary to be able to do this in classic mode (the other is a `heading` command, see #955).
- The proposed recipe for `GPS receiver` is `antenna + circuit + clock + compass`, which is a somewhat difficult recipe (`antenna` requires `silver` which requires a `deep mine`; a `clock` requires `quartz` + a bunch of `iron gears`, etc.).  One might wonder whether we should make the recipe easier since finding out where you are seems like a kind of fundamental operation.  However, consider that in order to even be able to make use of the result of `whereami` you need at least (1) an `ADT calculator` to deal with the pair (which transitively requires `typewriter` -> `circuit` -> `silicon` -> `quartz`) (2) probably things like `comparator` and `calculator` to do anything useful with the coordinates.  By the time you have those things you can definitely already build `circuit` + `clock` + `compass`, and you're probably not that far away from getting some `silver` for an `antenna`.  Also, in practice it's an interesting/fun constraint to build up machinery that has to work entirely based on *relative* position without being able to find out your absolute coordinates.
- For some reason this is causing `Testing/508-capability-subset` to fail.  I think perhaps it is due to #397 ?  I will investigate. *EDIT*: unfortunately, that wasn't it!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug The observed behaviour is incorrect or unexpected. L-Capability checking Capability checking determines which capabilities are required by a given piece of code. S-Critical This is an issue that seriously affects playability or user experience.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants