forked from matelaszlo/advent-of-code-scala
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay19.scala
98 lines (79 loc) · 3.61 KB
/
Day19.scala
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
package com.lmat.adventofcode.year2017
import com.lmat.adventofcode.SimpleCommonPuzzle
import com.lmat.adventofcode.year2017.Day19Definitions._
import com.lmat.util.Files.readResource
import scala.annotation.tailrec
object Day19Definitions {
case class Position(row: Int, column: Int)
sealed trait Tile
case object Empty extends Tile
case object VerticalPath extends Tile
case object HorizontalPath extends Tile
case object Crossing extends Tile
case class CheckPoint(name: Char) extends Tile
}
object Day19 extends SimpleCommonPuzzle[Map[Position, Tile], String, Int] {
override def parse(resource: String): Map[Position, Tile] = parseMap(readResource(resource).map(_.toSeq))
def parseMap(input: Seq[Seq[Char]]): Map[Position, Tile] = {
val rowsNum = input.size
val columnsNum = input.map(_.size).max
(for {
row <- 0 until rowsNum
column <- 0 until columnsNum
tile <- parseTile(input.lift(row).flatMap(_.lift(column)).getOrElse(' '))
} yield (Position(row, column), tile)).toMap
}
def parseTile(char: Char): Option[Tile] = char match {
case '|' => Some(VerticalPath)
case '-' => Some(HorizontalPath)
case '+' => Some(Crossing)
case ' ' => None
case c => Some(CheckPoint(c))
}
override def part1(tiles: Map[Position, Tile]): String = {
@tailrec
def checkpoints(current: Position, heading: String, acc: String): String =
(tiles.getOrElse(current, Empty), next(tiles, current, heading)) match {
case (CheckPoint(c), None) => acc + c
case (_, None) => acc
case (CheckPoint(c), Some((nextPos, nextHead))) => checkpoints(nextPos, nextHead, acc + c)
case (_, Some((nextPos, nextHead))) => checkpoints(nextPos, nextHead, acc)
}
checkpoints(findStartPosition(tiles).get, "down", "")
}
def next(tiles: Map[Position, Tile], current: Position, heading: String): Option[(Position, String)] = {
val left = Position(current.row, current.column - 1)
val right = Position(current.row, current.column + 1)
val up = Position(current.row - 1, current.column)
val down = Position(current.row + 1, current.column)
val goLeft = Some((left, "left"))
val goRight = Some((right, "right"))
val goUp = Some((up, "up"))
val goDown = Some((down, "down"))
(tiles.getOrElse(current, Empty), heading) match {
case (Empty, _) => None
case (Crossing, "down") => if (tiles.getOrElse(left, Empty) != Empty) goLeft else goRight
case (Crossing, "up") => if (tiles.getOrElse(left, Empty) != Empty) goLeft else goRight
case (Crossing, "left") => if (tiles.getOrElse(up, Empty) != Empty) goUp else goDown
case (Crossing, "right") => if (tiles.getOrElse(up, Empty) != Empty) goUp else goDown
case (_, "down") => goDown
case (_, "up") => goUp
case (_, "left") => goLeft
case (_, "right") => goRight
case (_, _) => throw new IllegalArgumentException("Wrong state")
}
}
def findStartPosition(tiles: Map[Position, Tile]): Option[Position] = tiles.find {
case (p, VerticalPath) => p.row == 0
case (_, _) => false
}.map(_._1)
override def part2(tiles: Map[Position, Tile]): Int = {
@tailrec
def countSteps(current: Position, heading: String, acc: Int): Int =
next(tiles, current, heading) match {
case None => acc
case Some((nextPos, nextHead)) => countSteps(nextPos, nextHead, acc + 1)
}
countSteps(findStartPosition(tiles).get, "down", 0)
}
}