32 List().map(inc) |
71 List().map(inc) |
33 |
72 |
34 |
73 |
35 |
74 |
36 my_map(inc, List(1,2,3,4)) |
75 my_map(inc, List(1,2,3,4)) |
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 // A Recursive Web Crawler / Email Harvester |
|
44 //=========================================== |
|
45 // |
|
46 // the idea is to look for links using the |
|
47 // regular expression "https?://[^"]*" and for |
|
48 // email addresses using another regex. |
|
49 |
|
50 import io.Source |
|
51 import scala.util._ |
|
52 |
|
53 // gets the first 10K of a web-page |
|
54 def get_page(url: String) : String = { |
|
55 Try(Source.fromURL(url)("ISO-8859-1").take(10000).mkString). |
|
56 getOrElse { println(s" Problem with: $url"); ""} |
|
57 } |
|
58 |
|
59 // regex for URLs and emails |
|
60 val http_pattern = """"https?://[^"]*"""".r |
|
61 val email_pattern = """([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})""".r |
|
62 |
|
63 //test case: |
|
64 //email_pattern.findAllIn |
|
65 // ("foo bla christian@kcl.ac.uk 1234567").toList |
|
66 |
|
67 |
|
68 // drops the first and last character from a string |
|
69 def unquote(s: String) = s.drop(1).dropRight(1) |
|
70 |
|
71 def get_all_URLs(page: String): Set[String] = |
|
72 http_pattern.findAllIn(page).map(unquote).toSet |
|
73 |
|
74 // naive version of crawl - searches until a given depth, |
|
75 // visits pages potentially more than once |
|
76 def crawl(url: String, n: Int) : Unit = { |
|
77 if (n == 0) () |
|
78 else { |
|
79 println(s" Visiting: $n $url") |
|
80 for (u <- get_all_URLs(get_page(url))) crawl(u, n - 1) |
|
81 } |
|
82 } |
|
83 |
|
84 // some starting URLs for the crawler |
|
85 val startURL = """https://nms.kcl.ac.uk/christian.urban/""" |
|
86 |
|
87 crawl(startURL, 2) |
|
88 |
|
89 |
|
90 // a primitive email harvester |
|
91 def emails(url: String, n: Int) : Set[String] = { |
|
92 if (n == 0) Set() |
|
93 else { |
|
94 println(s" Visiting: $n $url") |
|
95 val page = get_page(url) |
|
96 val new_emails = email_pattern.findAllIn(page).toSet |
|
97 new_emails ++ (for (u <- get_all_URLs(page)) yield emails(u, n - 1)).flatten |
|
98 } |
|
99 } |
|
100 |
|
101 emails(startURL, 2) |
|
102 |
|
103 |
|
104 |
|
105 // Sudoku |
|
106 //======== |
|
107 |
|
108 // THE POINT OF THIS CODE IS NOT TO BE SUPER |
|
109 // EFFICIENT AND FAST, just explaining exhaustive |
|
110 // depth-first search |
|
111 |
|
112 //> using dep org.scala-lang.modules::scala-parallel-collections:1.0.4 |
|
113 import scala.collection.parallel.CollectionConverters.* |
|
114 |
|
115 |
|
116 val s1 = "s\n" |
|
117 val s2 = """s\n""" |
|
118 |
|
119 val game0 = """.14.6.3.. |
|
120 |62...4..9 |
|
121 |.8..5.6.. |
|
122 |.6.2....3 |
|
123 |.7..1..5. |
|
124 |5....9.6. |
|
125 |..6.2..3. |
|
126 |1..5...92 |
|
127 |..7.9.41.""".stripMargin.replaceAll("\\n", "") |
|
128 |
|
129 type Pos = (Int, Int) |
|
130 val EmptyValue = '.' |
|
131 val MaxValue = 9 |
|
132 |
|
133 def pretty(game: String): String = |
|
134 "\n" + (game.grouped(MaxValue).mkString("\n")) |
|
135 |
|
136 pretty(game0) |
|
137 |
|
138 |
|
139 val allValues = "123456789".toList |
|
140 val indexes = (0 to 8).toList |
|
141 |
|
142 def empty(game: String) = game.indexOf(EmptyValue) |
|
143 def isDone(game: String) = empty(game) == -1 |
|
144 def emptyPosition(game: String) : Pos = { |
|
145 val e = empty(game) |
|
146 (e % MaxValue, e / MaxValue) |
|
147 } |
|
148 |
|
149 def get_row(game: String, y: Int) = |
|
150 indexes.map(col => game(y * MaxValue + col)) |
|
151 def get_col(game: String, x: Int) = |
|
152 indexes.map(row => game(x + row * MaxValue)) |
|
153 |
|
154 //get_row(game0, 0) |
|
155 //get_row(game0, 1) |
|
156 //get_col(game0, 0) |
|
157 |
|
158 def get_box(game: String, pos: Pos): List[Char] = { |
|
159 def base(p: Int): Int = (p / 3) * 3 |
|
160 val x0 = base(pos._1) |
|
161 val y0 = base(pos._2) |
|
162 val ys = (y0 until y0 + 3).toList |
|
163 (x0 until x0 + 3).toList |
|
164 .flatMap(x => ys.map(y => game(x + y * MaxValue))) |
|
165 } |
|
166 |
|
167 |
|
168 //get_box(game0, (3, 1)) |
|
169 |
|
170 |
|
171 // this is not mutable!! |
|
172 def update(game: String, pos: Int, value: Char): String = |
|
173 game.updated(pos, value) |
|
174 |
|
175 def toAvoid(game: String, pos: Pos): List[Char] = |
|
176 (get_col(game, pos._1) ++ |
|
177 get_row(game, pos._2) ++ |
|
178 get_box(game, pos)) |
|
179 |
|
180 def candidates(game: String, pos: Pos): List[Char] = |
|
181 allValues.diff(toAvoid(game, pos)) |
|
182 |
|
183 //candidates(game0, (0,0)) |
|
184 |
|
185 |
|
186 def search(game: String): List[String] = { |
|
187 if (isDone(game)) List(game) |
|
188 else { |
|
189 val cs = candidates(game, emptyPosition(game)) |
|
190 cs.par.map(c => search(update(game, empty(game), c))).flatten.toList |
|
191 } |
|
192 } |
|
193 |
|
194 pretty(game0) |
|
195 search(game0).map(pretty) |
|
196 |
|
197 val game1 = """23.915... |
|
198 |...2..54. |
|
199 |6.7...... |
|
200 |..1.....9 |
|
201 |89.5.3.17 |
|
202 |5.....6.. |
|
203 |......9.5 |
|
204 |.16..7... |
|
205 |...329..1""".stripMargin.replaceAll("\\n", "") |
|
206 |
|
207 search(game1).map(pretty) |
|
208 |
|
209 // a game that is in the hard category |
|
210 val game2 = """8........ |
|
211 |..36..... |
|
212 |.7..9.2.. |
|
213 |.5...7... |
|
214 |....457.. |
|
215 |...1...3. |
|
216 |..1....68 |
|
217 |..85...1. |
|
218 |.9....4..""".stripMargin.replaceAll("\\n", "") |
|
219 |
|
220 search(game2).map(pretty) |
|
221 |
|
222 // game with multiple solutions |
|
223 val game3 = """.8...9743 |
|
224 |.5...8.1. |
|
225 |.1....... |
|
226 |8....5... |
|
227 |...8.4... |
|
228 |...3....6 |
|
229 |.......7. |
|
230 |.3.5...8. |
|
231 |9724...5.""".stripMargin.replaceAll("\\n", "") |
|
232 |
|
233 search(game3).map(pretty).foreach(println) |
|
234 |
|
235 // for measuring time |
|
236 def time_needed[T](i: Int, code: => T) = { |
|
237 val start = System.nanoTime() |
|
238 for (j <- 1 to i) code |
|
239 val end = System.nanoTime() |
|
240 s"${(end - start) / 1.0e9} secs" |
|
241 } |
|
242 |
|
243 time_needed(2, search(game2)) |
|
244 |
|
245 |
|
246 // concurrency |
|
247 // scala-cli --extra-jars scala-parallel-collections_3-1.0.4.jar |
|
248 // import scala.collection.parallel.CollectionConverters._ |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 // String Interpolations |
|
254 //======================= |
|
255 |
|
256 def cube(n: Int) : Int = n * n * n |
|
257 |
|
258 val n = 3 |
|
259 println("The cube of " + n + " is " + cube(n) + ".") |
|
260 |
|
261 println(s"The cube of $n is ${cube(n)}.") |
|
262 |
|
263 // or even |
|
264 |
|
265 println(s"The cube of $n is ${n * n * n}.") |
|
266 |
|
267 // helpful for debugging purposes |
|
268 // |
|
269 // "The most effective debugging tool is still careful |
|
270 // thought, coupled with judiciously placed print |
|
271 // statements." |
|
272 // — Brian W. Kernighan, in Unix for Beginners (1979) |
|
273 |
|
274 |
|
275 def gcd_db(a: Int, b: Int) : Int = { |
|
276 println(s"Function called with $a and $b.") |
|
277 if (b == 0) a else gcd_db(b, a % b) |
|
278 } |
|
279 |
|
280 gcd_db(48, 18) |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 // Recursion Again ;o) |
|
286 //==================== |
|
287 |
|
288 |
|
289 // another well-known example: Towers of Hanoi |
|
290 //============================================= |
|
291 |
|
292 def move(from: Char, to: Char) = |
|
293 println(s"Move disc from $from to $to!") |
|
294 |
|
295 def hanoi(n: Int, from: Char, via: Char, to: Char) : Unit = { |
|
296 if (n == 0) () |
|
297 else { |
|
298 hanoi(n - 1, from, to, via) |
|
299 move(from, to) |
|
300 hanoi(n - 1, via, from, to) |
|
301 } |
|
302 } |
|
303 |
|
304 hanoi(4, 'A', 'B', 'C') |
|
305 |
76 |
306 |
77 |
307 |
78 |
308 // Pattern Matching |
79 // Pattern Matching |
309 //================== |
80 //================== |
611 parse_date("2019-11-26") |
388 parse_date("2019-11-26") |
612 parse_date("26/11/2019") |
389 parse_date("26/11/2019") |
613 parse_date("26.11.2019") |
390 parse_date("26.11.2019") |
614 |
391 |
615 |
392 |
|
393 // Countdown Game using Powerset |
|
394 //=============================== |
|
395 |
|
396 |
|
397 def powerset(xs: Set[Int]) : Set[Set[Int]] = { |
|
398 if (xs == Set()) Set(Set()) |
|
399 else { |
|
400 val ps = powerset(xs.tail) |
|
401 ps ++ ps.map(_ + xs.head) |
|
402 } |
|
403 } |
|
404 |
|
405 powerset(Set(1,2,3)).mkString("\n") |
|
406 |
|
407 // proper subsets |
|
408 def psubsets(xs: Set[Int]) = |
|
409 powerset(xs) -- Set(Set(), xs) |
|
410 |
|
411 psubsets(Set(1,2,3)).mkString("\n") |
|
412 |
|
413 def splits(xs: Set[Int]) : Set[(Set[Int], Set[Int])] = |
|
414 psubsets(xs).map(s => (s, xs -- s)) |
|
415 |
|
416 splits(Set(1,2,3,4)).mkString("\n") |
|
417 |
|
418 |
|
419 enum Tree { |
|
420 case Num(i: Int) |
|
421 case Add(l: Tree, r: Tree) |
|
422 case Mul(l: Tree, r: Tree) |
|
423 case Sub(l: Tree, r: Tree) |
|
424 case Div(l: Tree, r: Tree) |
|
425 } |
|
426 import Tree._ |
|
427 |
|
428 //pretty printing |
|
429 def pp(tr: Tree) : String = tr match { |
|
430 case Num(n) => s"$n" |
|
431 case Add(l, r) => s"(${pp(l)} + ${pp(r)})" |
|
432 case Mul(l, r) => s"(${pp(l)} * ${pp(r)})" |
|
433 case Sub(l, r) => s"(${pp(l)} - ${pp(r)})" |
|
434 case Div(l, r) => s"(${pp(l)} / ${pp(r)})" |
|
435 } |
|
436 |
|
437 |
|
438 def eval(tr: Tree) : Option[Int] = tr match { |
|
439 case Num(n) => Some(n) |
|
440 case Add(l, r) => |
|
441 for (ln <- eval(l); rn <- eval(r)) yield ln + rn |
|
442 case Mul(l, r) => |
|
443 for (ln <- eval(l); rn <- eval(r)) yield ln * rn |
|
444 case Sub(l, r) => |
|
445 for (ln <- eval(l); rn <- eval(r)) yield ln - rn |
|
446 case Div(l, r) => |
|
447 for (ln <- eval(l); rn <- eval(r); if rn != 0) |
|
448 yield ln / rn |
|
449 } |
|
450 |
|
451 |
|
452 |
|
453 def search(nums: Set[Int]) : Set[Tree] = nums.size match { |
|
454 case 0 => Set() |
|
455 case 1 => Set(Num(nums.head)) |
|
456 case 2 => { |
|
457 val ln = Num(nums.head) |
|
458 val rn = Num(nums.tail.head) |
|
459 Set(Add(ln, rn), Mul(ln, rn), |
|
460 Sub(ln, rn), Sub(rn, ln), |
|
461 Div(ln, rn), Div(rn, ln)) |
|
462 } |
|
463 case n => { |
|
464 val spls = splits(nums) |
|
465 val res = |
|
466 for ((ls, rs) <- spls) yield { |
|
467 for (lt <- search(ls); |
|
468 rt <- search(rs)) yield { |
|
469 Set(Add(lt, rt), Mul(lt, rt), |
|
470 Sub(lt, rt), Sub(rt, lt), |
|
471 Div(lt, rt), Div(rt, lt)) |
|
472 } |
|
473 } |
|
474 res.flatten.flatten |
|
475 } |
|
476 } |
|
477 |
|
478 Set(Set(1,2), Set(2,3), Set(4,5)).flatten |
|
479 |
|
480 search(Set(1)) |
|
481 search(Set(1, 2)).mkString("\n") |
|
482 search(Set(1, 2,3)).map(pp).mkString("\n") |
|
483 search(Set(1, 2,3)).map(pl).mkString("\n") |
|
484 search(Set(1, 2,3)).map(tr => s"${pp(tr)} = ${eval(tr)}").mkString("\n") |
|
485 |
|
486 |
|
487 def search(nums: Set[Int]) : Set[Tree] = nums.size match { |
|
488 case 0 => Set() |
|
489 case 1 => Set(Num(nums.head)) |
|
490 case xs => { |
|
491 val spls = splits(nums) |
|
492 val subtrs = |
|
493 for ((lspls, rspls) <- spls; |
|
494 lt <- search(lspls); |
|
495 rt <- search(rspls)) yield { |
|
496 Set(Add(lt, rt), Mul(lt, rt)) |
|
497 } |
|
498 subtrs.flatten |
|
499 } |
|
500 } |
|
501 |
|
502 println(search(Set(1,2,3,4)).mkString("\n")) |
|
503 |
|
504 def pp(tr: Tree) : String = tr match { |
|
505 case Num(n) => s"$n" |
|
506 case Add(l, r) => s"(${pp(l)} + ${pp(r)})" |
|
507 case Mul(l, r) => s"(${pp(l)} * ${pp(r)})" |
|
508 } |
|
509 |
|
510 |
|
511 |
|
512 def eval(tr: Tree) : Int = tr match { |
|
513 case Num(n) => n |
|
514 case Add(l, r) => eval(l) + eval(r) |
|
515 case Mul(l, r) => eval(l) * eval(r) |
|
516 } |
|
517 |
|
518 |
|
519 |
|
520 |
|
521 |
|
522 def search(nums: Set[Int]) : Set[Tree] = nums.size match { |
|
523 case 0 => Set() |
|
524 case 1 => Set(Num(nums.head)) |
|
525 case 2 => { |
|
526 val l = nums.head |
|
527 val r = nums.tail.head |
|
528 Set(Add(Num(l), Num(r)), |
|
529 Mul(Num(l), Num(r))) |
|
530 ++ Option.when(l <= r)(Sub(Num(r), Num(l))) |
|
531 ++ Option.when(l > r)(Sub(Num(l), Num(r))) |
|
532 ++ Option.when(r > 0 && l % r == 0)(Div(Num(l), Num(r))) |
|
533 ++ Option.when(l > 0 && r % l == 0)(Div(Num(r), Num(l))) |
|
534 } |
|
535 case xs => { |
|
536 val spls = splits(nums) |
|
537 val subtrs = |
|
538 for ((lspls, rspls) <- spls; |
|
539 lt <- search(lspls); |
|
540 rt <- search(rspls)) yield { |
|
541 Set(Add(lt, rt), Sub(lt, rt), |
|
542 Mul(lt, rt), Div(lt, rt)) |
|
543 } |
|
544 subtrs.flatten |
|
545 } |
|
546 } |
|
547 |
|
548 println(search(Set(1,2,3,4)).mkString("\n")) |
|
549 |
|
550 def eval(tr: Tree) : Option[Int] = tr match { |
|
551 case Num(n) => Some(n) |
|
552 case Add(l, r) => |
|
553 for (rl <- eval(l); rr <- eval(r)) yield rl + rr |
|
554 case Mul(l, r) => |
|
555 for (rl <- eval(l); rr <- eval(r)) yield rl * rr |
|
556 case Sub(l, r) => |
|
557 for (rl <- eval(l); rr <- eval(r); |
|
558 if 0 <= rl - rr) yield rl - rr |
|
559 case Div(l, r) => |
|
560 for (rl <- eval(l); rr <- eval(r); |
|
561 if rr > 0 && rl % rr == 0) yield rl / rr |
|
562 } |
|
563 |
|
564 eval(Add(Num(1), Num(2))) |
|
565 eval(Mul(Add(Num(1), Num(2)), Num(4))) |
|
566 eval(Sub(Num(3), Num(2))) |
|
567 eval(Sub(Num(3), Num(6))) |
|
568 eval(Div(Num(6), Num(2))) |
|
569 eval(Div(Num(6), Num(4))) |
|
570 |
|
571 def time_needed[T](n: Int, code: => T) = { |
|
572 val start = System.nanoTime() |
|
573 for (i <- (0 to n)) code |
|
574 val end = System.nanoTime() |
|
575 (end - start) / 1.0e9 |
|
576 } |
|
577 |
|
578 def check(xs: Set[Int], target: Int) = |
|
579 search(xs).find(eval(_) == Some(target)) |
|
580 |
|
581 for (sol <- check(Set(50, 5, 4, 9, 10, 8), 560)) { |
|
582 println(s"${pp(sol)} => ${eval(sol)}") |
|
583 } |
|
584 |
|
585 |
|
586 |
|
587 time_needed(1, check(Set(50, 5, 4, 9, 10, 8), 560)) |
|
588 |
|
589 |
|
590 println(check(Set(25, 5, 2, 10, 7, 1), 986).mkString("\n")) |
|
591 |
|
592 for (sol <- check(Set(25, 5, 2, 10, 7, 1), 986)) { |
|
593 println(s"${pp(sol)} => ${eval(sol)}") |
|
594 } |
|
595 |
|
596 for (sol <- check(Set(25, 5, 2, 10, 7, 1), -1)) { |
|
597 println(s"${pp(sol)} => ${eval(sol)}") |
|
598 } |
|
599 |
|
600 for (sol <- check(Set(100, 25, 75, 50, 7, 10), 360)) { |
|
601 println(s"${pp(sol)} => ${eval(sol)}") |
|
602 } |
|
603 time_needed(1, check(Set(100, 25, 75, 50, 7, 10), 360)) |
|
604 |
|
605 |
|
606 |
|
607 time_needed(1, check(Set(25, 5, 2, 10, 7, 1), 986)) |
|
608 time_needed(1, check(Set(25, 5, 2, 10, 7, 1), -1)) |
|
609 |
|
610 |
|
611 def generate(nums: Set[Int]) : Set[(Tree, Int)] = nums.size match { |
|
612 case 0 => Set() |
|
613 case 1 => Set((Num(nums.head), nums.head)) |
|
614 case xs => { |
|
615 val spls = splits(nums) |
|
616 val subtrs = |
|
617 for ((lspls, rspls) <- spls; |
|
618 (lt, ln) <- generate(lspls); |
|
619 (rt, rn) <- generate(rspls)) yield { |
|
620 Set((Add(lt, rt), ln + rn), |
|
621 (Mul(lt, rt), ln * rn)) |
|
622 ++ Option.when(ln <= rn)((Sub(rt, lt), rn - ln)) |
|
623 ++ Option.when(ln > rn)((Sub(lt, rt), ln - rn)) |
|
624 ++ Option.when(rn > 0 && ln % rn == 0)((Div(lt, rt), ln / rn)) |
|
625 ++ Option.when(ln > 0 && rn % ln == 0)((Div(rt, lt), rn / ln)) |
|
626 } |
|
627 subtrs.flatten |
|
628 } |
|
629 } |
|
630 |
|
631 def check2(xs: Set[Int], target: Int) = |
|
632 generate(xs).find(_._2 == target) |
|
633 |
|
634 for ((sol, ev) <- check2(Set(50, 5, 4, 9, 10, 8), 560)) { |
|
635 println(s"${pp(sol)} => ${eval(sol)} / $ev") |
|
636 } |
|
637 |
|
638 time_needed(1, check(Set(50, 5, 4, 9, 10, 8), 560)) |
|
639 time_needed(1, check2(Set(50, 5, 4, 9, 10, 8), 560)) |
|
640 |
|
641 time_needed(1, check(Set(50, 5, 4, 9, 10, 8), -1)) |
|
642 time_needed(1, check2(Set(50, 5, 4, 9, 10, 8), -1)) |
|
643 |
|
644 |
|
645 // Sudoku |
|
646 //======== |
|
647 |
|
648 // THE POINT OF THIS CODE IS NOT TO BE SUPER |
|
649 // EFFICIENT AND FAST, just explaining exhaustive |
|
650 // depth-first search |
|
651 |
|
652 //> using dep org.scala-lang.modules::scala-parallel-collections:1.0.4 |
|
653 import scala.collection.parallel.CollectionConverters.* |
|
654 |
|
655 |
|
656 val s1 = "s\n" |
|
657 val s2 = """s\n""" |
|
658 |
|
659 val game0 = """.14.6.3.. |
|
660 |62...4..9 |
|
661 |.8..5.6.. |
|
662 |.6.2....3 |
|
663 |.7..1..5. |
|
664 |5....9.6. |
|
665 |..6.2..3. |
|
666 |1..5...92 |
|
667 |..7.9.41.""".stripMargin.replaceAll("\\n", "") |
|
668 |
|
669 type Pos = (Int, Int) |
|
670 val EmptyValue = '.' |
|
671 val MaxValue = 9 |
|
672 |
|
673 def pretty(game: String): String = |
|
674 "\n" + (game.grouped(MaxValue).mkString("\n")) |
|
675 |
|
676 pretty(game0) |
|
677 |
|
678 |
|
679 val allValues = "123456789".toList |
|
680 val indexes = (0 to 8).toList |
|
681 |
|
682 def empty(game: String) = game.indexOf(EmptyValue) |
|
683 def isDone(game: String) = empty(game) == -1 |
|
684 def emptyPosition(game: String) : Pos = { |
|
685 val e = empty(game) |
|
686 (e % MaxValue, e / MaxValue) |
|
687 } |
|
688 |
|
689 def get_row(game: String, y: Int) = |
|
690 indexes.map(col => game(y * MaxValue + col)) |
|
691 def get_col(game: String, x: Int) = |
|
692 indexes.map(row => game(x + row * MaxValue)) |
|
693 |
|
694 //get_row(game0, 0) |
|
695 //get_row(game0, 1) |
|
696 //get_col(game0, 0) |
|
697 |
|
698 def get_box(game: String, pos: Pos): List[Char] = { |
|
699 def base(p: Int): Int = (p / 3) * 3 |
|
700 val x0 = base(pos._1) |
|
701 val y0 = base(pos._2) |
|
702 val ys = (y0 until y0 + 3).toList |
|
703 (x0 until x0 + 3).toList |
|
704 .flatMap(x => ys.map(y => game(x + y * MaxValue))) |
|
705 } |
|
706 |
|
707 |
|
708 //get_box(game0, (3, 1)) |
|
709 |
|
710 |
|
711 // this is not mutable!! |
|
712 def update(game: String, pos: Int, value: Char): String = |
|
713 game.updated(pos, value) |
|
714 |
|
715 def toAvoid(game: String, pos: Pos): List[Char] = |
|
716 (get_col(game, pos._1) ++ |
|
717 get_row(game, pos._2) ++ |
|
718 get_box(game, pos)) |
|
719 |
|
720 def candidates(game: String, pos: Pos): List[Char] = |
|
721 allValues.diff(toAvoid(game, pos)) |
|
722 |
|
723 //candidates(game0, (0,0)) |
|
724 |
|
725 |
|
726 def search(game: String): List[String] = { |
|
727 if (isDone(game)) List(game) |
|
728 else { |
|
729 val cs = candidates(game, emptyPosition(game)) |
|
730 cs.par.map(c => search(update(game, empty(game), c))).flatten.toList |
|
731 } |
|
732 } |
|
733 |
|
734 pretty(game0) |
|
735 search(game0).map(pretty) |
|
736 |
|
737 val game1 = """23.915... |
|
738 |...2..54. |
|
739 |6.7...... |
|
740 |..1.....9 |
|
741 |89.5.3.17 |
|
742 |5.....6.. |
|
743 |......9.5 |
|
744 |.16..7... |
|
745 |...329..1""".stripMargin.replaceAll("\\n", "") |
|
746 |
|
747 search(game1).map(pretty) |
|
748 |
|
749 // a game that is in the hard category |
|
750 val game2 = """8........ |
|
751 |..36..... |
|
752 |.7..9.2.. |
|
753 |.5...7... |
|
754 |....457.. |
|
755 |...1...3. |
|
756 |..1....68 |
|
757 |..85...1. |
|
758 |.9....4..""".stripMargin.replaceAll("\\n", "") |
|
759 |
|
760 search(game2).map(pretty) |
|
761 |
|
762 // game with multiple solutions |
|
763 val game3 = """.8...9743 |
|
764 |.5...8.1. |
|
765 |.1....... |
|
766 |8....5... |
|
767 |...8.4... |
|
768 |...3....6 |
|
769 |.......7. |
|
770 |.3.5...8. |
|
771 |9724...5.""".stripMargin.replaceAll("\\n", "") |
|
772 |
|
773 search(game3).map(pretty).foreach(println) |
|
774 |
|
775 // for measuring time |
|
776 def time_needed[T](i: Int, code: => T) = { |
|
777 val start = System.nanoTime() |
|
778 for (j <- 1 to i) code |
|
779 val end = System.nanoTime() |
|
780 s"${(end - start) / 1.0e9} secs" |
|
781 } |
|
782 |
|
783 time_needed(2, search(game2)) |
|
784 |
|
785 |
|
786 // concurrency |
|
787 // scala-cli --extra-jars scala-parallel-collections_3-1.0.4.jar |
|
788 // import scala.collection.parallel.CollectionConverters._ |
|
789 |
|
790 |
|
791 |
|
792 |
|
793 // String Interpolations |
|
794 //======================= |
|
795 |
|
796 def cube(n: Int) : Int = n * n * n |
|
797 |
|
798 val n = 3 |
|
799 println("The cube of " + n + " is " + cube(n) + ".") |
|
800 |
|
801 println(s"The cube of $n is ${cube(n)}.") |
|
802 |
|
803 // or even |
|
804 |
|
805 println(s"The cube of $n is ${n * n * n}.") |
|
806 |
|
807 // helpful for debugging purposes |
|
808 // |
|
809 // "The most effective debugging tool is still careful |
|
810 // thought, coupled with judiciously placed print |
|
811 // statements." |
|
812 // — Brian W. Kernighan, in Unix for Beginners (1979) |
|
813 |
|
814 |
|
815 def gcd_db(a: Int, b: Int) : Int = { |
|
816 println(s"Function called with $a and $b.") |
|
817 if (b == 0) a else gcd_db(b, a % b) |
|
818 } |
|
819 |
|
820 gcd_db(48, 18) |
|
821 |
|
822 |
|
823 |
|
824 |
|
825 // Recursion Again ;o) |
|
826 //==================== |
|
827 |
|
828 |
|
829 // another well-known example: Towers of Hanoi |
|
830 //============================================= |
|
831 |
|
832 def move(from: Char, to: Char) = |
|
833 println(s"Move disc from $from to $to!") |
|
834 |
|
835 def hanoi(n: Int, from: Char, via: Char, to: Char) : Unit = { |
|
836 if (n == 0) () |
|
837 else { |
|
838 hanoi(n - 1, from, to, via) |
|
839 move(from, to) |
|
840 hanoi(n - 1, via, from, to) |
|
841 } |
|
842 } |
|
843 |
|
844 hanoi(4, 'A', 'B', 'C') |
|
845 |
|
846 |
|
847 |
|
848 |
|
849 |
616 |
850 |
617 |
851 |
618 // Map type (upper-case) |
852 // Map type (upper-case) |
619 //======================= |
853 //======================= |
620 |
854 |