1. import scala.collection.mutable.{Map => MMap}
  2. import scala.collection.mutable.{Set => MSet}
  3. case class Field(name:String, a: Int, b:Int, c:Int, d:Int){
  4. def accept(x: Int) = (x >= a && x <= b) || (x >= c && x <= d)
  5. }
  6. object Field {
  7. def apply(line: String) : Field = {
  8. val Array(name, values) = line.split(": ")
  9. val Array(a,b,c,d) = values.split(" or ").map(_.split("-")) .flatten.map(_.toInt)
  10. Field(name, a, b, c, d)
  11. }
  12. }
  13. val C = scala.io.Source.fromFile("input").getLines.toList
  14. val fields = C.takeWhile(_.size>0).map(Field.apply)
  15. val myTicket = C.dropWhile(_.size>0).drop(2).head.split(",").map(_.toInt).toArray
  16. val nearBy = C.dropWhile(_.size>0).drop(5).map(_.split(",").map(_.toInt)).map(_.toArray)
  17. // Peeking input, no field value is above 1000, collect up to 1000 that is in some fields.
  18. val acceptable = (1 to 1000).filter(x => fields.exists(_.accept(x))).toSet
  19. // Partition not invalid (potential tickets) and invalids
  20. val (acceptableTickets, invalidTickets) = nearBy.partition(_.forall(acceptable))
  21. // Sum of all the rubbish, definitely invalid tickets
  22. val part1 = invalidTickets.flatMap(_.filterNot(acceptable)).sum
  23. println(part1)
  24. val cols = nearBy.head.size
  25. val columnToCandidateNames = (0 until cols).map { column =>
  26. val columnValues = acceptableTickets.map(_(column))
  27. // Field names that accept all the values in this column
  28. val fieldCandidates = fields.filter(f => columnValues.forall(f.accept))
  29. .map(_.name).toSet
  30. (column, fieldCandidates)
  31. }
  32. // From debugging, I noticed that candidates size are increasing from 1 to cols
  33. .sortBy(_._2.size)
  34. // Since it's sorted with least candidates first, easy to eliminate used names.
  35. // Practice to use fold left some other day
  36. val usedName = MSet[String]()
  37. val departures = MSet[Int]()
  38. columnToCandidateNames.foreach {
  39. case (column, fieldCandidates) => {
  40. val currName = (fieldCandidates -- usedName).head
  41. if(currName.contains("departure"))
  42. departures.add(column)
  43. usedName.add(currName)
  44. }
  45. }
  46. val part2 = departures.map(k => myTicket(k)).map(_.toLong).product
  47. println(part2)