1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
| import tutorial
class Cargo extends string { Cargo() { this = "Nothing" or this = "Goat" or this = "Cabbage" or this = "Wolf" } } /** One of two shores. */ class Shore extends string { Shore() { this = "Left" or this = "Right" } /** Returns the other shore. */ Shore other() { this = "Left" and result = "Right" or this = "Right" and result = "Left" } } /** Renders the state as a string. */ string renderState(Shore manShore, Shore goatShore, Shore cabbageShore, Shore wolfShore) { result = manShore + "," + goatShore + "," + cabbageShore + "," + wolfShore } /** A record of where everything is. */ class State extends string { Shore manShore; Shore goatShore; Shore cabbageShore; Shore wolfShore; State() { this = renderState(manShore, goatShore, cabbageShore, wolfShore) } /** Returns the state that is reached after ferrying a particular cargo item. */ State ferry(Cargo cargo) { cargo = "Nothing" and result = renderState(manShore.other(), goatShore, cabbageShore, wolfShore) or cargo = "Goat" and result = renderState(manShore.other(), goatShore.other(), cabbageShore, wolfShore) or cargo = "Cabbage" and result = renderState(manShore.other(), goatShore, cabbageShore.other(), wolfShore) or cargo = "Wolf" and result = renderState(manShore.other(), goatShore, cabbageShore, wolfShore.other()) } /** * Holds if the state is safe. This occurs when neither the goat nor the cabbage * can get eaten. */ predicate isSafe() { // The goat can't eat the cabbage. (goatShore != cabbageShore or goatShore = manShore) and // The wolf can't eat the goat. (wolfShore != goatShore or wolfShore = manShore) } /** Returns the state that is reached after safely ferrying a cargo item. */ State safeFerry(Cargo cargo) { result = this.ferry(cargo) and result.isSafe() } /** man's move */ string towards() { manShore = "Left" and result = "to the left" or manShore = "Right" and result = "to the right" } /** * Returns all states that are reachable via safe ferrying. * `path` keeps track of how it is achieved. * `visitedStates` keeps track of previously visited states and is used to avoid loops. */ State reachesVia(string path, string visitedStates) { // Reachable in 1 step by ferrying a specific cargo exists(Cargo cargo | result = this.safeFerry(cargo) and visitedStates = result and path = "First " + cargo + " is ferried " + result.towards() + "," ) or // Reachable by first following pathSoFar and then ferrying cargo exists(string pathSoFar, string visitedStatesSoFar, Cargo cargo | result = this.reachesVia(pathSoFar, visitedStatesSoFar).safeFerry(cargo) and // resulting state is not visited yet not exists(int i | i = visitedStatesSoFar.indexOf(result)) and visitedStates = visitedStatesSoFar + "_" + result and path = pathSoFar + "\nthen " + cargo + " is ferried " + result.towards() + "," ) } } /** The initial state, where everything is on the left shore. */ class InitialState extends State { InitialState() { this = renderState("Left", "Left", "Left", "Left") } } /** The goal state, where everything is on the right shore. */ class GoalState extends State { GoalState() { this = renderState("Right", "Right", "Right", "Right") } } from string path where any(InitialState i).reachesVia(path, _) = any(GoalState g) select path + "."
|