File: zebra.sml

package info (click to toggle)
mlton 20100608-2
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 34,980 kB
  • ctags: 69,089
  • sloc: ansic: 18,421; lisp: 2,879; makefile: 1,570; sh: 1,325; pascal: 256; asm: 97
file content (298 lines) | stat: -rw-r--r-- 9,528 bytes parent folder | download | duplicates (7)
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
(* Copyright Stephen Weeks (sweeks@sweeks.com).  1999-6-21.
 *
 * This code solves the following "zebra" puzzle, and prints the solution.
 * There are 120^5 ~= 24 billion possibilities, so exhaustive search should
 * work fine, but I decided to write something that was a bit more clever.
 * It took me longer to write (2.5 hours) than to write exhaustive search, but
 * it runs fast (0.06 seconds on my 400MhZ P6). The code only needs to explore
 * 3342 posibilites to solve the puzzle.
 *
 * Here is the puzzle.
 *
 * This word problem has 25 variables and 24 are given values. You must
 * solve 
 * the 25th. 
 * 
 * The trick is HOW? 
 * 
 * If you look at the problem mathematically, no sweat. If you get lost
 * in the 
 * English, you are dead. 
 * 
 * You will know you are right by checking the answer with all the
 * conditions. 
 * 
 * Less than 1 percent of the population can solve this problem. 
 * 
 * The question is: Based on the following clues, who owns the zebra? 
 * 
 * **There are five houses. 
 * 
 * **Each house has its own unique color. 
 * 
 * **All house owners are of different nationalities. 
 * 
 * **They all have different pets. 
 * 
 * **They all drink different drinks. 
 * 
 * **They all smoke different cigarettes. 
 * 
 * **The Englishman lives in the red house. 
 * 
 * **The Swede has a dog. 
 * 
 * **The Dane drinks tea. 
 * 
 * **The green house is adjacent to the white house on the left. 
 * 
 * **In the green house they drink coffee. 
 * 
 * **The man who smokes Pall Malls has birds. 
 * 
 * **In the yellow house they smoke Dunhills. 
 * 
 * **In the middle house they drink milk. 
 * 
 * **The Norwegian lives in the first house. 
 * 
 * **The man who smokes Blends lives in a house next to the house with
 * cats. 
 * 
 * **In a house next to the house where they have a horse, they smoke 
 * Dunhills. 
 * 
 * **The man who smokes Blue Masters drinks beer. 
 * 
 * **The German smokes Princes. 
 * 
 * **The Norwegian lives next to the blue house. 
 * 
 * **They drink water in a house next to the house where they smoke
 * Blends. 
 * 
 * Who owns the zebra? 
 *)

fun peek (l, p) = List.find p l
fun map (l, f) = List.map f l
fun fold (l, b, f) = List.foldl f b l

datatype cigarette = Blend | BlueMaster | Dunhill | PallMall | Prince
val cigaretteToString =
   fn Blend => "Blend"
    | BlueMaster => "BlueMaster"
    | Dunhill => "Dunhill"
    | PallMall => "PallMall"
    | Prince => "Prince"
datatype color = Blue | Green | Red | White | Yellow
val colorToString =
   fn Blue => "Blue"
    | Green => "Green"
    | Red => "Red"
    | White => "White"
    | Yellow => "Yellow"
datatype drink = Beer | Coffee | Milk | Tea | Water
val drinkToString =
   fn Beer => "Beer"
    | Coffee => "Coffee"
    | Milk => "Milk"
    | Tea => "Tea"
    | Water => "Water"
datatype nationality = Dane | English | German | Norwegian | Swede
val nationalityToString =
   fn Dane => "Dane"
    | English => "English"
    | German => "German"
    | Norwegian => "Norwegian"
    | Swede => "Swede"
datatype pet = Bird | Cat | Dog | Horse | Zebra
val petToString =
   fn Bird => "Bird"
    | Cat => "Cat"
    | Dog => "Dog"
    | Horse => "Horse"
    | Zebra => "Zebra"

type pos = int
val poss = [1, 2, 3, 4, 5]
val first = SOME 1
val middle = SOME 3

type 'a attribute = {poss: pos list,
                     unknown: 'a list,
                     known: (pos * 'a) list}

exception Done
fun 'a fluidLet (r: 'a ref, x: 'a, f: unit -> 'b): 'b =
   let val old = !r
   in r := x
      ; (f () before r := old)
      handle Done => raise Done
           | e => (r := old; raise e)
   end

fun search () =
   let
      fun init (unknown: 'a list): 'a attribute ref =
         ref {poss = poss, unknown = unknown, known = []}
      val cigarettes = init [Blend, BlueMaster, Dunhill, PallMall, Prince]
      val colors = init [Blue, Green, Red, White, Yellow]
      val drinks = init [Beer, Coffee, Milk, Tea, Water]
      val nationalities = init [Dane, English, German, Norwegian, Swede]
      val pets = init [Bird, Cat, Dog, Horse, Zebra]

      fun ''a find (r: ''a attribute ref) (x: ''a): pos option =
         Option.map #1 (peek (#known (!r), fn (_, y) => x = y))
      val smoke = find cigarettes
      val color = find colors
      val drink = find drinks
      val nat = find nationalities
      val pet = find pets

      fun display () =
         let
            fun loop (r: 'a attribute ref, toString) =
               (List.app (fn i =>
                          let
                             val x = #2 (valOf (peek (#known (!r),
                                                   fn (j, _) => i = j)))
                             val s = toString x
                          in print s
                             ; print (CharVector.tabulate (12 - size s,
                                                         fn _ => #" "))
                          end) poss 
                ; print "\n")
         in
            loop (cigarettes, cigaretteToString)
            ; loop (colors, colorToString)
            ; loop (drinks, drinkToString)
            ; loop (nationalities, nationalityToString)
            ; loop (pets, petToString)
         end
   
      fun make f =
         fn (SOME x, SOME y) => f (x, y)
          | _ => true
      val same = make (op =)
      val adjacent = make (fn (x, y) => x = y - 1 orelse y = x - 1)
      val left = make (fn (x, y) => x = y - 1)

      val num = ref 0
      fun isConsistent (): bool =
         (num := !num + 1
          ;
         same (nat English, color Red)
         andalso same (nat Swede, pet Dog)
         andalso same (nat Dane, drink Tea)
         andalso left (color Green, color White)
         andalso same (color Green, drink Coffee)
         andalso same (smoke PallMall, pet Bird)
         andalso same (color Yellow, smoke Dunhill)
         andalso same (middle, drink Milk)
         andalso same (nat Norwegian, first)
         andalso adjacent (smoke Blend, pet Cat)
         andalso adjacent (pet Horse, smoke Dunhill)
         andalso same (drink Beer, smoke BlueMaster)
         andalso same (nat German, smoke Prince)
         andalso adjacent (nat Norwegian, color Blue)
         andalso adjacent (drink Water, smoke Blend)
          )
         
      fun tryEach (l, f) =
         let
            fun loop (l, ac) =
               case l of
                  [] => ()
                | x :: l => (f (x, l @ ac); loop (l, x :: ac))
         in loop (l, [])
         end
               
      fun try (r: 'a attribute ref,
              f: unit -> (('a attribute -> unit)
                          * ( unit -> unit))) =
         let val {poss, unknown, known} = !r
         in case unknown of
            [] => ()
          | _ => 
               tryEach (unknown, fn (x, unknown) =>
                       let val (each, done) = f ()
                       in tryEach (poss, fn (p, poss) =>
                                  let val attr = {known = (p, x) :: known,
                                                  unknown = unknown,
                                                  poss = poss}
                                  in fluidLet
                                     (r, attr, fn () =>
                                      if isConsistent () then each attr else ())
                                  end)
                          ; done ()
                       end)
         end

      (* loop takes the current state and either
       *   - terminates in the same state if there is no consistent extension
       *   - raises Done with the state set at the consistent extension
       *)
      exception Inconsistent
      exception Continue of unit -> unit
      fun loop (): unit =
         let
            fun test r =
               try
               (r, fn () =>
                let
                   datatype 'a attrs = None | One of 'a | Many
                   val attrs = ref None
                   fun each a =
                      case !attrs of
                         None => attrs := One a
                       | One _ => attrs := Many
                       | Many => ()
                   fun done () =
                      case !attrs of
                         None => raise Inconsistent
                       | One a => raise (Continue (fn () => fluidLet (r, a, loop)))
                       | Many => ()
                in (each, done)
                end)
            fun explore r =
               try (r, fn () =>
                   let
                      fun each _ = loop ()
                      fun done () = raise Inconsistent
                   in (each, done)
                   end)
         in (test cigarettes
             ; test colors
             ; test drinks
             ; test nationalities
             ; test pets
             ; explore cigarettes
             ; explore colors
             ; explore drinks
             ; explore nationalities
             ; explore pets
             ; raise Done)
            handle Inconsistent => ()
                 | Continue f => f ()
         end
      val _ =    loop () handle Done => ()
      val _ = if 3342 = !num
                 then ()
              else raise Fail "bug"
(*      val _ = display () *)
   in ()
   end

structure Main =
   struct
      fun doit n =
         let
            fun loop n =
               if n < 0
                  then ()
               else (search ()
                     ; loop (n - 1))
         in loop (n * 1000)
         end
   end