@@ -47,7 +47,8 @@ a piece of cargo.
4747Second, any item can be on one of two shores. Let's call these the "left shore" and the "right shore".
4848Define a class ``Shore `` containing ``"Left" `` and ``"Right" ``.
4949
50- It would be helpful to express "the other shore". You can do this by defining a member predicate
50+ It would be helpful to express "the other shore" to model moving from one side of the river to the other.
51+ You can do this by defining a member predicate
5152``other `` in the class ``Shore `` such that ``"Left".other() `` returns ``"Right" `` and vice versa.
5253
5354.. container :: toggle
@@ -59,8 +60,8 @@ It would be helpful to express "the other shore". You can do this by defining a
5960 .. literalinclude :: river-crossing.ql
6061 :lines: 25-38
6162
62- We also want a way to keep track of where the man, the goat, the cabbage, and the wolf are— call this combined
63- information the "state". Define a class ``State `` that encodes this information .
63+ We also want a way to keep track of where the man, the goat, the cabbage, and the wolf are at any point. We can call this combined
64+ information the "state". Define a class ``State `` that encodes the location of each piece of cargo .
6465For example, if the man is on the left shore, the goat on the right shore, and the cabbage and wolf on the left
6566shore, the state should be ``Left, Right, Left, Left ``.
6667
@@ -74,9 +75,10 @@ temporary variables in the body of a class are called `fields <https://help.semm
7475 *Show/hide code *
7576
7677 .. literalinclude :: river-crossing-1.ql
77- :lines: 33-43,84
78+ :lines: 33-43,90
7879
79- We are interested in two particular states, namely the initial state and the goal state.
80+ We are interested in two particular states, namely the initial state and the goal state,
81+ which we have to achieve to solve the puzzle.
8082Assuming that all items start on the left shore and end up on the right shore, define
8183``InitialState `` and ``GoalState `` as subclasses of ``State ``.
8284
@@ -87,7 +89,7 @@ Assuming that all items start on the left shore and end up on the right shore, d
8789 *Show/hide code *
8890
8991 .. literalinclude :: river-crossing-1.ql
90- :lines: 86-94
92+ :lines: 92-100
9193
9294.. pull-quote ::
9395
@@ -105,12 +107,12 @@ Using the above note, the QL code so far looks like this:
105107 *Show/hide code *
106108
107109 .. literalinclude :: river-crossing.ql
108- :lines: 14-55,100-110
110+ :lines: 14-55,105-115
109111
110112Model the action of "ferrying"
111113~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
112114
113- The basic act of ferrying moves the man and a cargo item to the other shore,
115+ The basic act of ferrying moves the man and one cargo item to the other shore,
114116resulting in a new state.
115117
116118Write a member predicate (of ``State ``) called ``ferry ``, that specifies what happens to the state
@@ -123,13 +125,13 @@ after ferrying a particular cargo. (Hint: Use the predicate ``other``.)
123125 *Show/hide code *
124126
125127 .. literalinclude :: river-crossing.ql
126- :lines: 57-66
128+ :lines: 57-70
127129
128130Of course, not all ferrying actions are possible. Add some extra conditions to describe when a ferrying
129- action is "safe", that is, it doesn't lead to a state where the goat or the cabbage get eaten.
131+ action is "safe". That is, it doesn't lead to a state where the goat or the cabbage get eaten.
130132For example, follow these steps:
131133
132- #. Define a predicate ``eats `` that encodes the conditions for when a "predator" is able to eat an
134+ #. Define a predicate ``eating `` that encodes the conditions for when a "predator" is able to eat an
133135 unguarded "prey".
134136 #. Define a predicate ``isSafe `` that holds when nothing gets eaten.
135137 #. Define a predicate ``safeFerry `` that restricts ``ferry `` to only include safe ferrying actions.
@@ -141,18 +143,20 @@ For example, follow these steps:
141143 *Show/hide code *
142144
143145 .. literalinclude :: river-crossing.ql
144- :lines: 68-78
146+ :lines: 72-84
145147
146148Find paths from one state to another
147149~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
148150
149151The main aim of this query is to find a path, that is, a list of successive ferrying actions, to get
150- from one state to another .
152+ from the initial state to the goal state .
151153
152- When finding the path , you should be careful to avoid "infinite" solutions . For example, the man
154+ When finding the solution , you should be careful to avoid "infinite" paths . For example, the man
153155could ferry the goat back and forth any number of times without ever reaching an unsafe state.
156+ Such a path would have an infinite number of river crossings without ever solving the puzzle.
154157
155- One way to restrict to finite solutions is to define a `member predicate <https://help.semmle.com/QL/ql-handbook/types.html#member-predicates >`__
158+ One way to restrict our paths to a finite number of river crossings is to define a
159+ `member predicate <https://help.semmle.com/QL/ql-handbook/types.html#member-predicates >`__
156160``State reachesVia(string path, int steps) ``.
157161The result of this predicate is any state that is reachable from the current state (``this ``) via
158162the given path in a specified finite number of steps.
@@ -176,10 +180,11 @@ for example ``steps <= 7``.
176180 *Show/hide code *
177181
178182 .. literalinclude :: river-crossing-1.ql
179- :lines: 67-83
183+ :lines: 73-89
180184
181185However, although this ensures that the solution is finite, it can still contain loops if the upper bound
182- for ``steps `` is large.
186+ for ``steps `` is large. In other words, you could get an inefficient solution by revisiting the same state
187+ multiple times.
183188
184189Instead of picking an arbitrary upper bound for the number of steps, you can avoid
185190counting steps altogether. If you keep track of states that have already been visited and ensure
@@ -205,7 +210,7 @@ the given path without revisiting any previously visited states.
205210 *Show/hide code *
206211
207212 .. literalinclude :: river-crossing.ql
208- :lines: 80-99
213+ :lines: 86-105
209214
210215Display the results
211216~~~~~~~~~~~~~~~~~~~
@@ -220,7 +225,7 @@ that returns the resulting path.
220225 *Show/hide code *
221226
222227 .. literalinclude :: river-crossing.ql
223- :lines: 112-114
228+ :lines: 118-120
224229
225230For now, the path defined in the above predicate ``reachesVia `` just lists the order of cargo items to ferry.
226231You could tweak the predicates and the select clause to make the solution clearer. Here are some suggestions:
@@ -240,5 +245,5 @@ Here are some more example QL queries that solve the river crossing puzzle:
240245 - Solutions described in more detail: https://lgtm.com/query/4550752404102766320/
241246 - Solutions displayed in a more visual way: https://lgtm.com/query/5824364611285694673/
242247
243- .. TODO: Add more examples
248+ .. TODO: Add more examples + check that the queries are "tidied up" and clear.
244249
0 commit comments