Koza, zelí, vlk je logická hádanka, v níž hráč (převozník) musí přemístit kozu, zelí a vlka z jednoho břehu řeky na druhý. Ve hře platí následující pravidla:
- Hráč má k dispozici loďku, do níž se vejde převozník a maximálně jeden předmět.
- Loďku musí vždy řídit převozník.
- Pokud zůstanou spolu na jednom břehu bez dozoru převozníka koza a vlk, tak vlk sežere kozu.
- Pokud zůstane spolu na jednom břehu bez dozoru převozníka koza a zelí, tak koza sežere zelí.
Strojové řešení
Z algoritmického hlediska se jedná o prohledávání stavového prostoru s návratem (backtracking). Backtrackingový algoritmus v každém svém kroku zkusí převézt jednu věc na druhý břeh a zkontroluje, jestli nedošlo k porušení podmínek úlohy (tj. nebyla sežrána koza nebo zelí). Pokud kontrola proběhne úspěšně, tak se proces opakuje. Pokud daný převoz způsobí nekonzistenci úlohy, tak se algoritmus vrátí do místa posledního rozhodnutí a převeze jinou věc (nebo se vrátí o rozhodnutí výše, pokud již byly všechny možnosti vyzkoušeny). Organizovaným prohledáváním ve stylu pokus-omyl algoritmus nalezne hledanou sekvenci kroků (řešení).
Kód
01.
/**
02.
* Solves the sheep-cabbage-wolf riddle and prints out its solution
03.
*/
04.
public
static
void
solveSheepCabbageWolf(){
05.
sheepCabbageWolf(
false
,
false
,
false
,
false
,
new
LinkedList<String>());
06.
}
07.
/**
08.
* Solves the sheep-cabbage-wolf riddle and prints out its solution (the actual worker method)
09.
* @param sheep true if the sheep is on the right bank, false otherwise
10.
* @param cabbage true if the cabbage is on the right bank, false otherwise
11.
* @param wolf true if the wolf is on the right bank, false otherwise
12.
* @param farmer true if the farmer (boat) is on the right bank, false otherwise
13.
* @param solution partial solution
14.
* @return false if the partial solution is invalid
15.
*/
16.
private
static
boolean
sheepCabbageWolf(
boolean
sheep,
boolean
cabbage,
boolean
wolf,
boolean
farmer, Deque<String> solution) {
17.
if
(sheep && cabbage && wolf && farmer) {
18.
printSolution(solution);
19.
return
true
;
20.
}
21.
if
(!checkConsistency(sheep, cabbage, wolf, farmer)) {
22.
return
false
;
23.
}
24.
if
(solution.isEmpty() || !solution.peek().equals(
"boatman"
)) {
25.
solution.addFirst(
"boatman"
);
26.
if
(sheepCabbageWolf(sheep, cabbage, wolf, !farmer, solution)) {
27.
return
true
;
28.
}
29.
solution.pop();
//backtrack
30.
}
31.
if
(sheep == farmer && (solution.isEmpty() || !solution.peek().equals(
"sheep"
))) {
32.
solution.addFirst(
"sheep"
);
33.
if
(sheepCabbageWolf(!sheep, cabbage, wolf, !farmer, solution)) {
34.
return
true
;
35.
}
36.
solution.pop();
//backtrack
37.
}
38.
if
(cabbage == farmer && (solution.isEmpty() || !solution.peek().equals(
"cabbage"
))) {
39.
solution.addFirst(
"cabbage"
);
40.
if
(sheepCabbageWolf(sheep, !cabbage, wolf, !farmer, solution)) {
41.
return
true
;
42.
}
43.
solution.pop();
//backtrack
44.
}
45.
if
(wolf == farmer && (solution.isEmpty() || !solution.peek().equals(
"wolf"
))) {
46.
solution.addFirst(
"wolf"
);
47.
if
(sheepCabbageWolf(sheep, cabbage, !wolf, !farmer, solution)) {
48.
return
true
;
49.
}
50.
solution.pop();
//backtrack
51.
}
52.
return
false
;
53.
}
54.
55.
/**
56.
* Check consistency of the partial solution
57.
* @param sheep if the sheep is on the right bank, false otherwise
58.
* @param cabbage if the cabbage is on the right bank, false otherwise
59.
* @param wolf if the wolf is on the right bank, false otherwise
60.
* @param farmer if the farmer is on the right bank, false otherwise
61.
* @return true if the solution is consistent, false otherwise
62.
*/
63.
private
static
boolean
checkConsistency(
boolean
sheep,
boolean
cabbage,
boolean
wolf,
boolean
farmer) {
64.
if
(sheep == cabbage && sheep != farmer) {
65.
return
false
;
66.
}
else
if
(sheep == wolf && sheep != farmer) {
67.
return
false
;
68.
}
69.
return
true
;
70.
}
71.
72.
/**
73.
* Prints out the solution of the sheep-cabbage-wolf problem
74.
* @param solution
75.
*/
76.
private
static
void
printSolution(Deque<String> solution) {
77.
while
(!solution.isEmpty()) {
78.
System.out.print(solution.pollLast() +
" "
);
79.
}
80.
System.out.println();
81.
}
01.
% farmer, sheep, cabbage, wolf
02.
start :- transport(left, left, left, left, empty,
X
).
03.
04.
opposite(left, right).
05.
opposite(right, left).
06.
07.
safe(
X
,
X
,
X
).
08.
safe(
X
,
Y
,
Z
) :-
X
\\=
Y
.
09.
10.
transport(right, right, right, right, _, []).
11.
12.
transport(
F
,
K
,
Z
,
V
,
B
,
X
) :-
13.
B
\\= empty,
14.
opposite(
F
,
F
2),
15.
safe(
K
,
Z
,
F
2),
16.
safe(
K
,
V
,
F
2),
17.
transport(
F
2,
K
,
Z
,
V
, empty,
X
1),
18.
append
([farmer],
X
1,
X
).
19.
20.
transport(
F
,
K
,
Z
,
V
,
B
,
X
) :-
21.
F
==
V
,
22.
B
\\= wolf,
23.
opposite(
F
,
F
2),
24.
opposite(
V
,
V
2),
25.
safe(
K
,
V
2,
F
2),
26.
safe(
K
,
Z
,
F
2),
27.
transport(
F
2,
K
,
Z
,
V
2, wolf,
X
1),
28.
append
([wolf],
X
1,
X
).
29.
30.
transport(
F
,
K
,
Z
,
V
,
B
,
X
) :-
31.
F
==
K
,
32.
B
\\= sheep,
33.
opposite(
F
,
F
2),
34.
opposite(
K
,
K
2),
35.
safe(
K
2,
Z
,
F
2),
36.
safe(
K
2,
V
,
F
2),
37.
transport(
F
2,
K
2,
Z
,
V
, sheep,
X
1),
38.
append
([sheep],
X
1,
X
).
39.
40.
transport(
F
,
K
,
Z
,
V
,
B
,
X
) :-
41.
F
==
Z
,
42.
B
\\= cabbage,
43.
opposite(
F
,
F
2),
44.
opposite(
Z
,
Z
2),
45.
safe(
K
,
Z
2,
F
2),
46.
safe(
K
,
V
,
F
2),
47.
transport(
F
2,
K
,
Z
2,
V
, cabbage,
X
1),
48.
append
([cabbage],
X
1,
X
).