Created
June 11, 2021 13:07
-
-
Save sungkmi/e6947d4e6ffcba65eeaa9842c45dd53c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sungkmi.aoc2020.day24 | |
// x: 3 o'clock, y: 1 o'clock | |
case class D(dx: Int, dy: Int) | |
object D: | |
val reference: D = D(0, 0) | |
extension (d: D) | |
@annotation.targetName("plus") | |
def +(other: D): D = D(d.dx + other.dx, d.dy + other.dy) | |
enum Direction(val d: D): | |
case E extends Direction(D( 1, 0)) | |
case SE extends Direction(D( 1, -1)) | |
case SW extends Direction(D( 0, -1)) | |
case W extends Direction(D(-1, 0)) | |
case NW extends Direction(D(-1, 1)) | |
case NE extends Direction(D( 0, 1)) | |
def parseLine(line: String): D = | |
@annotation.tailrec | |
def loop(xs: List[Char], sum: D): D = xs match | |
case Nil => sum | |
case 'e' :: tail => loop(tail, Direction.E.d + sum) | |
case 's' :: 'e' :: tail => loop(tail, Direction.SE.d + sum) | |
case 's' :: 'w' :: tail => loop(tail, Direction.SW.d + sum) | |
case 'w' :: tail => loop(tail, Direction.W.d + sum) | |
case 'n' :: 'w' :: tail => loop(tail, Direction.NW.d + sum) | |
case 'n' :: 'e' :: tail => loop(tail, Direction.NE.d + sum) | |
case _ => throw new Exception(s"Wrong input: ${xs.mkString}") | |
loop(line.toList, D.reference) | |
def parse(s: String): Seq[D] = s.split("\n").map(parseLine).toSeq | |
def findBlackTiles(s: String): Seq[D] = for | |
(tile, tiles) <- parse(s).groupBy(identity).toSeq | |
size = tiles.size if size % 2 != 0 | |
yield tile | |
def solve1(s: String): Int = findBlackTiles(s).size | |
def step(blackTiles: Set[D]): Set[D] = | |
val blackNeighbors: Seq[D] = for | |
blackTile <- blackTiles.toSeq | |
neighborD <- Direction.values.toSeq | |
yield blackTile + neighborD.d | |
val blackNeighborCountMap: Map[D, Int] = blackNeighbors | |
.groupBy(identity) | |
.view | |
.mapValues(_.size) | |
.toMap | |
.withDefaultValue(0) | |
val becomeWhites = blackTiles.filter{ t => | |
val neighborCount = blackNeighborCountMap(t) | |
neighborCount == 0 || neighborCount > 2 | |
} | |
val becomeBlacks = for | |
(tile, neighborCount) <- blackNeighborCountMap | |
if neighborCount == 2 && !blackTiles.contains(tile) | |
yield tile | |
blackTiles -- becomeWhites ++ becomeBlacks | |
def day(n: Int, initialBlackTiles: Set[D]): Set[D] = | |
(1 to n).foldLeft(initialBlackTiles)((blackTiles, _) => step(blackTiles)) | |
def solve2(s: String): Int = day(100, findBlackTiles(s).toSet).size | |
@main def part1: Unit = | |
val ans = solve1(input) | |
println(ans) | |
@main def part2: Unit = | |
val ans = solve2(input) | |
println(ans) | |
//lazy val input: String = """wswwwwwwwnenewswnewswwswswww |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sungkmi.aoc2020.day24 | |
class Day24Test extends munit.FunSuite { | |
val testInput = """sesenwnenenewseeswwswswwnenewsewsw | |
neeenesenwnwwswnenewnwwsewnenwseswesw | |
seswneswswsenwwnwse | |
nwnwneseeswswnenewneswwnewseswneseene | |
swweswneswnenwsewnwneneseenw | |
eesenwseswswnenwswnwnwsewwnwsene | |
sewnenenenesenwsewnenwwwse | |
wenwwweseeeweswwwnwwe | |
wsweesenenewnwwnwsenewsenwwsesesenwne | |
neeswseenwwswnwswswnw | |
nenwswwsewswnenenewsenwsenwnesesenew | |
enewnwewneswsewnwswenweswnenwsenwsw | |
sweneswneswneneenwnewenewwneswswnese | |
swwesenesewenwneswnwwneseswwne | |
enesenwswwswneneswsenwnewswseenwsese | |
wnwnesenesenenwwnenwsewesewsesesew | |
nenewswnwewswnenesenwnesewesw | |
eneswnwswnwsenenwnwnwwseeswneewsenese | |
neswnwewnwnwseenwseesewsenwsweewe | |
wseweeenwnesenwwwswnew""" | |
test("day24 parseLine") { | |
assertEquals(parseLine("esew"), D(1, -1)) | |
assertEquals(parseLine("nwwswee"), D.reference) | |
} | |
test("day24 parse") { | |
val flipCountMap = parse(testInput) | |
.groupBy(identity) | |
.view | |
.mapValues(_.size) | |
.toSeq | |
.groupBy(_._2) | |
.view | |
.mapValues(_.size) | |
.toMap | |
assertEquals(flipCountMap, Map(1 -> 10, 2 -> 5)) | |
} | |
test("day24 solve1") { | |
assertEquals(solve1(testInput), 10) | |
} | |
test("day24 day") { | |
val initialBlackTiles: Set[D] = findBlackTiles(testInput).toSet | |
assertEquals(day(1, initialBlackTiles).size, 15) | |
assertEquals(day(2, initialBlackTiles).size, 12) | |
assertEquals(day(3, initialBlackTiles).size, 25) | |
assertEquals(day(4, initialBlackTiles).size, 14) | |
assertEquals(day(5, initialBlackTiles).size, 23) | |
assertEquals(day(6, initialBlackTiles).size, 28) | |
assertEquals(day(7, initialBlackTiles).size, 41) | |
assertEquals(day(8, initialBlackTiles).size, 37) | |
assertEquals(day(9, initialBlackTiles).size, 49) | |
assertEquals(day(10, initialBlackTiles).size, 37) | |
assertEquals(day(20, initialBlackTiles).size, 132) | |
assertEquals(day(30, initialBlackTiles).size, 259) | |
assertEquals(day(40, initialBlackTiles).size, 406) | |
assertEquals(day(50, initialBlackTiles).size, 566) | |
assertEquals(day(60, initialBlackTiles).size, 788) | |
assertEquals(day(70, initialBlackTiles).size, 1106) | |
assertEquals(day(80, initialBlackTiles).size, 1373) | |
assertEquals(day(90, initialBlackTiles).size, 1844) | |
assertEquals(day(100, initialBlackTiles).size, 2208) | |
} | |
test("day24 solve2") { | |
assertEquals(solve2(testInput), 2208) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment