diff --git a/src/main/kotlin/no/rodland/advent/Dir.kt b/src/main/kotlin/no/rodland/advent/Dir.kt new file mode 100644 index 00000000..58d4db56 --- /dev/null +++ b/src/main/kotlin/no/rodland/advent/Dir.kt @@ -0,0 +1,12 @@ +package no.rodland.advent + +enum class Dir(val move: (input: Pos, howMuch: Int) -> Pos) { + N({ pos, howMuch -> pos.above(howMuch) }), + S({ pos, howMuch -> pos.below(howMuch) }), + W({ pos, howMuch -> pos.left(howMuch) }), + E({ pos, howMuch -> pos.right(howMuch) }), + NW({ pos, howMuch -> pos.nw(howMuch) }), + NE({ pos, howMuch -> pos.ne(howMuch) }), + SW({ pos, howMuch -> pos.sw(howMuch) }), + SE({ pos, howMuch -> pos.se(howMuch) }), +} \ No newline at end of file diff --git a/src/main/kotlin/no/rodland/advent/SpacePos.kt b/src/main/kotlin/no/rodland/advent/SpacePos.kt index e562966e..195e2a32 100644 --- a/src/main/kotlin/no/rodland/advent/SpacePos.kt +++ b/src/main/kotlin/no/rodland/advent/SpacePos.kt @@ -173,10 +173,10 @@ data class Pos(val x: Int, val y: Int) : SpacePos(), Comparable { fun below(howMuch: Int = 1): Pos = Pos(x, y + howMuch) fun left(howMuch: Int = 1): Pos = Pos(x - howMuch, y) fun right(howMuch: Int = 1): Pos = Pos(x + howMuch, y) - private fun nw(): Pos = Pos(x - 1, y - 1) - fun ne(): Pos = Pos(x + 1, y - 1) - fun sw(): Pos = Pos(x - 1, y + 1) - fun se(): Pos = Pos(x + 1, y + 1) + fun nw(howMuch: Int = 1): Pos = Pos(x - howMuch, y - howMuch) + fun ne(howMuch: Int = 1): Pos = Pos(x + howMuch, y - howMuch) + fun sw(howMuch: Int = 1): Pos = Pos(x - howMuch, y + howMuch) + fun se(howMuch: Int = 1): Pos = Pos(x + howMuch, y + howMuch) private fun Any?.size(): Int { return when (this) { diff --git a/src/main/kotlin/no/rodland/advent_2024/Day04.kt b/src/main/kotlin/no/rodland/advent_2024/Day04.kt new file mode 100644 index 00000000..1953adbd --- /dev/null +++ b/src/main/kotlin/no/rodland/advent_2024/Day04.kt @@ -0,0 +1,52 @@ +package no.rodland.advent_2024 + +import no.rodland.advent.Day +import no.rodland.advent.Dir +import no.rodland.advent.Pos + +// template generated: 04/12/2024 +// Fredrik Rødland 2024 +typealias Grid = Array + +class Day04(val input: List) : Day { + + private val parsed = input.parse() + + override fun partOne(): Int { + return parsed.mapIndexed { y, row -> + row.mapIndexed { x, _ -> + val start = Pos(x, y) + val words = Dir.entries.map { d -> + (0..3) + .map { num -> d.move(start, num) } + .filter { p -> p.isInGrid(parsed) } + .map { p -> parsed[p.y][p.x] } + .joinToString("") + }.count { it == "XMAS" } + words + }.sum() + }.sum() + } + + override fun partTwo(): Int { + val intRange = 1..(parsed.size - 2) // assuming grid is square (which both example and input is), avoiding both a filter and many comparisons. + return intRange.sumOf { y -> + intRange.map { x -> + val start = Pos(x, y) + listOf(listOf(start.nw(), start, start.se()), listOf(start.sw(), start, start.ne())) + .all { diagonal -> + diagonal.map { p -> parsed[p.y][p.x] }.joinToString("").let { it == "MAS" || it == "SAM" } + } + }.count { it } + } + } + + override fun List.parse(): Grid { + return indices.map { y -> indices.map { x -> this[y][x] }.toCharArray() }.toTypedArray() + } + + override val day = "04".toInt() +} + + + diff --git a/src/main/script/download_aoc_input.sh b/src/main/script/download_aoc_input.sh index 1a0d84a8..e39b2591 100755 --- a/src/main/script/download_aoc_input.sh +++ b/src/main/script/download_aoc_input.sh @@ -26,3 +26,4 @@ tail -10 "$PUZZLE_FILE" echo echo "TEST-FILE: $PUZZLE_FILE_TEST" echo "FILE: $PUZZLE_FILE" +echo "NUM-LINES: " `wc -l $PUZZLE_FILE` diff --git a/src/test/kotlin/no/rodland/advent_2024/Day04Test.kt b/src/test/kotlin/no/rodland/advent_2024/Day04Test.kt new file mode 100644 index 00000000..590fd585 --- /dev/null +++ b/src/test/kotlin/no/rodland/advent_2024/Day04Test.kt @@ -0,0 +1,84 @@ +package no.rodland.advent_2024 + +import no.rodland.advent.* +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import readFile + +// +// run: download_aoc_input.sh to download input +// + +@Suppress("ClassName") +@DisableSlow +internal class Day04Test { + private val data04 = "2024/input_04.txt".readFile() + private val test04 = "2024/input_04_test.txt".readFile() + + private val resultTestOne = 18 + private val resultTestTwo = 9 + private val resultOne = 2344 + private val resultTwo = 1815 + + val test = defaultTestSuiteParseOnInit( + Day04(data04), + Day04(test04), + resultTestOne, + resultOne, + resultTestTwo, + resultTwo, + { Day04(data04) }, + { Day04(test04) }, + numTestPart1 = 1, + numTestPart2 = 1 + ) + + @Nested + inner class Init { + @Test + fun `04,-,example,1`() { + report(AOCTest({ "123".toInt() }, Unit, 123, 5, "04".toInt(), Part.TWO, false, "example")) + } + + @Test + fun `04,-,example,2`() { + report(test.initTest.copy()) + } + + @Test + fun `04,-,test,init`() { + report(test.initTest) + } + + @Test + fun `04,-,live,init`() { + report(test.initLive) + } + } + + @Nested + inner class `Part 1` { + @Test + fun `04,1,test`() { + report(test.testPart1) + } + + @Test + fun `04,1,live,1`() { + report(test.livePart1) + } + } + + @Nested + inner class `Part 2` { + @Test + fun `04,2,test`() { + report(test.testPart2) + } + + @Test + fun `04,2,live,1`() { + report(test.livePart2) + } + } +} diff --git a/src/test/resources/2024/input_04_test.txt b/src/test/resources/2024/input_04_test.txt index e69de29b..c41c5eae 100644 --- a/src/test/resources/2024/input_04_test.txt +++ b/src/test/resources/2024/input_04_test.txt @@ -0,0 +1,10 @@ +MMMSXXMASM +MSAMXMSMSA +AMXSXMAAMM +MSAMASMSMX +XMASAMXAMM +XXAMMXXAMA +SMSMSASXSS +SAXAMASAAA +MAMMMXMMMM +MXMXAXMASX \ No newline at end of file