- import scala.collection.mutable.{Map => MMap}
- import scala.collection.mutable.{Set => MSet}
- case class Field(name:String, a: Int, b:Int, c:Int, d:Int){
- def accept(x: Int) = (x >= a && x <= b) || (x >= c && x <= d)
- }
- object Field {
- def apply(line: String) : Field = {
- val Array(name, values) = line.split(": ")
- val Array(a,b,c,d) = values.split(" or ").map(_.split("-")) .flatten.map(_.toInt)
- Field(name, a, b, c, d)
- }
- }
- val C = scala.io.Source.fromFile("input").getLines.toList
- val fields = C.takeWhile(_.size>0).map(Field.apply)
- val myTicket = C.dropWhile(_.size>0).drop(2).head.split(",").map(_.toInt).toArray
- val nearBy = C.dropWhile(_.size>0).drop(5).map(_.split(",").map(_.toInt)).map(_.toArray)
- // Peeking input, no field value is above 1000, collect up to 1000 that is in some fields.
- val acceptable = (1 to 1000).filter(x => fields.exists(_.accept(x))).toSet
- // Partition not invalid (potential tickets) and invalids
- val (acceptableTickets, invalidTickets) = nearBy.partition(_.forall(acceptable))
- // Sum of all the rubbish, definitely invalid tickets
- val part1 = invalidTickets.flatMap(_.filterNot(acceptable)).sum
- println(part1)
- val cols = nearBy.head.size
- val columnToCandidateNames = (0 until cols).map { column =>
- val columnValues = acceptableTickets.map(_(column))
- // Field names that accept all the values in this column
- val fieldCandidates = fields.filter(f => columnValues.forall(f.accept))
- .map(_.name).toSet
- (column, fieldCandidates)
- }
- // From debugging, I noticed that candidates size are increasing from 1 to cols
- .sortBy(_._2.size)
- // Since it's sorted with least candidates first, easy to eliminate used names.
- // Practice to use fold left some other day
- val usedName = MSet[String]()
- val departures = MSet[Int]()
- columnToCandidateNames.foreach {
- case (column, fieldCandidates) => {
- val currName = (fieldCandidates -- usedName).head
- if(currName.contains("departure"))
- departures.add(column)
- usedName.add(currName)
- }
- }
- val part2 = departures.map(k => myTicket(k)).map(_.toLong).product
- println(part2)