1
+ package cps .logic
2
+
3
+ import org .junit .{Test ,Ignore }
4
+
5
+ import cps .*
6
+ import cps .monads .logic .*
7
+
8
+ /**
9
+ * Someone in Dreadsbury Mansion killed Aunt Agatha.
10
+ *Agatha
11
+ *, the butler
12
+ *, and Charles live in Dreadsbury Mansion
13
+ *, and
14
+ * are the only ones to live there.
15
+ * (+)A killer always hates and is
16
+ * (+) no richer than his victim.
17
+ * (+)Charles hates noone that Agatha hates
18
+ *.
19
+ *(+)Agatha hates everybody except the butler
20
+ *.(+) The butler hates everyone
21
+ *not richer than Aunt Agatha.
22
+ * (+) The butler hates everyone whom Agatha hates.
23
+ *(+) Noone hates everyone.
24
+ * Who killed Agatha ?
25
+ **/
26
+
27
+ class AgadaTest {
28
+
29
+ @ Test def testAgada (): Unit = {
30
+ val worlds = AgadaTest .allWorlds()
31
+ val result = worlds.toLazyList.toIndexedSeq
32
+ val killer = result.map(_.killer).toSet
33
+ // for { r <- result } {
34
+ // println(s"result.killer=${r.killer}")
35
+ // println(s"result.richer=${r.richer}")
36
+ // println(s"result.hates=${r.hates}")
37
+ // println("------------------")
38
+ // }
39
+ assert(killer.size == 1 )
40
+ assert(killer.head == AgadaTest .agata)
41
+ }
42
+
43
+ }
44
+
45
+ object AgadaTest {
46
+
47
+ case class Person (name : String )
48
+ case class Richer (p1 : Person , p2 : Person )
49
+ case class Hates (p1 : Person , p2 : Person )
50
+
51
+ val agata = Person (" Agatha" )
52
+ val butler = Person (" Butler" )
53
+ val charles = Person (" Charles" )
54
+
55
+ val citizents = List (agata,butler,charles)
56
+
57
+ case class World (
58
+ richer : Set [Richer ],
59
+ hates : Set [Hates ],
60
+ killer : Person
61
+ )
62
+
63
+ def allWorlds (): LogicStream [World ] = reify[LogicStream ]{
64
+ val richer = reflect(allRichers())
65
+ val hates = reflect(allHates())
66
+ val killer = reflect(LogicStream .fromCollection(citizents))
67
+
68
+ // a killer alwasy hates. ... victim
69
+
70
+ guard {
71
+ hates.contains(Hates (killer,agata))
72
+ }
73
+
74
+ guard {
75
+ // a killer is no richer than his victim
76
+ ! richer.contains(Richer (killer,agata))
77
+ }
78
+
79
+
80
+ // The butler hates everyone
81
+ // * not richer than Aunt Agatha
82
+ guard {
83
+ citizents.forall(p =>
84
+ if (richer.contains(Richer (agata,p))) {
85
+ hates.contains(Hates (butler,p))
86
+ } else {
87
+ true
88
+ }
89
+ )
90
+ }
91
+
92
+ World (richer, hates, killer)
93
+ }
94
+
95
+
96
+ def allRichers (): LogicStream [Set [Richer ]] = reify[LogicStream ]{
97
+ val allRicher = for (p1 <- citizents; p2 <- citizents if p1!= p2) yield Richer (p1,p2)
98
+ val candidate = reflect(allSubsets(allRicher.toIndexedSeq)).toSet
99
+ guard(candidate.forall{ case Richer (p1,p2) => ! candidate.contains(Richer (p2,p1)) })
100
+ guard{
101
+ // note, that if we assume, that all wealth are different.
102
+ // if we don't assume this, other solutions become possible
103
+ val pairs = for { p1 <- citizents; p2 <- citizents if p1!= p2 } yield (p1,p2)
104
+ pairs.forall{ case (p1,p2) =>
105
+ candidate.contains(Richer (p1,p2)) || candidate.contains(Richer (p2,p1))
106
+ }
107
+ }
108
+ candidate
109
+ }
110
+
111
+ def allHates (): LogicStream [Set [Hates ]] = reify[LogicStream ]{
112
+ def allPairs = for (p1 <- citizents; p2 <- citizents) yield Hates (p1,p2)
113
+ val candidate = reflect(allSubsets(allPairs.toIndexedSeq)).toSet
114
+
115
+ // Agatha hates everybody except the butler
116
+ guard(
117
+ ! candidate.contains(Hates (agata,butler)) &&
118
+ candidate.contains(Hates (agata,agata)) &&
119
+ candidate.contains(Hates (agata,charles))
120
+ )
121
+
122
+ // Charles hates noone that Agatha hates
123
+ // The butler hates everyone whom Agatha hates.
124
+ guard(
125
+ citizents.forall{ p =>
126
+ if (candidate.contains(Hates (agata,p))) {
127
+ ! candidate.contains(Hates (charles,p))
128
+ &&
129
+ candidate.contains(Hates (butler,p))
130
+ } else {
131
+ true
132
+ }
133
+ }
134
+ )
135
+
136
+
137
+
138
+ // Noone hates everyone.
139
+ guard{
140
+ citizents.forall(p => ! citizents.forall(p1 => candidate.contains(Hates (p,p1))))
141
+ }
142
+
143
+ candidate
144
+ }
145
+
146
+ def allSubsets [T ](xs : IndexedSeq [T ], from: Int = 0 ): LogicStream [List [T ]] = reify[LogicStream ]{
147
+ if (xs.size == from) {
148
+ List .empty
149
+ } else {
150
+ val head = xs(from)
151
+ val tail = reflect(allSubsets(xs,from+ 1 ))
152
+ val next = LogicStream .pure(head:: tail) |+| LogicStream .pure(tail)
153
+ reflect(next)
154
+ }
155
+ }
156
+
157
+
158
+ }
0 commit comments