- // Wrap in object, otherwise ammonite .par or Future would not work
- object Solution {
- val O = scala.io.Source.fromFile("input").getLines.map(_.toCharArray).toArray
- //FIXME: This M is global variable entangled in case class S for defining empty etc.
- // Also this is used in findStable which is sad.
- var M = O
- val R = M.length
- val C = M.head.length
- case class S(r: Int, c: Int) {
- def +(o: S) = S(r + o.r, c + o.c)
- def ok = r >= 0 && r < R && c >= 0 && c < C
- def empty = M(r)(c) == 'L'
- def occupied = M(r)(c) == '#'
- def floor = M(r)(c) == '.'
- def dbg = s"$r $c ${M(r)(c)}"
- def update(v: Char, n: Array[Array[Char]]) = n(r)(c) = v
- }
- val NB = List(
- S(-1, -1), S(-1, 0), S(-1, 1),
- S( 0, -1), S( 0, 1),
- S( 1, -1), S( 1, 0), S( 1, 1)
- )
- val coords = for (r <- 0 to R - 1; c <- 0 to C - 1) yield (r, c)
- // direct 8 neighbours
- def seen1(cur: S) = NB.map(_ + cur).filter(_.ok).filter(_.occupied).size
- // closest 8 direction neighbours
- def seen2(cur : S) = {
- var seen : List[S] = Nil
- NB.foreach{ dir =>
- var c = cur + dir
- while(c.ok && c.floor){
- c += dir
- }
- if(c.ok) seen = c :: seen
- }
- seen.filter(_.occupied).size
- }
- def evolve(fSeen : S => Int, minSeen : Int) = {
- var stable = true
- val N = M.map(_.clone)
- val updates = coords.par.map { case (r,c) =>
- val cur = S(r,c)
- val nSeen = fSeen(cur)
- if(cur.empty && nSeen == 0) {
- cur.update('#', N)
- stable = false
- }
- if(cur.occupied && nSeen >= minSeen) {
- cur.update('L', N)
- stable = false
- }
- }
- M = N
- stable
- }
- def findStable(fSeen : S => Int, minSeen : Int): Int = {
- M = O
- var stable = evolve(fSeen, minSeen)
- while(!stable){ stable = evolve(fSeen, minSeen) }
- M.flatten.filter(_ == '#').size
- }
- def solveAll() = {
- println(findStable(seen1, 4))
- println(findStable(seen2, 5))
- }
- }
- Solution.solveAll()