From 4ec9c19f4a1f5eb78f119cf0268d7f95fd7b56c5 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 18 Dec 2018 11:44:28 +0100 Subject: [PATCH] Adding day 13 p1 and day 14 p1 --- .gitignore | 2 +- day13/part1.scala | 197 ++++++++++++++++++++++++++++++++++++++++++++++ day14/part1.scala | 42 ++++++++++ 3 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 day13/part1.scala create mode 100644 day14/part1.scala diff --git a/.gitignore b/.gitignore index 770eab4..8da470a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -input \ No newline at end of file +*input* \ No newline at end of file diff --git a/day13/part1.scala b/day13/part1.scala new file mode 100644 index 0000000..005f15d --- /dev/null +++ b/day13/part1.scala @@ -0,0 +1,197 @@ +import scala.io.StdIn.readLine +import scala.annotation.tailrec + +val ESC = "\u001B" +val RED = s"$ESC[1;31m" +val BLU = s"$ESC[1;34m" +val CLR = s"$ESC[0;37m" + +val CLEAR_SCREEN = s"$ESC[2J" + +case class Pos(x: Int, y: Int) { + def +(that: Pos): Pos = Pos(x + that.x, y + that.y) +} + +object Up extends Pos(0, -1) +object Down extends Pos(0, 1) +object Left extends Pos(-1, 0) +object Right extends Pos(1, 0) + +val directions: Array[Pos] = Array( + Left, + Up, + Right, + Down) + +sealed trait TrackOrientation { + val c: Char + def move(cart: Cart): Cart +} + +trait Straight extends TrackOrientation { + override def move(cart: Cart): Cart = + cart.copy(p = cart.p + cart.o) +} + +case object StraightImpl extends Straight { + override val c: Char = '?' +} + +case object Horizontal extends Straight { + override val c: Char = '-' +} +case object Vertical extends Straight { + override val c: Char = '|' +} + +case object Diag1 extends TrackOrientation { + override val c: Char = '/' + override def move(cart: Cart): Cart = cart.o match { + case Up => TurnRight.move(cart) + case Down => TurnRight.move(cart) + case Left => TurnLeft.move(cart) + case Right => TurnLeft.move(cart) + } +} + +case object Diag2 extends TrackOrientation { + override val c: Char = '\\' + override def move(cart: Cart): Cart = cart.o match { + case Up => TurnLeft.move(cart) + case Down => TurnLeft.move(cart) + case Left => TurnRight.move(cart) + case Right => TurnRight.move(cart) + } +} + +case object TurnLeft extends TrackOrientation { + override val c: Char = '?' + + val changes: Map[Pos, Pos] = Map( + Up -> Left, + Down -> Right, + Left -> Down, + Right -> Up + ) + + override def move(cart: Cart): Cart = { + val newDir = changes(cart.o) + cart.copy(o = newDir, p = cart.p + newDir) + } +} + +case object TurnRight extends TrackOrientation { + override val c: Char = '?' + + val changes: Map[Pos, Pos] = Map( + Up -> Right, + Down -> Left, + Left -> Up, + Right -> Down + ) + + override def move(cart: Cart): Cart = { + val newDir = changes(cart.o) + cart.copy(o = newDir, p = cart.p + newDir) + } +} + +val crossDirections = Array( + TurnLeft, + StraightImpl, + TurnRight) + +case object CX extends TrackOrientation { + override val c: Char = '+' + + override def move(cart: Cart): Cart = + crossDirections(cart.nextCross) + .move(cart.copy(nextCross=(cart.nextCross + 1) % 3)) +} + +sealed trait Entry + +case class Track(p: Pos, o: TrackOrientation) extends Entry + +case class Cart(p: Pos, o: Pos, nextCross: Int = 0) extends Entry with Ordered[Cart] { + def compare(that: Cart): Int = { + val cmpL = p.y - that.p.y + if (cmpL == 0) { + p.x - that.p.x + } else { + cmpL + } + } + + def next(tracks: Map[Pos, TrackOrientation]): Cart = + tracks(p).move(this) + + override def toString: String = "#" +} + +val input = Iterator + .continually(readLine) + .takeWhile(_ != null) + .toList + .zipWithIndex + .flatMap { case (l, y) => l.zipWithIndex.map { case (c, x) => Pos(x, y) -> c } } + .flatMap { + // tracks + case (p, c) if c == '|' => List(Track(p, Vertical)) + case (p, c) if c == '-' => List(Track(p, Horizontal)) + case (p, c) if c == '+' => List(Track(p, CX)) + case (p, c) if c == '\\' => List(Track(p, Diag2)) + case (p, c) if c == '/' => List(Track(p, Diag1)) + case (p, c) if c == 'v' => List(Track(p, Vertical), Cart(p, Down)) + case (p, c) if c == '^' => List(Track(p, Vertical), Cart(p, Up)) + case (p, c) if c == '>' => List(Track(p, Horizontal), Cart(p, Right)) + case (p, c) if c == '<' => List(Track(p, Horizontal), Cart(p, Left)) + case _ => List() + } + +val tracks: Map[Pos, TrackOrientation] = input.flatMap { + case t: Track => Some(t.p -> t.o) + case _ => None +}.toMap + +val initialCarts: List[Cart] = input.filter(_.isInstanceOf[Cart]).map(_.asInstanceOf[Cart]) + +def printGame(tracks: Map[Pos, TrackOrientation], carts: List[Cart], turn: Int): Unit = { + val maxX = tracks.keys.map(_.x).max + val maxY = tracks.keys.map(_.y).max + + // print(CLEAR_SCREEN) + + val lines = (0 to maxY).map { y => + val line = (0 to maxX).map { x => + Pos(x, y) match { + case p if carts.exists(_.p == p) => + carts.filter(_.p == p) match { + case cs if cs.size > 1 => s"${RED}X${CLR}" + case c :: Nil => s"${BLU}$c${CLR}" + } + case p if tracks.isDefinedAt(p) => tracks(p).c.toString + case _ => " " + } + }.mkString("") + "%03d %s".format(turn, line) + } + lines.foreach(println) + // Thread.sleep(200) +} + +@tailrec +def playGame(tracks: Map[Pos, TrackOrientation])(carts: List[Cart], turn: Int): (Int, Pos) = { + val nextCarts = carts.sorted.map(_.next(tracks)) + // printGame(tracks, nextCarts, turn) + + (nextCarts ++ carts).groupBy(_.p).find(_._2.size > 1) match { + case Some((p, _)) => turn -> p + case _ => playGame(tracks)(nextCarts, turn + 1) + } +} + +// printGame(tracks, initialCarts, 1) + +val (crashTurn, crashPos) = playGame(tracks)(initialCarts, 2) +println(crashTurn, crashPos) diff --git a/day14/part1.scala b/day14/part1.scala new file mode 100644 index 0000000..4134b4c --- /dev/null +++ b/day14/part1.scala @@ -0,0 +1,42 @@ +import scala.io.StdIn.readInt + +case class Turn(recipes: Vector[Int], idx1: Int, idx2: Int, turnNb: Int) { + def newScores(a: Int, b: Int): Vector[Int] = + (a + b).toString.map(_.toInt - '0').toVector + + def next: Turn = { + val newRecipes = recipes ++ newScores(recipes(idx1), recipes(idx2)) + val newIdx1 = (idx1 + (newRecipes(idx1) + 1)) % newRecipes.size + val newIdx2 = (idx2 + (newRecipes(idx2) + 1)) % newRecipes.size + Turn(newRecipes, newIdx1, newIdx2, turnNb + 1) + } + + override def toString: String = + recipes.zipWithIndex.map { + case (v, k) => k match { + case _ if k == idx1 => s"($v)" + case _ if k == idx2 => s"[$v]" + case _ => s" $v " + } + }.mkString(" ") + + def computeScoreAt(n: Int): Option[String] = + recipes match { + case _ if recipes.size < n + 10 => None + case _ => Some(recipes.slice(n, n + 10).mkString("")) + } +} + +val input = readInt + +val turn0 = Turn(Vector(3, 7), 0, 1, 0) + +val endTurn = Iterator.iterate(turn0)(_.next) + .filter(_.recipes.length >= input + 11) + .next + +// println(s"Score: ${endTurn.computeScoreAt(5)} - Expected: 0124515891") +// println(s"Score: ${endTurn.computeScoreAt(18)} - Expected: 9251071085") +// println(s"Score: ${endTurn.computeScoreAt(2018)} - Expected: 5941429882") + +println(s"Score: ${endTurn.computeScoreAt(input).get}")