Urbit / Docs

Bomb Defusing

In this exercise, we will build a generator that takes a list of wires to be cut in order to defuse a bomb. The bomb is defused according to the following rules.

There are 6 possible colors of wire: white, black, purple, red, green, and orange.

If you cut a white cable, you cannot cut white or black cable.

If you cut a red cable, you must cut a green cable.

If you cut a black cable, you cannot cut a white, green, or orange cable.

If you cut an orange cable, you must cut a red or cable.

If you cut a green cable, you must cut an orange or white cable.

If you cut a purple cable, you cannot cut a purple, green, orange, or white cable.

If the list of cuts does not result in the bomb exploding, the output should be a tape `bomb defused`. If any of the rules were broken, it should output the tape `boom`.

```!:
:-  %say
|=  [* [cuts=(list tape) ~] ~]
:-  %noun
=<
(defuse cuts)
|%
++  defuse
|=  cuts=(list tape)
|-
?~  cuts
"bomb defused"
=/  cut  i.cuts
\$(bad (~(got by rules) cut), cuts t.cuts)
\$(bad (~(got by rules) cut), cuts t.cuts)
"boom"
++  rules
%-  ~(gas by *(map tape (list tape)))
:~  :-  "white"
:~  "white"
"black"
==
:-  "red"
:~  "white"
"red"
"black"
"orange"
"purple"
==
:-  "black"
:~  "white"
"green"
"orange"
==
:-  "orange"
:~  "white"
"orange"
"green"
"purple"
==
:-  "green"
:~  "red"
"black"
"green"
"purple"
==
:-  "purple"
:~  "purple"
"green"
"orange"
"white"
==
==
--
```

The start of our generator is simple enough. The `!:` in the first line enables a full stack trace in the event of an error. The following three lines create a `%say` generator that takes a list of tape as an argument.

We next use the `=<` rune to compose two hoons, inverted. Remember that it's good Hoon style is to have the heaviest parts to the bottom -- that's what we're doing here.

We call `defuse` and pass it `cuts`, the list of tapes we were provided with when the generator started.

Next is the core that has two arms, `defuse` and `rules`. Let's look at `rules` quickly.

We can see that it is a `map` of `tape` to `list` of `tape`. For each of the rules above we have specified when you cut a wire, which of the other wires you cannot cut. This required some reformulating of the rules given to use at the start, but it is much simpler to work with.

```++  defuse
|=  cuts=(list tape)
|-
?~  cuts
"bomb defused"
=/  cut  i.cuts
\$(bad (~(got by rules) cut), cuts t.cuts)
\$(bad (~(got by rules) cut), cuts t.cuts)
"boom"
```

The `defuse` arm is the meat of our program. It is a `gate` that takes a `list` of `tape`. We combine a defaulted `list` of `tape` with the face `bad` with the subject.

If `cuts` is empty, the bomb has been successfully defused.

Otherwise, we give `i.cuts`, the first item in the list, a face called `cut`.

If `bad` is empty, we recurse. This recursion sets `bad` to be the list of wires we can not cut from the `rules` map and make `cuts` `t.cuts` or the tail of `cuts`, removing the first element of the list.

If `bad` is not empty, we want to search inside of it for the current `cut`. If it's not listed, then we are safe to continue and recurse to process the rest of the cuts. However, if it is found in the bad list, the user has failed and the bomb explodes.