├── 2015 ├── day1.kt ├── day10.kt ├── day11.kt ├── day12.kt ├── day13.kt ├── day14.kt ├── day15.kt ├── day16.kt ├── day17.kt ├── day18.kt ├── day19.kt ├── day2.kt ├── day20.kt ├── day21.kt ├── day22.kt ├── day23.kt ├── day24.kt ├── day25.kt ├── day3.kt ├── day4.kt ├── day5.kt ├── day6.kt ├── day7.kt ├── day8.kt └── day9.kt ├── 2016 ├── day1.java ├── day10.java ├── day11.java ├── day12.java ├── day13.java ├── day14.java ├── day15.java ├── day16.java ├── day17.java ├── day18.java ├── day19.java ├── day2.java ├── day20.java ├── day21.java ├── day22.java ├── day23.java ├── day24.java ├── day25.java ├── day3.java ├── day4.java ├── day5.java ├── day6.java ├── day7.java ├── day8.java └── day9.java ├── 2017 ├── day1 │ └── main.go ├── day10 │ └── main.go ├── day11 │ └── main.go ├── day2 │ └── main.go ├── day3 │ └── main.go ├── day4 │ └── main.go ├── day5 │ └── main.go ├── day6 │ └── main.go ├── day7 │ └── main.go ├── day8 │ └── main.go ├── day9 │ └── main.go └── go.mod ├── 2022 ├── .gitignore ├── Package.swift └── Sources │ ├── day1 │ └── main.swift │ ├── day10 │ └── main.swift │ ├── day11 │ └── main.swift │ ├── day12 │ └── main.swift │ ├── day13 │ └── main.swift │ ├── day14 │ └── main.swift │ ├── day15 │ └── main.swift │ ├── day16 │ └── main.swift │ ├── day17 │ └── main.swift │ ├── day18 │ └── main.swift │ ├── day19 │ └── main.swift │ ├── day2 │ └── main.swift │ ├── day20 │ └── main.swift │ ├── day21 │ └── main.swift │ ├── day22 │ └── main.swift │ ├── day23 │ └── main.swift │ ├── day24 │ └── main.swift │ ├── day25 │ └── main.swift │ ├── day3 │ └── main.swift │ ├── day4 │ └── main.swift │ ├── day5 │ └── main.swift │ ├── day6 │ └── main.swift │ ├── day7 │ └── main.swift │ ├── day8 │ └── main.swift │ └── day9 │ └── main.swift ├── 2023 ├── c++ │ ├── .clangd │ ├── day1.cpp │ ├── day10.cpp │ ├── day11.cpp │ ├── day12.cpp │ ├── day13.cpp │ ├── day14.cpp │ ├── day15.cpp │ ├── day16.cpp │ ├── day17.cpp │ ├── day18.cpp │ ├── day19.cpp │ ├── day2.cpp │ ├── day20.cpp │ ├── day21.cpp │ ├── day22.cpp │ ├── day23.cpp │ ├── day24.cpp │ ├── day25.cpp │ ├── day3.cpp │ ├── day4.cpp │ ├── day5.cpp │ ├── day6.cpp │ ├── day7.cpp │ ├── day8.cpp │ └── day9.cpp ├── mojo │ ├── day1.🔥 │ ├── day10.🔥 │ ├── day11.🔥 │ ├── day12.🔥 │ ├── day13.🔥 │ ├── day14.🔥 │ ├── day15.🔥 │ ├── day16.🔥 │ ├── day17.🔥 │ ├── day18.🔥 │ ├── day19.🔥 │ ├── day2.🔥 │ ├── day20.🔥 │ ├── day21.🔥 │ ├── day22.🔥 │ ├── day23.🔥 │ ├── day24.🔥 │ ├── day25.🔥 │ ├── day3.🔥 │ ├── day4.🔥 │ ├── day5.🔥 │ ├── day6.🔥 │ ├── day7.🔥 │ ├── day8.🔥 │ └── day9.🔥 └── rust │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── day1 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day10 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day11 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day12 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day13 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day14 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day15 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day16 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day17 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day18 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day19 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day2 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day20 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day21 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day22 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day23 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day24 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day25 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day3 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day4 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day5 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day6 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day7 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── day8 │ ├── Cargo.toml │ └── src │ │ └── main.rs │ └── day9 │ ├── Cargo.toml │ └── src │ └── main.rs ├── 2024 ├── day1.rb ├── day10.rb ├── day11.rb ├── day12.rb ├── day13.rb ├── day14.rb ├── day15.rb ├── day16.rb ├── day17.rb ├── day18.rb ├── day19.rb ├── day2.rb ├── day20.rb ├── day21.rb ├── day22.rb ├── day23.rb ├── day24.rb ├── day25.rb ├── day3.rb ├── day4.rb ├── day5.rb ├── day6.rb ├── day7.rb ├── day8.rb └── day9.rb ├── .gitignore └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/.vscode 2 | 3 | **/.idea 4 | **/out 5 | 6 | *.out 7 | *.exe 8 | *.txt 9 | -------------------------------------------------------------------------------- /2015/day1.kt: -------------------------------------------------------------------------------- 1 | private fun part1(str: String): Int { 2 | var level = 0 3 | 4 | for (c in str) { 5 | when (c) { 6 | '(' -> ++level 7 | ')' -> --level 8 | } 9 | } 10 | 11 | return level 12 | } 13 | 14 | private fun part2(str: String): Int { 15 | var level = 0 16 | 17 | for ((i, c) in str.withIndex()) { 18 | when (c) { 19 | '(' -> ++level 20 | ')' -> --level 21 | } 22 | 23 | if (level < 0) 24 | return i + 1 25 | } 26 | 27 | return 0 28 | } 29 | 30 | fun main() { 31 | val line = readlnOrNull().orEmpty() 32 | 33 | println(part1(line)) 34 | println(part2(line)) 35 | } 36 | -------------------------------------------------------------------------------- /2015/day10.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | private fun lookAndSaySequence(start: String, nth: Int): String { 5 | var seq = start 6 | 7 | repeat(nth) { 8 | val builder = StringBuilder() 9 | var cnt = 1 10 | 11 | for ((i, c) in seq.withIndex()) { 12 | if (i + 1 < seq.length && seq[i] == seq[i + 1]) { 13 | ++cnt 14 | continue 15 | } 16 | 17 | builder.append(cnt) 18 | builder.append(c) 19 | cnt = 1 20 | } 21 | 22 | seq = builder.toString() 23 | } 24 | 25 | return seq 26 | } 27 | 28 | private fun part1(line: String): Int { 29 | return lookAndSaySequence(line, 40).length 30 | } 31 | 32 | private fun part2(line: String): Int { 33 | return lookAndSaySequence(line, 50).length 34 | } 35 | 36 | fun main() { 37 | val reader = BufferedReader(InputStreamReader(System.`in`)) 38 | val line = reader.readLine() 39 | 40 | println(part1(line)) 41 | println(part2(line)) 42 | } 43 | -------------------------------------------------------------------------------- /2015/day11.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | private fun skipUntilValid(password: String): String { 5 | val match = "[iol]".toRegex().find(password) 6 | 7 | if (match != null) { 8 | val i = match.range.first 9 | 10 | return password.slice(0 until i) + 11 | (password[i].code + 1).toChar() + 12 | "a".repeat(password.length - i - 1) 13 | } 14 | 15 | return password 16 | } 17 | 18 | private fun increment(password: String): String { 19 | val builder = StringBuilder() 20 | 21 | var increment = true 22 | for (i in password.indices.reversed()) { 23 | var cur = password[i] 24 | 25 | if (increment) { 26 | val overflow = cur == 'z' 27 | cur = if (overflow) 'a' else (cur.code + 1).toChar() 28 | increment = overflow 29 | } 30 | 31 | builder.append(cur) 32 | } 33 | 34 | return builder.toString().reversed() 35 | } 36 | 37 | private fun hasIncreasingStraight(password: String): Boolean { 38 | for (i in 0 until password.length - 2) 39 | if (password[i + 2] == password[i + 1] + 1 && password[i + 1] == password[i] + 1) 40 | return true 41 | 42 | return false 43 | } 44 | 45 | private fun validate(password: String): Boolean { 46 | return hasIncreasingStraight(password) && 47 | "[^iol]*".toRegex().matches(password) && 48 | "(.)\\1".toRegex().findAll(password).count() >= 2 49 | } 50 | 51 | private fun part1(line: String): String { 52 | var pw = line 53 | 54 | while (!validate(pw)) 55 | pw = skipUntilValid(increment(pw)) 56 | 57 | return pw 58 | } 59 | 60 | private fun part2(line: String): String { 61 | return part1(increment(part1(line))) 62 | } 63 | 64 | fun main() { 65 | val reader = BufferedReader(InputStreamReader(System.`in`)) 66 | val line = reader.readLine() 67 | 68 | println(part1(line)) 69 | println(part2(line)) 70 | } 71 | -------------------------------------------------------------------------------- /2015/day12.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | import java.util.* 4 | 5 | private fun hasTopLevelRedValue(obj: String): Boolean { 6 | var level = 0 7 | var array = 0 8 | for (si in obj.indices) { 9 | when (obj[si]) { 10 | '{' -> ++level 11 | '}' -> --level 12 | '[' -> ++array 13 | ']' -> --array 14 | } 15 | 16 | if (level == 0 && array == 0 && obj.substring(si).startsWith(":\"red")) 17 | return true 18 | } 19 | 20 | return false 21 | } 22 | 23 | private fun part1(json: String): Int { 24 | return "(-?\\d+)".toRegex().findAll(json).map { it.value.toInt() }.sum() 25 | } 26 | 27 | private fun part2(json: String): Int { 28 | val subs = mutableListOf() 29 | 30 | val s = Stack() 31 | for (i in json.indices) { 32 | when (json[i]) { 33 | '{' -> s.push(i) 34 | '}' -> { 35 | val obj = json.substring(s.pop() + 1, i) 36 | if (hasTopLevelRedValue(obj)) 37 | subs.add(obj) 38 | } 39 | } 40 | } 41 | 42 | subs.sortByDescending { it.length } 43 | 44 | var filtered = json 45 | while (true) { 46 | val (idx, sub) = filtered.findAnyOf(subs) ?: break 47 | 48 | filtered = filtered.slice(0 until idx) + 49 | filtered.slice(idx + sub.length until filtered.length) 50 | } 51 | 52 | return part1(filtered) 53 | } 54 | 55 | fun main() { 56 | val reader = BufferedReader(InputStreamReader(System.`in`)) 57 | val json = reader.readText() 58 | 59 | println(part1(json)) 60 | println(part2(json)) 61 | } 62 | -------------------------------------------------------------------------------- /2015/day13.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | // Heap's algorithm 5 | private fun getPermutations(k: Int, a: Array): List> { 6 | if (k == 0) 7 | return emptyList() 8 | 9 | if (k == 1) 10 | return listOf(a.toList()) 11 | 12 | var out = getPermutations(k - 1, a) 13 | 14 | for (i in 0 until k - 1) { 15 | if (k % 2 == 1) 16 | a[i] = a[k - 1].also { a[k - 1] = a[i] } 17 | else 18 | a[0] = a[k - 1].also { a[k - 1] = a[0] } 19 | out = out + getPermutations(k - 1, a) 20 | } 21 | 22 | return out 23 | } 24 | 25 | private fun buildConnections(lines: List): HashMap> { 26 | val edges = HashMap>() 27 | 28 | for (line in lines) { 29 | val split = line.split(" ") 30 | val n = "\\d+".toRegex().find(line)!!.value.toInt() 31 | val sign = when (line.contains("gain")) { 32 | true -> 1 33 | false -> -1 34 | } 35 | 36 | edges.getOrPut(split.first()) { HashMap() }[split.last().trim('.')] = sign * n 37 | } 38 | 39 | return edges 40 | } 41 | 42 | private fun part1(lines: List): Int { 43 | val connections = buildConnections(lines) 44 | val perms = getPermutations(connections.size, connections.keys.toTypedArray()) 45 | 46 | return perms.maxOf { 47 | it.withIndex().sumOf { (i, cur) -> 48 | val next = it[(i + 1) % it.size] 49 | val prev = when (i) { 50 | 0 -> it.last() 51 | else -> it[i - 1] 52 | } 53 | 54 | (connections[cur]!![prev] ?: 0) + (connections[cur]!![next] ?: 0) 55 | } 56 | } 57 | } 58 | 59 | private fun part2(lines: List): Int { 60 | return part1(lines + "Self 0 Self") 61 | } 62 | 63 | fun main() { 64 | val reader = BufferedReader(InputStreamReader(System.`in`)) 65 | val lines = reader.readLines() 66 | 67 | println(part1(lines)) 68 | println(part2(lines)) 69 | } 70 | -------------------------------------------------------------------------------- /2015/day14.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | import kotlin.math.min 4 | 5 | private const val t = 2503 6 | 7 | private fun distance(reindeer: List, t: Int): Int { 8 | val (d, m, s) = reindeer 9 | val q = t / (m + s) 10 | val r = t % (m + s) 11 | 12 | return q * m * d + min(r, m) * d 13 | } 14 | 15 | private fun part1(reindeer: List>): Int { 16 | return reindeer.maxOf { distance(it, t) } 17 | } 18 | 19 | private fun part2(reindeer: List>): Int { 20 | val leads = (1..t).flatMap { s -> 21 | val distances = reindeer.map { distance(it, s) } 22 | distances.indices.filter { distances[it] == distances.max() } 23 | } 24 | 25 | return reindeer.indices.maxOf { idx -> leads.count { it == idx } } 26 | } 27 | 28 | fun main() { 29 | val reader = BufferedReader(InputStreamReader(System.`in`)) 30 | val reindeer = reader.readLines().map { "\\d+".toRegex().findAll(it).map { res -> res.value.toInt() }.toList() } 31 | 32 | println(part1(reindeer)) 33 | println(part2(reindeer)) 34 | } 35 | -------------------------------------------------------------------------------- /2015/day15.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | import kotlin.math.max 4 | 5 | 6 | private fun calculateHighestScore(ingredients: List>, ignoreCalories: Boolean = true): Int { 7 | var highest = 0 8 | 9 | for (a in 1..100) { 10 | for (b in 1..100 - a) { 11 | for (c in 1..100 - a - b) { 12 | for (d in 1..100 - a - b - c) { 13 | if (a + b + c + d != 100) 14 | continue 15 | 16 | val calories = 17 | a * ingredients[0][4] + b * ingredients[1][4] + c * ingredients[2][4] + d * ingredients[3][4] 18 | 19 | if (!ignoreCalories && calories != 500) 20 | continue 21 | 22 | val properties = listOf( 23 | a * ingredients[0][0] + b * ingredients[1][0] + c * ingredients[2][0] + d * ingredients[3][0], 24 | a * ingredients[0][1] + b * ingredients[1][1] + c * ingredients[2][1] + d * ingredients[3][1], 25 | a * ingredients[0][2] + b * ingredients[1][2] + c * ingredients[2][2] + d * ingredients[3][2], 26 | a * ingredients[0][3] + b * ingredients[1][3] + c * ingredients[2][3] + d * ingredients[3][3] 27 | ) 28 | 29 | highest = max(highest, properties.map { max(0, it) }.reduce(Int::times)) 30 | } 31 | } 32 | } 33 | } 34 | 35 | return highest 36 | } 37 | 38 | private fun part1(ingredients: List>): Int { 39 | return calculateHighestScore(ingredients) 40 | } 41 | 42 | private fun part2(ingredients: List>): Int { 43 | return calculateHighestScore(ingredients, false) 44 | } 45 | 46 | fun main() { 47 | val reader = BufferedReader(InputStreamReader(System.`in`)) 48 | val ingredients = reader.readLines().map { 49 | "(-?\\d+)".toRegex().findAll(it) 50 | .map { res -> res.value.toInt() } 51 | .toList() 52 | } 53 | 54 | println(part1(ingredients)) 55 | println(part2(ingredients)) 56 | } 57 | -------------------------------------------------------------------------------- /2015/day16.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | private val sampleMap = HashMap() 5 | get() { 6 | if (field.isNotEmpty()) 7 | return field 8 | 9 | val samples = "children: 3\n" + "cats: 7\n" + "samoyeds: 2\n" + "pomeranians: 3\n" + 10 | "akitas: 0\n" + "vizslas: 0\n" + "goldfish: 5\n" + "trees: 3\n" + "cars: 2\n" + "perfumes: 1" 11 | 12 | samples.split("\n").associateTo(field) { 13 | val (type, count) = it.split(": ") 14 | Pair(type, count.toInt()) 15 | } 16 | 17 | return field 18 | } 19 | 20 | private fun findAunt(lines: List, condition: (Pair) -> Boolean): Int { 21 | val aunt = lines.find { line -> 22 | val list = line.split("\\d: ".toRegex())[1] 23 | 24 | list.split(", ") 25 | .map { 26 | val (type, count) = it.split(": ") 27 | Pair(type, count.toInt()) 28 | } 29 | .all { condition(it) } 30 | } 31 | 32 | if (aunt == null) 33 | return 0 34 | 35 | return "\\d+".toRegex().find(aunt)!!.value.toInt() 36 | } 37 | 38 | private fun part1(lines: List): Int { 39 | return findAunt(lines) { (type, cnt) -> sampleMap[type] == cnt } 40 | } 41 | 42 | private fun part2(lines: List): Int { 43 | return findAunt(lines) { (type, cnt) -> 44 | when (type) { 45 | "cats", "trees" -> sampleMap[type]!! < cnt 46 | "pomeranians", "goldfish" -> sampleMap[type]!! > cnt 47 | else -> sampleMap[type] == cnt 48 | } 49 | } 50 | } 51 | 52 | fun main() { 53 | val reader = BufferedReader(InputStreamReader(System.`in`)) 54 | val lines = reader.readLines() 55 | 56 | println(part1(lines)) 57 | println(part2(lines)) 58 | } 59 | -------------------------------------------------------------------------------- /2015/day17.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | @Suppress("SameParameterValue") 5 | private fun part1(containers: List, n: Int): Int { 6 | val dp = Array(containers.size + 1) { Array(n + 1) { 0 } } 7 | dp[0][0] = 1 8 | 9 | for (l in 0..n) { 10 | for ((idx, c) in containers.withIndex()) { 11 | dp[idx + 1][l] = dp[idx][l] 12 | 13 | if (l - c >= 0) 14 | dp[idx + 1][l] += dp[idx][l - c] 15 | } 16 | } 17 | 18 | return dp[containers.size][n] 19 | } 20 | 21 | @Suppress("SameParameterValue") 22 | private fun part2(containers: List, n: Int): Int { 23 | val paths = Array(containers.size + 1) { Array(n + 1) { listOf(listOf()) } } 24 | 25 | for (l in 0..n) { 26 | for ((idx, c) in containers.withIndex()) { 27 | paths[idx + 1][l] = paths[idx][l].toList() 28 | 29 | if (l - c >= 0) 30 | paths[idx + 1][l] = paths[idx + 1][l] + paths[idx][l - c].map { it + c } 31 | } 32 | } 33 | 34 | val validPaths = paths[containers.size][n].filter { it.sum() == n } 35 | val shortest = validPaths.minOf { it.size } 36 | 37 | return validPaths.count { it.size == shortest } 38 | } 39 | 40 | fun main() { 41 | val reader = BufferedReader(InputStreamReader(System.`in`)) 42 | 43 | val containers = reader.readLines().map { it.toInt() }.toList() 44 | val n = 150 45 | 46 | println(part1(containers, n)) 47 | println(part2(containers, n)) 48 | } 49 | -------------------------------------------------------------------------------- /2015/day18.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | private fun isCorner(n: Int, r: Int, c: Int): Boolean { 5 | return (r == 0 || r == n - 1) && (c == 0 || c == n - 1) 6 | } 7 | 8 | private fun countAdjacentOn(grid: List>, r: Int, c: Int): Int { 9 | fun inbounds(r: Int, c: Int): Boolean { 10 | return 0 <= r && r < grid.size && 0 <= c && c < grid[0].size 11 | } 12 | 13 | val dr = listOf(-1, -1, -1, 0, 0, 1, 1, 1) 14 | val dc = listOf(-1, 0, 1, -1, 1, -1, 0, 1) 15 | 16 | return dr.zip(dc).count { (vr, vc) -> 17 | val nr = r + vr 18 | val nc = c + vc 19 | 20 | inbounds(nr, nc) && grid[nr][nc] == '#' 21 | } 22 | } 23 | 24 | private fun transform(grid: List>, r: Int, c: Int, fixCorners: Boolean = false): Char { 25 | if (fixCorners && isCorner(grid.size, r, c)) 26 | return '#' 27 | 28 | val adjacentOn = countAdjacentOn(grid, r, c) 29 | 30 | return when (adjacentOn) { 31 | 3 -> '#' 32 | 2 -> grid[r][c] 33 | else -> '.' 34 | } 35 | } 36 | 37 | @Suppress("SameParameterValue") 38 | private fun simulate( 39 | init: List>, 40 | t: Int, 41 | transformFunction: (List>, Int, Int) -> Char 42 | ): List> { 43 | var grid = init 44 | 45 | repeat(t) { 46 | grid = grid.withIndex().map { (ri, row) -> row.indices.map { transformFunction(grid, ri, it) } } 47 | } 48 | 49 | return grid 50 | } 51 | 52 | private fun part1(lines: List): Int { 53 | val grid = lines.map { it.toList() } 54 | 55 | return simulate(grid, 100, ::transform).sumOf { it.count { c -> c == '#' } } 56 | } 57 | 58 | private fun part2(lines: List): Int { 59 | val grid = lines.withIndex().map { (ri, row) -> 60 | row.withIndex().map { (ci, c) -> 61 | if (isCorner(lines.size, ri, ci)) { 62 | '#' 63 | } else { 64 | c 65 | } 66 | } 67 | } 68 | 69 | val simulated = simulate(grid, 100) { g, r, c -> transform(g, r, c, true) } 70 | 71 | return simulated.sumOf { it.count { c -> c == '#' } } 72 | } 73 | 74 | fun main() { 75 | val reader = BufferedReader(InputStreamReader(System.`in`)) 76 | val lines = reader.readLines() 77 | 78 | println(part1(lines)) 79 | println(part2(lines)) 80 | } 81 | -------------------------------------------------------------------------------- /2015/day19.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | import java.util.* 4 | 5 | private fun replacePatternAt(str: String, idx: Int, pattern: String, replacement: String): String { 6 | val sub = str.substring(idx) 7 | if (!sub.startsWith(pattern)) 8 | return str 9 | 10 | return str.substring(0 until idx) + str.substring(idx).replaceFirst(pattern, replacement) 11 | } 12 | 13 | private fun part1(rules: List>, molecule: String): Int { 14 | val uniques = molecule.indices.flatMap { idx -> 15 | rules.map { (p, r) -> replacePatternAt(molecule, idx, p, r) } 16 | }.toHashSet() 17 | 18 | uniques.remove(molecule) 19 | 20 | return uniques.size 21 | } 22 | 23 | private fun part2(rules: List>, molecule: String): Int { 24 | val q = PriorityQueue> { lhs, rhs -> 25 | lhs.first.length - rhs.first.length 26 | } 27 | q.add(Pair(molecule, 0)) 28 | 29 | while (q.isNotEmpty()) { 30 | val (cur, steps) = q.remove() 31 | 32 | if (cur == "e") 33 | return steps 34 | 35 | for (i in cur.indices) { 36 | for ((replacement, pattern) in rules) { 37 | val replaced = replacePatternAt(cur, i, pattern, replacement) 38 | 39 | if (replaced != cur) { 40 | q.add(Pair(replaced, steps + 1)) 41 | } 42 | } 43 | } 44 | } 45 | 46 | return 0 47 | } 48 | 49 | fun main() { 50 | val reader = BufferedReader(InputStreamReader(System.`in`)) 51 | val lines = reader.readLines().filter { it.isNotEmpty() } 52 | 53 | val rules = lines.take(lines.size - 1).map { it.split(" => ").toList() } 54 | val molecule = lines.last() 55 | 56 | println(part1(rules, molecule)) 57 | println(part2(rules, molecule)) 58 | } 59 | -------------------------------------------------------------------------------- /2015/day2.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | private fun part1(lines: List): Int { 5 | var total = 0 6 | for (line in lines) { 7 | val (l, w, h) = line.split("x").map { it.toInt() }.toList() 8 | val dims = intArrayOf(l * w, w * h, h * l) 9 | 10 | total += 2 * dims.sum() + dims.min() 11 | } 12 | 13 | return total 14 | } 15 | 16 | private fun part2(lines: List): Int { 17 | var total = 0 18 | for (line in lines) { 19 | val sides = line.split("x").map { it.toInt() }.toList().sorted() 20 | 21 | total += 2 * sides[0] + 2 * sides[1] + sides.fold(1) { acc, i -> acc * i } 22 | } 23 | 24 | return total 25 | } 26 | 27 | fun main() { 28 | val reader = BufferedReader(InputStreamReader(System.`in`)) 29 | val lines = reader.readLines() 30 | 31 | println(part1(lines)) 32 | println(part2(lines)) 33 | } 34 | -------------------------------------------------------------------------------- /2015/day20.kt: -------------------------------------------------------------------------------- 1 | import kotlin.math.sqrt 2 | 3 | private fun getDivisors(n: Int): List { 4 | val divisors = mutableListOf() 5 | 6 | for (d in 1..sqrt(n.toDouble()).toInt()) { 7 | if (n % d == 0) { 8 | divisors.add(d) 9 | 10 | if (d != n / d) 11 | divisors.add(n / d) 12 | } 13 | } 14 | 15 | return divisors 16 | } 17 | 18 | private fun part1(target: Int): Int { 19 | return (1..target).find { getDivisors(it).sum() * 10 > target } ?: 0 20 | } 21 | 22 | private fun part2(target: Int): Int { 23 | return (1..target).find { h -> getDivisors(h).filter { h / it <= 50 }.sum() * 11 > target } ?: 0 24 | } 25 | 26 | fun main() { 27 | val target = readln().toInt() 28 | 29 | println(part1(target)) 30 | println(part2(target)) 31 | } 32 | -------------------------------------------------------------------------------- /2015/day21.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | import kotlin.math.max 4 | 5 | private fun getItemCombinations(): List> { 6 | val swords = listOf( 7 | listOf(8, 4, 0), 8 | listOf(10, 5, 0), 9 | listOf(25, 6, 0), 10 | listOf(40, 7, 0), 11 | listOf(74, 8, 0) 12 | ) 13 | 14 | val armor = listOf( 15 | listOf(0, 0, 0), 16 | listOf(13, 0, 1), 17 | listOf(31, 0, 2), 18 | listOf(53, 0, 3), 19 | listOf(75, 0, 4), 20 | listOf(102, 0, 5) 21 | ) 22 | 23 | val rings = listOf( 24 | listOf(0, 0, 0), 25 | listOf(0, 0, 0), 26 | listOf(25, 1, 0), 27 | listOf(50, 2, 0), 28 | listOf(100, 3, 0), 29 | listOf(20, 0, 1), 30 | listOf(40, 0, 2), 31 | listOf(80, 0, 3) 32 | ) 33 | 34 | val combinations = mutableListOf>() 35 | 36 | for (s in swords) 37 | for (a in armor) 38 | for (r1 in rings) 39 | for (r2 in rings) { 40 | if (r1 == r2) 41 | continue 42 | 43 | val cost = s[0] + a[0] + r1[0] + r2[0] 44 | val atk = s[1] + a[1] + r1[1] + r2[1] 45 | val arm = s[2] + a[2] + r1[2] + r2[2] 46 | 47 | combinations.add(listOf(cost, atk, arm)) 48 | } 49 | 50 | return combinations 51 | } 52 | 53 | private fun simulate(boss: List, gear: List): Pair { 54 | var hp = 100 55 | val (_, atk, arm) = gear 56 | var (bHp, bAtk, bArm) = boss 57 | 58 | while (hp > 0 && bHp > 0) { 59 | bHp -= max(1, atk - bArm) 60 | hp -= max(1, bAtk - arm) 61 | } 62 | 63 | return Pair(hp, bHp) 64 | } 65 | 66 | private fun part1(boss: List): Int { 67 | return getItemCombinations().filter { simulate(boss, it).second <= 0 }.minBy { it[0] }[0] 68 | } 69 | 70 | private fun part2(boss: List): Int { 71 | return getItemCombinations().filter { 72 | val (hp, bHp) = simulate(boss, it) 73 | bHp > 0 && hp <= 0 74 | }.maxBy { it[0] }[0] 75 | } 76 | 77 | fun main() { 78 | val reader = BufferedReader(InputStreamReader(System.`in`)) 79 | val boss = reader.readLines().map { "\\d+".toRegex().find(it)!!.value.toInt() } 80 | 81 | println(part1(boss)) 82 | println(part2(boss)) 83 | } 84 | -------------------------------------------------------------------------------- /2015/day23.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | private data class Instr( 5 | val kind: String, 6 | val o1: String, 7 | val o2: String? 8 | ) 9 | 10 | private fun interpret(registers: List, instructions: List): List { 11 | val regs = registers.toTypedArray() 12 | var ip = 0 13 | 14 | while (ip < instructions.size) { 15 | val (kind, o1, o2) = instructions[ip] 16 | 17 | ip += when (kind) { 18 | "hlf" -> { 19 | regs[(o1[0] - 'a')] /= 2 20 | 1 21 | } 22 | 23 | "tpl" -> { 24 | regs[(o1[0] - 'a')] *= 3 25 | 1 26 | } 27 | 28 | "inc" -> { 29 | ++regs[(o1[0] - 'a')] 30 | 1 31 | } 32 | 33 | "jmp" -> { 34 | o1.toInt() 35 | } 36 | 37 | "jie" -> { 38 | if (regs[(o1[0] - 'a')] % 2 == 0) o2!!.toInt() else 1 39 | } 40 | 41 | "jio" -> { 42 | if (regs[(o1[0] - 'a')] == 1) o2!!.toInt() else 1 43 | } 44 | 45 | else -> throw UnsupportedOperationException("invalid instruction") 46 | } 47 | } 48 | 49 | return regs.toList() 50 | } 51 | 52 | private fun part1(instructions: List): Int { 53 | return interpret(listOf(0, 0), instructions)[1] 54 | } 55 | 56 | private fun part2(instructions: List): Int { 57 | return interpret(listOf(1, 0), instructions)[1] 58 | } 59 | 60 | fun main() { 61 | val reader = BufferedReader(InputStreamReader(System.`in`)) 62 | val instructions = reader.readLines().map { 63 | val split = it.split(" |(, )".toRegex()) 64 | Instr(split[0], split[1], split.getOrNull(2)) 65 | } 66 | 67 | println(part1(instructions)) 68 | println(part2(instructions)) 69 | } 70 | -------------------------------------------------------------------------------- /2015/day24.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | private fun getNSumPackages(set: List, n: Long): List> { 5 | val subsets = mutableListOf>() 6 | 7 | for (i in 0 until (1 shl set.size)) { 8 | val subset = mutableListOf() 9 | 10 | for (j in set.indices) { 11 | if (i and (1 shl j) != 0) 12 | subset.add(set[j]) 13 | } 14 | 15 | if (subset.sum() == n) 16 | subsets.add(subset) 17 | } 18 | 19 | subsets.sortBy { it.reduce(Long::times) } 20 | subsets.sortBy { it.size } 21 | 22 | return subsets 23 | } 24 | 25 | private fun part1(weights: List): Long { 26 | val subsets = getNSumPackages(weights, weights.sum() / 3) 27 | 28 | for (s in subsets) { 29 | for (s1 in subsets) { 30 | val union = s.union(s1.toSet()) 31 | if (union.size != s.size + s1.size) 32 | continue 33 | 34 | return s.reduce(Long::times) 35 | } 36 | } 37 | 38 | return 0 39 | } 40 | 41 | private fun part2(weights: List): Long { 42 | val subsets = getNSumPackages(weights, weights.sum() / 4) 43 | 44 | for (s in subsets) { 45 | for (s1 in subsets) { 46 | var union = s.union(s1.toSet()) 47 | if (union.size != s.size + s1.size) 48 | continue 49 | 50 | for (s2 in subsets) { 51 | union = union.union(s2.toSet()) 52 | if (union.size != s.size + s1.size + s2.size) 53 | continue 54 | 55 | return s.reduce(Long::times) 56 | } 57 | } 58 | } 59 | 60 | return 0 61 | } 62 | 63 | fun main() { 64 | val reader = BufferedReader(InputStreamReader(System.`in`)) 65 | val weights = reader.readLines().map { it.toLong() } 66 | 67 | println(part1(weights)) 68 | println(part2(weights)) 69 | } 70 | -------------------------------------------------------------------------------- /2015/day25.kt: -------------------------------------------------------------------------------- 1 | private fun part1(line: String): ULong { 2 | val (r, c) = "\\d+".toRegex().findAll(line).map { it.value.toInt() }.toList() 3 | 4 | val sum1ToN = { n: Int -> (n * (n + 1)) / 2 } 5 | val base = sum1ToN(c) + sum1ToN(c + r - 2) - sum1ToN(c - 1) 6 | 7 | var code = 20151125uL 8 | for (n in 2..base) 9 | code = (code * 252533uL) % 33554393uL 10 | 11 | return code 12 | } 13 | 14 | private fun part2(): Int { 15 | return 0 16 | } 17 | 18 | fun main() { 19 | val line = readln() 20 | 21 | println(part1(line)) 22 | println(part2()) 23 | } 24 | -------------------------------------------------------------------------------- /2015/day3.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | private fun part1(line: String): Int { 5 | var pos = Pair(0, 0) 6 | 7 | val visited = HashSet>() 8 | visited.add(pos) 9 | 10 | for (dir in line) { 11 | var (x, y) = pos 12 | 13 | when (dir) { 14 | '>' -> ++x 15 | '<' -> --x 16 | 'v' -> ++y 17 | '^' -> --y 18 | } 19 | 20 | pos = Pair(x, y) 21 | visited.add(pos) 22 | } 23 | 24 | return visited.size 25 | } 26 | 27 | private fun part2(line: String): Int { 28 | val pos = mutableListOf(Pair(0, 0), Pair(0, 0)) 29 | var i = 0 30 | 31 | val visited = HashSet>() 32 | visited.add(pos[i]) 33 | 34 | for (dir in line) { 35 | var (x, y) = pos[i] 36 | 37 | when (dir) { 38 | '>' -> ++x 39 | '<' -> --x 40 | 'v' -> ++y 41 | '^' -> --y 42 | } 43 | 44 | pos[i] = Pair(x, y) 45 | visited.add(pos[i]) 46 | i = (i + 1) % pos.size 47 | } 48 | 49 | return visited.size 50 | } 51 | 52 | fun main() { 53 | val reader = BufferedReader(InputStreamReader(System.`in`)) 54 | val line = reader.readLine() 55 | 56 | println(part1(line)) 57 | println(part2(line)) 58 | } 59 | -------------------------------------------------------------------------------- /2015/day4.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | import java.security.MessageDigest 4 | 5 | private fun part1(line: String): Int { 6 | val md = MessageDigest.getInstance("MD5") 7 | 8 | var n = 0 9 | 10 | while (true) { 11 | val base = line + n.toString() 12 | val hash = md.digest(base.toByteArray()).joinToString("") { "%02x".format(it) } 13 | 14 | if (hash.slice(0..4).all { it == '0' }) 15 | break 16 | 17 | ++n 18 | } 19 | 20 | return n 21 | } 22 | 23 | private fun part2(line: String): Int { 24 | val md = MessageDigest.getInstance("MD5") 25 | 26 | var n = 0 27 | 28 | while (true) { 29 | val base = line + n.toString() 30 | val hash = md.digest(base.toByteArray()).joinToString("") { "%02x".format(it) } 31 | 32 | if (hash.slice(0..5).all { it == '0' }) 33 | break 34 | 35 | ++n 36 | } 37 | 38 | return n 39 | } 40 | 41 | fun main() { 42 | val reader = BufferedReader(InputStreamReader(System.`in`)) 43 | val line = reader.readLine() 44 | 45 | println(part1(line)) 46 | println(part2(line)) 47 | } 48 | -------------------------------------------------------------------------------- /2015/day5.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | private fun part1(lines: List): Int { 5 | return lines.count { 6 | val includedCount = "[aeiou]".toRegex().findAll(it).count() 7 | val hasExcluded = ".*(ab)|(cd)|(xy)|(pq).*".toRegex().find(it) != null 8 | val hasDouble = "(.)\\1".toRegex().find(it) != null 9 | 10 | includedCount >= 3 && !hasExcluded && hasDouble 11 | } 12 | } 13 | 14 | private fun part2(lines: List): Int { 15 | return lines.count { "(..).*(\\1)".toRegex().find(it) != null && "(.).(\\1)".toRegex().find(it) != null } 16 | } 17 | 18 | fun main() { 19 | val reader = BufferedReader(InputStreamReader(System.`in`)) 20 | val lines = reader.readLines() 21 | 22 | println(part1(lines)) 23 | println(part2(lines)) 24 | } 25 | -------------------------------------------------------------------------------- /2015/day6.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | import kotlin.math.max 4 | 5 | private fun part1(lines: List): Int { 6 | val grid = MutableList(1000) { MutableList(1000) { false } } 7 | 8 | for (line in lines) { 9 | val instruction = "[a-z]+(\\s[a-z]+)?".toRegex().find(line)?.value 10 | 11 | val (fr, fc, tr, tc) = "\\d+,\\d+".toRegex().findAll(line).flatMap { res -> 12 | res.value.split(",").map { 13 | it.toInt() 14 | }.toList() 15 | }.toList() 16 | 17 | for (r in fr..tr) { 18 | for (c in fc..tc) { 19 | grid[r][c] = when (instruction) { 20 | "toggle" -> !grid[r][c] 21 | "turn on" -> true 22 | "turn off" -> false 23 | else -> false 24 | } 25 | } 26 | } 27 | } 28 | 29 | return grid.sumOf { r -> r.count { it } } 30 | } 31 | 32 | private fun part2(lines: List): Int { 33 | val grid = MutableList(1000) { MutableList(1000) { 0 } } 34 | 35 | for (line in lines) { 36 | val instruction = "[a-z]+(\\s[a-z]+)?".toRegex().find(line)?.value 37 | 38 | val (fr, fc, tr, tc) = "\\d+,\\d+".toRegex().findAll(line).flatMap { res -> 39 | res.value.split(",").map { 40 | it.toInt() 41 | }.toList() 42 | }.toList() 43 | 44 | for (r in fr..tr) { 45 | for (c in fc..tc) { 46 | grid[r][c] += when (instruction) { 47 | "toggle" -> 2 48 | "turn on" -> 1 49 | "turn off" -> -1 50 | else -> 0 51 | } 52 | 53 | grid[r][c] = max(grid[r][c], 0) 54 | } 55 | } 56 | } 57 | 58 | return grid.sumOf { it.sum() } 59 | } 60 | 61 | fun main() { 62 | val reader = BufferedReader(InputStreamReader(System.`in`)) 63 | val lines = reader.readLines() 64 | 65 | println(part1(lines)) 66 | println(part2(lines)) 67 | } 68 | -------------------------------------------------------------------------------- /2015/day8.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | 5 | private fun part1(lines: List): Int { 6 | return lines.sumOf { it.length } - lines.sumOf { 7 | val str = it.removeSurrounding("\"") 8 | var len = str.length 9 | 10 | len -= "(\\\\\")|(\\\\\\\\)".toRegex().findAll(str).count() 11 | len -= 3 * "(\\\\x[0-9a-f][0-9a-f])".toRegex().findAll(str).count() 12 | 13 | len 14 | } 15 | } 16 | 17 | private fun part2(lines: List): Int { 18 | return lines.sumOf { it.length + "[\\\\\"]".toRegex().findAll(it).count() + 2 } - lines.sumOf { it.length } 19 | } 20 | 21 | fun main() { 22 | val reader = BufferedReader(InputStreamReader(System.`in`)) 23 | val lines = reader.readLines() 24 | 25 | println(part1(lines)) 26 | println(part2(lines)) 27 | } 28 | -------------------------------------------------------------------------------- /2015/day9.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | import java.io.InputStreamReader 3 | 4 | private fun dfs( 5 | cur: String, 6 | visited: HashSet, 7 | graph: HashMap>>, 8 | shortest: Boolean = true, 9 | cost: Int = 0 10 | ): Int { 11 | if (!visited.add(cur)) 12 | return if (shortest) Int.MAX_VALUE else 0 13 | 14 | if (visited.size == graph.keys.size) 15 | return cost 16 | 17 | return if (shortest) 18 | graph[cur]!!.minOf { dfs(it.first, visited.toHashSet(), graph, true, cost + it.second) } 19 | else 20 | graph[cur]!!.maxOf { dfs(it.first, visited.toHashSet(), graph, false, cost + it.second) } 21 | } 22 | 23 | private fun part1(graph: HashMap>>): Int { 24 | return graph.keys.minOf { dfs(it, HashSet(), graph) } 25 | } 26 | 27 | private fun part2(graph: HashMap>>): Int { 28 | return graph.keys.maxOf { dfs(it, HashSet(), graph, false) } 29 | } 30 | 31 | fun main() { 32 | val reader = BufferedReader(InputStreamReader(System.`in`)) 33 | 34 | val graph = HashMap>>() 35 | for (line in reader.lines()) { 36 | val (cities, cost) = line.split(" = ") 37 | val (from, to) = cities.split(" to ") 38 | 39 | graph.getOrPut(from) { HashSet() }.add(Pair(to, cost.toInt())) 40 | graph.getOrPut(to) { HashSet() }.add(Pair(from, cost.toInt())) 41 | } 42 | 43 | println(part1(graph)) 44 | println(part2(graph)) 45 | } 46 | -------------------------------------------------------------------------------- /2016/day1.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.IOException; 3 | import java.io.InputStreamReader; 4 | import java.util.ArrayList; 5 | import java.util.HashSet; 6 | 7 | class day1 { 8 | private record Pair(K first, V second) { 9 | } 10 | 11 | private static int rotate(boolean left, int facing) { 12 | if (left) return facing == 0 ? 3 : facing - 1; 13 | else return (facing + 1) % 4; 14 | } 15 | 16 | private static ArrayList> move(Pair pos, int facing, int steps) { 17 | ArrayList> moves = new ArrayList<>(); 18 | 19 | for (int i = 0; i < steps; i++) { 20 | moves.add(switch (facing) { 21 | case 0 -> new Pair<>(pos.first + 1, pos.second); 22 | case 1 -> new Pair<>(pos.first, pos.second + 1); 23 | case 2 -> new Pair<>(pos.first - 1, pos.second); 24 | case 3 -> new Pair<>(pos.first, pos.second - 1); 25 | default -> throw new UnknownError(); 26 | }); 27 | 28 | pos = moves.getLast(); 29 | } 30 | 31 | return moves; 32 | } 33 | 34 | private static int part1(String input) { 35 | // 0 - N; 1 - E; 2 - S; 3 - W 36 | int facing = 0; 37 | Pair pos = new Pair<>(0, 0); 38 | 39 | for (String dir : input.split(", ")) { 40 | boolean left = dir.charAt(0) == 'L'; 41 | int steps = Integer.parseInt(dir.substring(1)); 42 | 43 | facing = rotate(left, facing); 44 | pos = move(pos, facing, steps).getLast(); 45 | } 46 | 47 | return Math.abs(pos.first) + Math.abs(pos.second); 48 | } 49 | 50 | private static int part2(String input) { 51 | int facing = 0; 52 | Pair pos = new Pair<>(0, 0); 53 | 54 | HashSet> visited = new HashSet<>(); 55 | visited.add(pos); 56 | 57 | for (String dir : input.split(", ")) { 58 | boolean left = dir.charAt(0) == 'L'; 59 | int steps = Integer.parseInt(dir.substring(1)); 60 | 61 | facing = rotate(left, facing); 62 | var visitedPositions = move(pos, facing, steps); 63 | 64 | for (var visitedPos : visitedPositions) { 65 | if (!visited.add(visitedPos)) return Math.abs(visitedPos.first) + Math.abs(visitedPos.second); 66 | } 67 | 68 | pos = visitedPositions.getLast(); 69 | } 70 | 71 | throw new UnknownError(); 72 | } 73 | 74 | public static void main(String[] args) throws IOException { 75 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 76 | 77 | String line = reader.readLine(); 78 | System.out.println(part1(line)); 79 | System.out.println(part2(line)); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /2016/day10.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.TreeSet; 7 | 8 | class day10 { 9 | private static boolean process(HashMap> bots, int val, String dst, String dstId) { 10 | int to = Integer.parseInt(dstId); 11 | 12 | if (!dst.equals("output")) { 13 | bots.putIfAbsent(to, new TreeSet<>()); 14 | bots.get(to).add(val); 15 | return true; 16 | } 17 | 18 | return to > 2; 19 | } 20 | 21 | private static int[] simulate(List lines) { 22 | ArrayList linesToProcess = new ArrayList<>(lines); 23 | 24 | HashMap> bots = new HashMap<>(); 25 | int[] out = {-1, 1}; 26 | 27 | while (!linesToProcess.isEmpty()) { 28 | String line = linesToProcess.removeFirst(); 29 | String[] split = line.split(" "); 30 | 31 | if (split[0].equals("value")) { 32 | int val = Integer.parseInt(split[1]); 33 | int bot = Integer.parseInt(split[split.length - 1]); 34 | 35 | bots.putIfAbsent(bot, new TreeSet<>()); 36 | bots.get(bot).add(val); 37 | continue; 38 | } 39 | 40 | int from = Integer.parseInt(split[1]); 41 | if (!bots.containsKey(from) || bots.get(from).size() != 2) { 42 | linesToProcess.add(line); 43 | continue; 44 | } 45 | 46 | int low = bots.get(from).removeFirst(); 47 | int high = bots.get(from).removeFirst(); 48 | 49 | if (low == 17 && high == 61) out[0] = from; 50 | if (!process(bots, low, split[5], split[6])) out[1] *= low; 51 | if (!process(bots, high, split[10], split[11])) out[1] *= high; 52 | } 53 | 54 | return out; 55 | } 56 | 57 | public static void main(String[] args) { 58 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 59 | var res = simulate(reader.lines().toList()); 60 | 61 | System.out.println(res[0]); 62 | System.out.println(res[1]); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /2016/day12.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | 4 | class day12 { 5 | private static int getOpVal(int[] r, String[] instr, int n) { 6 | String op = instr[n]; 7 | 8 | char fst = op.charAt(0); 9 | if ('a' <= fst && fst <= 'd') return r[fst - 'a']; 10 | 11 | return Integer.parseInt(op); 12 | } 13 | 14 | private static int getRIdx(String[] instr, int n) { 15 | return instr[n].charAt(0) - 'a'; 16 | } 17 | 18 | private static void execute(int[] r, String[] instructions) { 19 | int rip = 0; 20 | while (rip < instructions.length) { 21 | String[] instr = instructions[rip].split(" "); 22 | String opcode = instr[0]; 23 | 24 | switch (opcode) { 25 | case "inc": 26 | ++r[getRIdx(instr, 1)]; 27 | break; 28 | case "dec": 29 | --r[getRIdx(instr, 1)]; 30 | break; 31 | case "cpy": 32 | r[getRIdx(instr, 2)] = getOpVal(r, instr, 1); 33 | break; 34 | case "jnz": 35 | if (getOpVal(r, instr, 1) != 0) { 36 | rip += getOpVal(r, instr, 2); 37 | continue; 38 | } 39 | } 40 | 41 | ++rip; 42 | } 43 | } 44 | 45 | private static int part1(String[] instructions) { 46 | int[] r = new int[4]; 47 | execute(r, instructions); 48 | return r[0]; 49 | } 50 | 51 | private static int part2(String[] instructions) { 52 | int[] r = new int[4]; 53 | r[2] = 1; 54 | 55 | execute(r, instructions); 56 | return r[0]; 57 | } 58 | 59 | public static void main(String[] args) { 60 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 61 | String[] instruction = reader.lines().toArray(String[]::new); 62 | 63 | System.out.println(part1(instruction)); 64 | System.out.println(part2(instruction)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /2016/day13.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.IOException; 3 | import java.io.InputStreamReader; 4 | import java.util.ArrayDeque; 5 | import java.util.HashSet; 6 | 7 | class day13 { 8 | private static boolean isWall(int x, int y, int n) { 9 | return Integer.bitCount(x * x + 3 * x + 2 * x * y + y + y * y + n) % 2 == 1; 10 | } 11 | 12 | private record Visited(int x, int y) { 13 | } 14 | 15 | private record State(int x, int y, int steps) { 16 | } 17 | 18 | private static int simulate(int targetX, int targetY, int designerN, boolean part2) { 19 | ArrayDeque q = new ArrayDeque<>(); 20 | HashSet visited = new HashSet<>(); 21 | 22 | q.add(new State(1, 1, 0)); 23 | while (!q.isEmpty()) { 24 | State cur = q.removeFirst(); 25 | 26 | if (part2 && cur.steps > 50) continue; 27 | if (!visited.add(new Visited(cur.x, cur.y))) continue; 28 | if (!part2 && cur.x == targetX && cur.y == targetY) return cur.steps; 29 | 30 | for (int[] dir : new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}}) { 31 | int dx = cur.x + dir[0]; 32 | int dy = cur.y + dir[1]; 33 | 34 | if (dx < 0 || dy < 0 || isWall(dx, dy, designerN)) continue; 35 | 36 | q.add(new State(dx, dy, cur.steps + 1)); 37 | } 38 | } 39 | 40 | return visited.size(); 41 | } 42 | 43 | private static int part1(int designerN) { 44 | return simulate(31, 39, designerN, false); 45 | } 46 | 47 | private static int part2(int designerN) { 48 | return simulate(0, 0, designerN, true); 49 | } 50 | 51 | public static void main(String[] args) throws IOException { 52 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 53 | int n = Integer.parseInt(reader.readLine()); 54 | 55 | System.out.println(part1(n)); 56 | System.out.println(part2(n)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /2016/day14.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.IOException; 3 | import java.io.InputStreamReader; 4 | import java.math.BigInteger; 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | import java.util.ArrayList; 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | class day14 { 12 | private interface HashFunction { 13 | String hash(MessageDigest md, String input); 14 | } 15 | 16 | private static String hash(MessageDigest md, String input) { 17 | return String.format("%032x", new BigInteger(1, md.digest(input.getBytes()))); 18 | } 19 | 20 | private static String stretchedHash(MessageDigest md, String input) { 21 | String hash = hash(md, input); 22 | for (int i = 0; i < 2016; i++) 23 | hash = hash(md, hash); 24 | return hash; 25 | } 26 | 27 | private static int solve(String salt, HashFunction fn) { 28 | try { 29 | MessageDigest md5 = MessageDigest.getInstance("MD5"); 30 | Pattern triple = Pattern.compile("(.)\\1{2}"); 31 | 32 | ArrayList hashes = new ArrayList<>(2000); 33 | int idx = 0; 34 | int keys = 0; 35 | 36 | while (keys < 64) { 37 | int oldIdx = idx; 38 | ++idx; 39 | 40 | if (oldIdx >= hashes.size()) hashes.add(fn.hash(md5, salt + oldIdx)); 41 | 42 | Matcher matcher = triple.matcher(hashes.get(oldIdx)); 43 | if (!matcher.find()) continue; 44 | String fivePattern = matcher.group().substring(0, 1).repeat(5); 45 | 46 | for (int i = oldIdx + 1; i < oldIdx + 1000; i++) { 47 | if (i >= hashes.size()) hashes.add(fn.hash(md5, salt + i)); 48 | if (!hashes.get(i).contains(fivePattern)) continue; 49 | 50 | ++keys; 51 | System.err.println("idx: " + oldIdx + ", keys: " + keys); 52 | break; 53 | } 54 | } 55 | 56 | return idx - 1; 57 | } catch (NoSuchAlgorithmException e) { 58 | throw new RuntimeException(e); 59 | } 60 | } 61 | 62 | private static int part1(String salt) { 63 | return solve(salt, day14::hash); 64 | } 65 | 66 | private static int part2(String salt) { 67 | return solve(salt, day14::stretchedHash); 68 | } 69 | 70 | public static void main(String[] args) throws IOException { 71 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 72 | String salt = reader.readLine(); 73 | 74 | System.out.println(part1(salt)); 75 | System.out.println(part2(salt)); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /2016/day15.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | import java.util.ArrayList; 4 | 5 | class day15 { 6 | private record Disc(int startPos, int positions) { 7 | } 8 | 9 | private static int simulate(ArrayList discs) { 10 | int t = 0; 11 | while (true) { 12 | boolean fallthrough = true; 13 | 14 | for (int i = 0; i < discs.size(); i++) 15 | fallthrough &= (discs.get(i).startPos + t + i + 1) % discs.get(i).positions == 0; 16 | 17 | if (fallthrough) return t; 18 | ++t; 19 | } 20 | } 21 | 22 | private static int part1(ArrayList discs) { 23 | return simulate(discs); 24 | } 25 | 26 | private static int part2(ArrayList discs) { 27 | discs.add(new Disc(0, 11)); 28 | return simulate(discs); 29 | } 30 | 31 | public static void main(String[] args) { 32 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 33 | ArrayList discs = new ArrayList<>( 34 | reader.lines().map(l -> { 35 | String[] split = l.split("[ .]"); 36 | return new Disc(Integer.parseInt(split[11]), Integer.parseInt(split[3])); 37 | }).toList() 38 | ); 39 | 40 | System.out.println(part1(discs)); 41 | System.out.println(part2(discs)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /2016/day16.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.IOException; 3 | import java.io.InputStreamReader; 4 | import java.util.stream.Collectors; 5 | 6 | class day16 { 7 | private static String dragon(String a, int length) { 8 | if (a.length() > length) 9 | return a.substring(0, length); 10 | 11 | String b = new StringBuilder(a).reverse().toString(); 12 | b = b.chars() 13 | .map(c -> (c - '0') ^ 1) 14 | .mapToObj(Integer::toString) 15 | .collect(Collectors.joining()); 16 | 17 | return dragon(a + "0" + b, length); 18 | } 19 | 20 | private static String checksum(String s) { 21 | if (s.length() % 2 == 1) 22 | return s; 23 | 24 | StringBuilder builder = new StringBuilder(); 25 | for (int i = 0; i < s.length(); i += 2) 26 | builder.append(s.charAt(i) == s.charAt(i + 1) ? 1 : 0); 27 | 28 | return checksum(builder.toString()); 29 | } 30 | 31 | private static String part1(String input) { 32 | return checksum(dragon(input, 272)); 33 | } 34 | 35 | private static String part2(String input) { 36 | return checksum(dragon(input, 35651584)); 37 | } 38 | 39 | public static void main(String[] args) throws IOException { 40 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 41 | String input = reader.readLine(); 42 | 43 | System.out.println(part1(input)); 44 | System.out.println(part2(input)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /2016/day18.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.IOException; 3 | import java.io.InputStreamReader; 4 | import java.util.ArrayList; 5 | 6 | class day18 { 7 | private static long calculateSafeTiles(String startingRow, int rowCnt) { 8 | ArrayList rows = new ArrayList<>(40); 9 | rows.add(startingRow); 10 | 11 | for (int i = 1; i < rowCnt; i++) { 12 | String extendedRow = "." + rows.get(i - 1) + "."; 13 | 14 | StringBuilder builder = new StringBuilder(startingRow.length()); 15 | for (int j = 1; j <= startingRow.length(); j++) { 16 | String pattern = extendedRow.substring(j - 1, j + 2); 17 | 18 | boolean trap = pattern.equals("^^.") || 19 | pattern.equals(".^^") || 20 | pattern.equals("^..") || 21 | pattern.equals("..^"); 22 | 23 | builder.append(trap ? '^' : '.'); 24 | } 25 | rows.add(builder.toString()); 26 | } 27 | 28 | return rows.stream() 29 | .mapToLong(r -> r.chars().filter(c -> c == '.').count()) 30 | .sum(); 31 | } 32 | 33 | private static long part1(String startingRow) { 34 | return calculateSafeTiles(startingRow, 40); 35 | } 36 | 37 | private static long part2(String startingRow) { 38 | return calculateSafeTiles(startingRow, 400000); 39 | } 40 | 41 | public static void main(String[] args) throws IOException { 42 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 43 | String row = reader.readLine(); 44 | 45 | System.out.println(part1(row)); 46 | System.out.println(part2(row)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /2016/day19.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.IOException; 3 | import java.io.InputStreamReader; 4 | import java.util.ArrayList; 5 | import java.util.stream.IntStream; 6 | 7 | class day19 { 8 | private static int part1(int elvesCnt) { 9 | int[] elves = IntStream.rangeClosed(1, elvesCnt).toArray(); 10 | 11 | while (elves.length > 2) { 12 | int[] tmp = elves; 13 | elves = IntStream.range(tmp.length % 2, tmp.length) 14 | .filter(i -> i % 2 == 0) 15 | .map(i -> tmp[i]) 16 | .toArray(); 17 | } 18 | 19 | return elves[0]; 20 | } 21 | 22 | private static int part2(int elvesCnt) { 23 | ArrayList elves = new ArrayList<>(IntStream.rangeClosed(1, elvesCnt).boxed().toList()); 24 | 25 | int cur = 0; 26 | while (elves.size() > 1) { 27 | if (elves.size() % 10000 == 0) 28 | System.err.println("Remaining elves: " + elves.size()); 29 | 30 | int tgt = cur + elves.size() / 2; 31 | boolean shouldInc = tgt < elves.size(); 32 | 33 | elves.remove(tgt % elves.size()); 34 | cur = (cur + (shouldInc ? 1 : 0)) % elves.size(); 35 | } 36 | 37 | return elves.getFirst(); 38 | } 39 | 40 | public static void main(String[] args) throws IOException { 41 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 42 | int elvesCnt = Integer.parseInt(reader.readLine()); 43 | 44 | System.out.println(part1(elvesCnt)); 45 | System.out.println(part2(elvesCnt)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /2016/day2.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | import java.util.ArrayList; 4 | import java.util.stream.Collectors; 5 | 6 | class day2 { 7 | private record Position(int row, int col) { 8 | } 9 | 10 | private static String getCode(char[][] keypad, ArrayList instructions, Position start) { 11 | StringBuilder codeStream = new StringBuilder(); 12 | Position pos = start; 13 | 14 | for (String line : instructions) { 15 | for (char c : line.toCharArray()) { 16 | Position newPos = switch (c) { 17 | case 'U' -> new Position(pos.row - 1, pos.col); 18 | case 'D' -> new Position(pos.row + 1, pos.col); 19 | case 'L' -> new Position(pos.row, pos.col - 1); 20 | case 'R' -> new Position(pos.row, pos.col + 1); 21 | default -> throw new IllegalStateException("Unexpected value: " + c); 22 | }; 23 | 24 | if (newPos.row < 0 || newPos.row >= keypad.length) continue; 25 | if (newPos.col < 0 || newPos.col >= keypad[0].length) continue; 26 | if (keypad[newPos.row][newPos.col] == ' ') continue; 27 | 28 | pos = newPos; 29 | } 30 | 31 | codeStream.append(keypad[pos.row][pos.col]); 32 | } 33 | 34 | return codeStream.toString(); 35 | } 36 | 37 | private static String part1(ArrayList lines) { 38 | char[][] keypad = { 39 | {'1', '2', '3'}, 40 | {'4', '5', '6'}, 41 | {'7', '8', '9'} 42 | }; 43 | 44 | return getCode(keypad, lines, new Position(1, 1)); 45 | } 46 | 47 | private static String part2(ArrayList lines) { 48 | char[][] keypad = { 49 | {' ', ' ', '1', ' ', ' '}, 50 | {' ', '2', '3', '4', ' '}, 51 | {'5', '6', '7', '8', '9'}, 52 | {' ', 'A', 'B', 'C', ' '}, 53 | {' ', ' ', 'D', ' ', ' '}, 54 | }; 55 | 56 | return getCode(keypad, lines, new Position(2, 0)); 57 | } 58 | 59 | public static void main(String[] args) { 60 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 61 | ArrayList lines = reader.lines().collect(Collectors.toCollection(ArrayList::new)); 62 | 63 | System.out.println(part1(lines)); 64 | System.out.println(part2(lines)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /2016/day20.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | import java.util.ArrayList; 4 | 5 | class day20 { 6 | private record Range(long begin, long end) { 7 | } 8 | 9 | private static ArrayList getAllowedIPRanges(String[] blacklist) { 10 | ArrayList allowedIPRanges = new ArrayList<>(1); 11 | allowedIPRanges.add(new Range(0L, 4294967295L)); 12 | 13 | for (String line : blacklist) { 14 | String[] split = line.split("-"); 15 | 16 | long begin = Long.parseLong(split[0]); 17 | long end = Long.parseLong(split[1]); 18 | 19 | ArrayList tmp = new ArrayList<>(); 20 | for (Range r : allowedIPRanges) { 21 | long overlapBegin = Long.max(begin, r.begin); 22 | long overlapEnd = Long.min(end, r.end); 23 | 24 | if (overlapBegin > overlapEnd) { 25 | tmp.add(r); 26 | continue; 27 | } 28 | 29 | if (r.begin < overlapBegin) 30 | tmp.add(new Range(r.begin, overlapBegin - 1)); 31 | 32 | if (r.end > overlapEnd) 33 | tmp.add(new Range(overlapEnd + 1, r.end)); 34 | } 35 | allowedIPRanges = tmp; 36 | } 37 | 38 | return allowedIPRanges; 39 | } 40 | 41 | private static long part1(String[] blacklist) { 42 | return getAllowedIPRanges(blacklist).getFirst().begin; 43 | } 44 | 45 | private static long part2(String[] blacklist) { 46 | return getAllowedIPRanges(blacklist).stream().mapToLong(r -> r.end - r.begin + 1).sum(); 47 | } 48 | 49 | public static void main(String[] args) { 50 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 51 | String[] lines = reader.lines().toArray(String[]::new); 52 | 53 | System.out.println(part1(lines)); 54 | System.out.println(part2(lines)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /2016/day25.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | 4 | class day25 { 5 | private static int part1(String[] instructions) { 6 | // Running the instructions infinitely prints the binary 7 | // representation of 'a + MAGIC_NUMBER' after each other. 8 | // 9 | // 1: while(true) { 10 | // 2: n = a + MAGIC_NUMBER; 11 | // 3: while(n != 0) { 12 | // 4: print(n % 2); 13 | // 5: n /= 2; 14 | // 6: } 15 | // 7: } 16 | int x = Integer.parseInt(instructions[1].split(" ")[1]); 17 | int y = Integer.parseInt(instructions[2].split(" ")[1]); 18 | 19 | String magicBinary = Integer.toBinaryString(x * y); 20 | String desiredBinary = "10".repeat(magicBinary.length() / 2); 21 | 22 | return Integer.parseUnsignedInt(desiredBinary, 2) - Integer.parseInt(magicBinary, 2); 23 | } 24 | 25 | public static void main(String[] args) { 26 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 27 | String[] instruction = reader.lines().toArray(String[]::new); 28 | 29 | System.out.println(part1(instruction)); 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /2016/day3.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | import java.util.Arrays; 4 | 5 | class day3 { 6 | private static boolean isValidTriangle(Integer[] sides) { 7 | return sides[0] + sides[1] > sides[2] && sides[0] + sides[2] > sides[1] && sides[1] + sides[2] > sides[0]; 8 | } 9 | 10 | private static long part1(Integer[][] lines) { 11 | return Arrays.stream(lines).filter(day3::isValidTriangle).count(); 12 | } 13 | 14 | private static long part2(Integer[][] lines) { 15 | int cnt = 0; 16 | 17 | for (int i = 0; i < lines.length; i += 3) 18 | for (int j = 0; j < 3; ++j) 19 | if (isValidTriangle(new Integer[]{lines[i][j], lines[i + 1][j], lines[i + 2][j]})) 20 | cnt += 1; 21 | 22 | return cnt; 23 | } 24 | 25 | public static void main(String[] args) { 26 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 27 | var lines = reader.lines() 28 | .map(str -> str.trim().split("\\s+")) 29 | .map(arr -> Arrays.stream(arr).map(Integer::parseInt).toArray(Integer[]::new)) 30 | .toArray(Integer[][]::new); 31 | 32 | System.out.println(part1(lines)); 33 | System.out.println(part2(lines)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /2016/day5.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.IOException; 3 | import java.io.InputStreamReader; 4 | import java.math.BigInteger; 5 | import java.nio.charset.StandardCharsets; 6 | import java.security.MessageDigest; 7 | import java.security.NoSuchAlgorithmException; 8 | 9 | class day5 { 10 | private static String crackPassword(String id, boolean part2) throws NoSuchAlgorithmException { 11 | MessageDigest md5 = MessageDigest.getInstance("MD5"); 12 | 13 | char[] password = new char[8]; 14 | boolean[] cracked = new boolean[8]; 15 | 16 | int knownCharacters = 0; 17 | int idx = 0; 18 | 19 | while (knownCharacters < 8) { 20 | md5.update(StandardCharsets.UTF_8.encode(id + idx)); 21 | 22 | var hash = String.format("%032x", new BigInteger(1, md5.digest())); 23 | if (hash.startsWith("00000")) { 24 | char currentChar = part2 ? hash.charAt(6) : hash.charAt(5); 25 | int currentCharIdx = part2 ? hash.charAt(5) - '0' : knownCharacters; 26 | 27 | if (currentCharIdx >= 0 && currentCharIdx <= 7 && !cracked[currentCharIdx]) { 28 | System.err.printf("Found %d. character: '%c'\n", currentCharIdx + 1, currentChar); 29 | 30 | password[currentCharIdx] = currentChar; 31 | cracked[currentCharIdx] = true; 32 | ++knownCharacters; 33 | } 34 | } 35 | 36 | ++idx; 37 | } 38 | 39 | return new String(password); 40 | } 41 | 42 | public static void main(String[] args) throws NoSuchAlgorithmException, IOException { 43 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 44 | String id = reader.readLine(); 45 | 46 | System.out.println(crackPassword(id, false)); 47 | System.out.println(crackPassword(id, true)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /2016/day6.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | import java.util.ArrayList; 4 | import java.util.Map; 5 | import java.util.function.Function; 6 | import java.util.stream.Collectors; 7 | import java.util.stream.IntStream; 8 | 9 | class day6 { 10 | public static void main(String[] args) { 11 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 12 | String[] lines = reader.lines().toArray(String[]::new); 13 | 14 | ArrayList> columns = new ArrayList<>(); 15 | for (int i = 0; i < lines[0].length(); i++) 16 | columns.add(new ArrayList<>()); 17 | 18 | for (String line : lines) 19 | IntStream.range(0, line.length()).forEach(i -> columns.get(i).add(line.charAt(i))); 20 | 21 | String part1 = columns.stream() 22 | .map(c -> c.stream() 23 | .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) 24 | .entrySet() 25 | .stream() 26 | .max(Map.Entry.comparingByValue()) 27 | .map(Map.Entry::getKey) 28 | .orElse(' ')) 29 | .map(Object::toString) 30 | .collect(Collectors.joining()); 31 | 32 | String part2 = columns.stream() 33 | .map(c -> c.stream() 34 | .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) 35 | .entrySet() 36 | .stream() 37 | .min(Map.Entry.comparingByValue()) 38 | .map(Map.Entry::getKey) 39 | .orElse(' ')) 40 | .map(Object::toString) 41 | .collect(Collectors.joining()); 42 | 43 | System.out.println(part1); 44 | System.out.println(part2); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /2016/day8.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.InputStreamReader; 3 | import java.util.Arrays; 4 | import java.util.stream.IntStream; 5 | 6 | class day8 { 7 | private static void rotateRow(boolean[][] screen, int r, int n) { 8 | boolean[] tmp = new boolean[screen[r].length]; 9 | 10 | for (int i = 0; i < tmp.length; i++) 11 | tmp[(i + n) % tmp.length] = screen[r][i]; 12 | 13 | screen[r] = tmp; 14 | } 15 | 16 | private static void rotateCol(boolean[][] screen, int c, int n) { 17 | boolean[] tmp = new boolean[screen.length]; 18 | 19 | for (int i = 0; i < tmp.length; i++) 20 | tmp[(i + n) % tmp.length] = screen[i][c]; 21 | 22 | for (int i = 0; i < tmp.length; i++) 23 | screen[i][c] = tmp[i]; 24 | } 25 | 26 | private static void rect(boolean[][] screen, int w, int h) { 27 | for (int i = 0; i < h; i++) 28 | for (int j = 0; j < w; j++) 29 | screen[i][j] = true; 30 | } 31 | 32 | private static long part1(boolean[][] screen, String[] lines) { 33 | for (String line : lines) { 34 | String[] split = line.split(" "); 35 | 36 | if (split[0].equals("rect")) { 37 | var vals = Arrays.stream(split[1].split("x")) 38 | .map(Integer::parseInt) 39 | .toArray(Integer[]::new); 40 | 41 | rect(screen, vals[0], vals[1]); 42 | } else if (split[0].equals("rotate")) { 43 | int target = Integer.parseInt(split[2].split("=")[1]); 44 | int n = Integer.parseInt(split[4]); 45 | 46 | if (split[1].equals("row")) rotateRow(screen, target, n); 47 | else rotateCol(screen, target, n); 48 | } 49 | } 50 | 51 | return Arrays.stream(screen) 52 | .map(l -> IntStream.range(0, l.length) 53 | .filter(i -> l[i]) 54 | .count()) 55 | .reduce(0L, Long::sum); 56 | } 57 | 58 | private static void part2(boolean[][] screen) { 59 | for (var r : screen) { 60 | for (var c : r) { 61 | if (c) System.out.print('O'); 62 | else System.out.print(' '); 63 | } 64 | System.out.println(); 65 | } 66 | } 67 | 68 | public static void main(String[] args) { 69 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 70 | 71 | String[] lines = reader.lines().toArray(String[]::new); 72 | boolean[][] screen = new boolean[6][50]; 73 | 74 | System.out.println(part1(screen, lines)); 75 | part2(screen); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /2016/day9.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.IOException; 3 | import java.io.InputStreamReader; 4 | 5 | class day9 { 6 | private static long length(String line, boolean recursive) { 7 | long length = 0; 8 | 9 | for (int i = 0; i < line.length(); ++i) { 10 | if (line.charAt(i) == '(') { 11 | int end = line.indexOf(')', i); 12 | String expandPattern = line.substring(i + 1, end); 13 | 14 | String[] vals = expandPattern.split("x"); 15 | int amount = Integer.parseInt(vals[0]); 16 | int repeat = Integer.parseInt(vals[1]); 17 | 18 | String pattern = line.substring(end + 1, end + amount + 1); 19 | long patternLength = (recursive ? length(pattern, true) : pattern.length()); 20 | length += patternLength * repeat; 21 | 22 | i = end + amount; 23 | continue; 24 | } 25 | 26 | ++length; 27 | } 28 | 29 | return length; 30 | } 31 | 32 | private static long part1(String line) { 33 | return length(line, false); 34 | } 35 | 36 | 37 | private static long part2(String line) { 38 | return length(line, true); 39 | } 40 | 41 | public static void main(String[] args) throws IOException { 42 | BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 43 | String line = reader.readLine(); 44 | 45 | System.out.println(part1(line)); 46 | System.out.println(part2(line)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /2017/day1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func solve(input string, shift int) int { 6 | sum := 0 7 | l := len(input) 8 | 9 | for i := range l { 10 | c, n := input[i], input[(i+shift)%l] 11 | 12 | if c == n { 13 | sum += int(c - '0') 14 | } 15 | } 16 | 17 | return sum 18 | } 19 | 20 | func part1(input string) int { 21 | return solve(input, 1) 22 | } 23 | 24 | func part2(input string) int { 25 | return solve(input, len(input)/2) 26 | } 27 | 28 | func main() { 29 | var input string 30 | fmt.Scanln(&input) 31 | 32 | println(part1(input)) 33 | println(part2(input)) 34 | } 35 | -------------------------------------------------------------------------------- /2017/day10/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "slices" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | func default_hash_input() []int { 13 | arr := make([]int, 256) 14 | for i := range len(arr) { 15 | arr[i] = i 16 | } 17 | return arr 18 | } 19 | 20 | func hash(input []int, current_position int, skip_size int, lengths []int) ([]int, int, int) { 21 | input = append(input[current_position:], input[:current_position]...) 22 | 23 | for _, l := range lengths { 24 | slices.Reverse(input[:l]) 25 | input = append(input[l:], input[:l]...) 26 | input = append(input[skip_size:], input[:skip_size]...) 27 | 28 | current_position = (current_position + l + skip_size) % len(input) 29 | skip_size = (skip_size + 1) % len(input) 30 | } 31 | 32 | idx := (len(input) - current_position) % len(input) 33 | return append(input[idx:], input[:idx]...), current_position, skip_size 34 | } 35 | 36 | func part1(input string) int { 37 | var lengths []int 38 | for n := range strings.SplitSeq(input, ",") { 39 | num, _ := strconv.Atoi(n) 40 | lengths = append(lengths, num) 41 | } 42 | 43 | h, _, _ := hash(default_hash_input(), 0, 0, lengths) 44 | return h[0] * h[1] 45 | } 46 | 47 | func part2(input string) string { 48 | var lengths []int 49 | for _, c := range input { 50 | lengths = append(lengths, int(c)) 51 | } 52 | lengths = append(lengths, []int{17, 31, 73, 47, 23}...) 53 | 54 | arr, t, s := default_hash_input(), 0, 0 55 | 56 | for range 64 { 57 | arr, t, s = hash(arr, t, s, lengths) 58 | } 59 | 60 | var hexString string 61 | for i := range 16 { 62 | xor := 0 63 | for _, n := range arr[i*16 : (i+1)*16] { 64 | xor ^= n 65 | } 66 | hexString += fmt.Sprintf("%02x", xor) 67 | } 68 | 69 | return hexString 70 | } 71 | 72 | func main() { 73 | scanner := bufio.NewScanner(os.Stdin) 74 | scanner.Scan() 75 | input := scanner.Text() 76 | 77 | println(part1(input)) 78 | println(part2(input)) 79 | } 80 | -------------------------------------------------------------------------------- /2017/day11/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func part1(input []string) int { 10 | nw, n, ne, sw, s, se := 0, 0, 0, 0, 0, 0 11 | 12 | for _, dir := range input { 13 | switch dir { 14 | case "nw": 15 | nw += 1 16 | case "n": 17 | n += 1 18 | case "ne": 19 | ne += 1 20 | case "sw": 21 | sw += 1 22 | case "s": 23 | s += 1 24 | case "se": 25 | se += 1 26 | } 27 | } 28 | 29 | acc, changed := nw+n+ne+sw+s+se, true 30 | for changed { 31 | // sw + se = s 32 | x := min(sw, se) 33 | sw, se, s = sw-x, se-x, s+x 34 | 35 | // nw + ne = n 36 | x = min(nw, ne) 37 | nw, ne, n = nw-x, ne-x, n+x 38 | 39 | // nw + se = 0 40 | x = min(nw, se) 41 | nw, se = nw-x, se-x 42 | 43 | // ne + sw = 0 44 | x = min(ne, sw) 45 | ne, sw = ne-x, sw-x 46 | 47 | // n + s = 0 48 | x = min(n, s) 49 | n, s = n-x, s-x 50 | 51 | // ne + s = se 52 | x = min(ne, s) 53 | ne, s, se = ne-x, s-x, se+x 54 | 55 | // nw + s = sw 56 | x = min(nw, s) 57 | nw, s, sw = nw-x, s-x, sw+x 58 | 59 | // se + n = ne 60 | x = min(se, n) 61 | se, n, ne = se-x, n-x, ne+x 62 | 63 | // sw + n = nw 64 | x = min(sw, n) 65 | sw, n, nw = sw-x, n-x, nw+x 66 | 67 | new_acc := nw + n + ne + sw + s + se 68 | acc, changed = new_acc, new_acc < acc 69 | } 70 | 71 | return acc 72 | } 73 | 74 | func part2(input []string) int { 75 | max_dist := 0 76 | for i := range len(input) { 77 | max_dist = max(max_dist, part1(input[0:i])) 78 | } 79 | return max_dist 80 | } 81 | 82 | func main() { 83 | scanner := bufio.NewScanner(os.Stdin) 84 | scanner.Scan() 85 | dirs := strings.Split(scanner.Text(), ",") 86 | 87 | println(part1(dirs)) 88 | println(part2(dirs)) 89 | } 90 | -------------------------------------------------------------------------------- /2017/day2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | func min_val(nums []int) int { 11 | out := nums[0] 12 | for _, n := range nums { 13 | out = min(out, n) 14 | } 15 | return out 16 | } 17 | 18 | func max_val(nums []int) int { 19 | out := nums[0] 20 | for _, n := range nums { 21 | out = max(out, n) 22 | } 23 | return out 24 | } 25 | 26 | func part1(input [][]int) int { 27 | sum := 0 28 | for _, line := range input { 29 | sum += max_val(line) - min_val(line) 30 | } 31 | return sum 32 | } 33 | 34 | func part2(input [][]int) int { 35 | sum := 0 36 | for _, line := range input { 37 | for i, n := range line { 38 | for i2, n2 := range line { 39 | if i != i2 && n2%n == 0 { 40 | sum += n2 / n 41 | } 42 | } 43 | } 44 | } 45 | return sum 46 | } 47 | 48 | func main() { 49 | var input [][]int 50 | 51 | scanner := bufio.NewScanner(os.Stdin) 52 | for scanner.Scan() { 53 | var line []int 54 | 55 | for _, n := range strings.Fields(scanner.Text()) { 56 | num, _ := strconv.Atoi(n) 57 | line = append(line, num) 58 | } 59 | 60 | input = append(input, line) 61 | } 62 | 63 | println(part1(input)) 64 | println(part2(input)) 65 | } 66 | -------------------------------------------------------------------------------- /2017/day3/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | func get_ring_info(value int) (int, int, int) { 9 | ring := 1 10 | width := 0 11 | highest_value_on_ring := 1 12 | 13 | for highest_value_on_ring < value { 14 | ring += 1 15 | width = 2*ring - 2 16 | highest_value_on_ring = int(math.Pow(float64(2*ring-1), 2)) 17 | } 18 | 19 | return ring, width, highest_value_on_ring 20 | } 21 | 22 | func part1(value int) int { 23 | ring, width, highest_value_on_ring := get_ring_info(value) 24 | 25 | section := int(math.Ceil(float64(highest_value_on_ring-value) / float64(width))) 26 | section_middle_value := highest_value_on_ring - ((section - 1) * width) - width/2 27 | 28 | return ring - 1 + int(math.Abs(float64(section_middle_value-value))) 29 | } 30 | 31 | func part2(value int) int { 32 | _, width, _ := get_ring_info(value) 33 | 34 | matrix := make([][]int, width+3) 35 | for i := range width + 3 { 36 | matrix[i] = make([]int, width+3) 37 | } 38 | 39 | r, c := width/2+1, width/2+1 40 | matrix[r][c] = 1 41 | 42 | vr, vc := -1, 0 43 | c += 1 44 | 45 | for r <= width+1 && c <= width+1 { 46 | dirs := [][]int{{1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}} 47 | for _, d := range dirs { 48 | matrix[r][c] += matrix[r+d[0]][c+d[1]] 49 | } 50 | 51 | if matrix[r][c] > value { 52 | return matrix[r][c] 53 | } 54 | 55 | r, c = r+vr, c+vc 56 | if matrix[r+-vc][c+vr] == 0 { 57 | vr, vc = -vc, vr 58 | } 59 | } 60 | 61 | return -1 62 | } 63 | 64 | func main() { 65 | var input int 66 | fmt.Scanln(&input) 67 | 68 | println(part1(input)) 69 | println(part2(input)) 70 | } 71 | -------------------------------------------------------------------------------- /2017/day4/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | func part1(input [][]string) int { 11 | cnt := 0 12 | 13 | for _, words := range input { 14 | set := make(map[string]bool) 15 | 16 | for _, word := range words { 17 | set[word] = true 18 | } 19 | 20 | if len(set) == len(words) { 21 | cnt += 1 22 | } 23 | } 24 | 25 | return cnt 26 | } 27 | 28 | func part2(input [][]string) int { 29 | cnt := 0 30 | 31 | for _, words := range input { 32 | set := make(map[string]bool) 33 | 34 | for _, word := range words { 35 | s := strings.Split(word, "") 36 | sort.Strings(s) 37 | set[strings.Join(s, "")] = true 38 | } 39 | 40 | if len(set) == len(words) { 41 | cnt += 1 42 | } 43 | } 44 | 45 | return cnt 46 | } 47 | 48 | func main() { 49 | var input [][]string 50 | 51 | scanner := bufio.NewScanner(os.Stdin) 52 | for scanner.Scan() { 53 | input = append(input, strings.Fields(scanner.Text())) 54 | } 55 | 56 | println(part1(input)) 57 | println(part2(input)) 58 | } 59 | -------------------------------------------------------------------------------- /2017/day5/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | func part1(offsets []int) int { 10 | pc := 0 11 | steps := 0 12 | 13 | for pc >= 0 && pc < len(offsets) { 14 | inc := offsets[pc] 15 | offsets[pc] += 1 16 | pc += inc 17 | 18 | steps += 1 19 | } 20 | 21 | return steps 22 | } 23 | 24 | func part2(offsets []int) int { 25 | pc := 0 26 | steps := 0 27 | 28 | for pc >= 0 && pc < len(offsets) { 29 | inc := offsets[pc] 30 | if offsets[pc] >= 3 { 31 | offsets[pc] -= 1 32 | } else { 33 | offsets[pc] += 1 34 | } 35 | pc += inc 36 | 37 | steps += 1 38 | } 39 | 40 | return steps 41 | } 42 | 43 | func main() { 44 | var offsets []int 45 | 46 | scanner := bufio.NewScanner(os.Stdin) 47 | for scanner.Scan() { 48 | n, _ := strconv.Atoi(scanner.Text()) 49 | offsets = append(offsets, n) 50 | } 51 | 52 | println(part1(append([]int{}, offsets...))) 53 | println(part2(append([]int{}, offsets...))) 54 | } 55 | -------------------------------------------------------------------------------- /2017/day6/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "slices" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | func solve(blocks []int) int { 13 | states := make(map[string]bool) 14 | iterations := 0 15 | 16 | for { 17 | state := fmt.Sprint(blocks) 18 | 19 | if states[state] { 20 | break 21 | } 22 | 23 | max_idx := 0 24 | for i := range len(blocks) { 25 | if blocks[i] > blocks[max_idx] { 26 | max_idx = i 27 | } 28 | } 29 | 30 | cnt := blocks[max_idx] 31 | blocks[max_idx] = 0 32 | 33 | for range cnt { 34 | max_idx = (max_idx + 1) % len(blocks) 35 | blocks[max_idx] += 1 36 | } 37 | 38 | iterations += 1 39 | states[state] = true 40 | } 41 | 42 | return iterations 43 | } 44 | 45 | func part1(blocks []int) int { 46 | return solve(blocks) 47 | } 48 | 49 | func part2(blocks []int) int { 50 | solve(blocks) 51 | return solve(blocks) 52 | } 53 | 54 | func main() { 55 | scanner := bufio.NewScanner(os.Stdin) 56 | 57 | var blocks []int 58 | scanner.Scan() 59 | for _, n := range strings.Fields(scanner.Text()) { 60 | num, _ := strconv.Atoi(n) 61 | blocks = append(blocks, num) 62 | } 63 | 64 | println(part1(slices.Clone(blocks))) 65 | println(part2(slices.Clone(blocks))) 66 | } 67 | -------------------------------------------------------------------------------- /2017/day7/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "reflect" 7 | "regexp" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | func part1(lines [][]string) string { 13 | set := make(map[string]bool) 14 | 15 | for _, l := range lines { 16 | set[l[0]] = true 17 | } 18 | 19 | for _, l := range lines { 20 | for n := range strings.SplitSeq(l[2], ", ") { 21 | delete(set, n) 22 | } 23 | } 24 | 25 | return reflect.ValueOf(set).MapKeys()[0].String() 26 | } 27 | 28 | func part2(lines [][]string) int { 29 | weights := make(map[string]int) 30 | children := make(map[string][]string) 31 | 32 | for _, l := range lines { 33 | w, _ := strconv.Atoi(l[1]) 34 | weights[l[0]] = w 35 | children[l[0]] = strings.Split(l[2], ", ") 36 | } 37 | 38 | s := []string{part1(lines)} 39 | diff := 0 40 | 41 | for len(s) != 0 { 42 | top := s[0] 43 | s = s[1:] 44 | 45 | ws := []int{} 46 | for _, child := range children[top] { 47 | w := 0 48 | s := []string{child} 49 | 50 | for len(s) != 0 { 51 | cur := s[0] 52 | s = s[1:] 53 | 54 | w += weights[cur] 55 | s = append(s, children[cur]...) 56 | } 57 | 58 | ws = append(ws, w) 59 | } 60 | 61 | unbalanced := 0 62 | for i := range ws { 63 | if ws[i] > ws[unbalanced] { 64 | unbalanced = i 65 | break 66 | } 67 | } 68 | 69 | if ws[unbalanced] == ws[(unbalanced+1)%len(ws)] { 70 | return weights[top] - diff 71 | } 72 | 73 | diff = ws[unbalanced] - ws[(unbalanced+1)%len(ws)] 74 | s = append(s, children[top][unbalanced]) 75 | } 76 | 77 | return 0 78 | } 79 | 80 | func main() { 81 | re := regexp.MustCompile(`([a-z]+).*\((\d+)(.*-> (.*))?`) 82 | 83 | lines := [][]string{} 84 | scanner := bufio.NewScanner(os.Stdin) 85 | for scanner.Scan() { 86 | match := re.FindStringSubmatch(scanner.Text()) 87 | lines = append(lines, []string{match[1], match[2], match[4]}) 88 | } 89 | 90 | println(part1(lines)) 91 | println(part2(lines)) 92 | } 93 | -------------------------------------------------------------------------------- /2017/day8/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "maps" 6 | "os" 7 | "regexp" 8 | "slices" 9 | "strconv" 10 | ) 11 | 12 | func eval(insns [][]string) (map[string]int, int) { 13 | registers := make(map[string]int) 14 | alloc := 0 15 | 16 | for _, insn := range insns { 17 | reg, op, rhs, creg, cop, crhs := insn[0], insn[1], insn[2], insn[3], insn[4], insn[5] 18 | 19 | creg_val := registers[creg] 20 | crhs_val, _ := strconv.Atoi(crhs) 21 | var cond bool 22 | switch cop { 23 | case ">": 24 | cond = creg_val > crhs_val 25 | case "<": 26 | cond = creg_val < crhs_val 27 | case ">=": 28 | cond = creg_val >= crhs_val 29 | case "<=": 30 | cond = creg_val <= crhs_val 31 | case "==": 32 | cond = creg_val == crhs_val 33 | case "!=": 34 | cond = creg_val != crhs_val 35 | } 36 | 37 | if !cond { 38 | continue 39 | } 40 | 41 | rhs_val, _ := strconv.Atoi(rhs) 42 | switch op { 43 | case "inc": 44 | registers[reg] += rhs_val 45 | case "dec": 46 | registers[reg] -= rhs_val 47 | } 48 | 49 | alloc = max(alloc, registers[reg]) 50 | } 51 | 52 | return registers, alloc 53 | } 54 | 55 | func part1(insns [][]string) int { 56 | registers, _ := eval(insns) 57 | 58 | values := slices.Sorted(maps.Values(registers)) 59 | slices.Reverse(values) 60 | return values[0] 61 | } 62 | 63 | func part2(insns [][]string) int { 64 | _, alloc := eval(insns) 65 | return alloc 66 | } 67 | 68 | func main() { 69 | re := regexp.MustCompile(`([a-z]+)\s(inc|dec)\s(-?\d+)\sif\s([a-z]+)\s([<>=!]+)\s(-?\d+)`) 70 | 71 | insns := [][]string{} 72 | scanner := bufio.NewScanner(os.Stdin) 73 | for scanner.Scan() { 74 | match := re.FindStringSubmatch(scanner.Text()) 75 | insns = append(insns, match[1:]) 76 | } 77 | 78 | println(part1(insns)) 79 | println(part2(insns)) 80 | } 81 | -------------------------------------------------------------------------------- /2017/day9/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func solve(input string) (int, int) { 6 | sum, cancelled, idx, group, garbage := 0, 0, 0, 0, false 7 | 8 | for idx < len(input) { 9 | c := input[idx] 10 | 11 | if c == '!' { 12 | idx += 1 13 | } else if c == '>' { 14 | garbage = false 15 | } else if garbage { 16 | cancelled += 1 17 | } else if c == '<' { 18 | garbage = true 19 | } else if c == '{' { 20 | group += 1 21 | } else if c == '}' { 22 | sum += group 23 | group -= 1 24 | } 25 | 26 | idx += 1 27 | } 28 | 29 | return sum, cancelled 30 | } 31 | 32 | func part1(input string) int { 33 | sum, _ := solve(input) 34 | return sum 35 | } 36 | 37 | func part2(input string) int { 38 | _, cancelled := solve(input) 39 | return cancelled 40 | } 41 | 42 | func main() { 43 | var input string 44 | fmt.Scanln(&input) 45 | 46 | println(part1(input)) 47 | println(part2(input)) 48 | } 49 | -------------------------------------------------------------------------------- /2017/go.mod: -------------------------------------------------------------------------------- 1 | module example.com/m/v2 2 | 3 | go 1.24.3 4 | -------------------------------------------------------------------------------- /2022/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /2022/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.9 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "2022", 7 | targets: [ 8 | .executableTarget(name: "day1"), 9 | .executableTarget(name: "day2"), 10 | .executableTarget(name: "day3"), 11 | .executableTarget(name: "day4"), 12 | .executableTarget(name: "day5"), 13 | .executableTarget(name: "day6"), 14 | .executableTarget(name: "day7"), 15 | .executableTarget(name: "day8"), 16 | .executableTarget(name: "day9"), 17 | .executableTarget(name: "day10"), 18 | .executableTarget(name: "day11"), 19 | .executableTarget(name: "day12"), 20 | .executableTarget(name: "day13"), 21 | .executableTarget(name: "day14"), 22 | .executableTarget(name: "day15"), 23 | .executableTarget(name: "day16"), 24 | .executableTarget(name: "day17"), 25 | .executableTarget(name: "day18"), 26 | .executableTarget(name: "day19"), 27 | .executableTarget(name: "day20"), 28 | .executableTarget(name: "day21"), 29 | .executableTarget(name: "day22"), 30 | .executableTarget(name: "day23"), 31 | .executableTarget(name: "day24"), 32 | .executableTarget(name: "day25"), 33 | ] 34 | ) 35 | -------------------------------------------------------------------------------- /2022/Sources/day1/main.swift: -------------------------------------------------------------------------------- 1 | func part1(calories: [Int]) -> Int { 2 | calories.max()! 3 | } 4 | 5 | func part2(calories: [Int]) -> Int { 6 | calories.sorted().suffix(3).reduce(0, +) 7 | } 8 | 9 | var lines: [String] = [String]() 10 | 11 | while let line = readLine() { 12 | lines.append(line) 13 | } 14 | 15 | let calories = lines.split(separator: "").map { 16 | group in group.map { line in Int(line)! }.reduce(0, +) 17 | } 18 | 19 | print(part1(calories: calories)) 20 | print(part2(calories: calories)) 21 | -------------------------------------------------------------------------------- /2022/Sources/day10/main.swift: -------------------------------------------------------------------------------- 1 | func part1(cycles: [Int]) -> Int { 2 | stride(from: 20, through: 220, by: 40).map { $0 * cycles[$0 - 1] }.reduce(0, +) 3 | } 4 | 5 | func part2(cycles: [Int]) { 6 | for r in 0...5 { 7 | for c in 0...39 { 8 | let m = cycles[r * 40 + c] 9 | 10 | print(m - 1 <= c && c <= m + 1 ? "#" : ".", terminator: "") 11 | } 12 | print() 13 | } 14 | } 15 | 16 | var lines: [String] = [String]() 17 | 18 | while let line = readLine() { 19 | lines.append(line) 20 | } 21 | 22 | var sim: [Int] = [1] 23 | 24 | for line in lines { 25 | let split = line.split(separator: " ") 26 | 27 | sim.append(sim.last!) 28 | 29 | if split[0] == "addx" { 30 | sim.append(sim.last! + Int(split[1])!) 31 | } 32 | } 33 | 34 | print(part1(cycles: sim)) 35 | part2(cycles: sim) 36 | -------------------------------------------------------------------------------- /2022/Sources/day11/main.swift: -------------------------------------------------------------------------------- 1 | class Monkey { 2 | var items: [Int] 3 | let op: (Int) -> Int 4 | let test: (Int, Int, Int) 5 | 6 | init(items: [Int], op: @escaping (Int) -> Int, test: (Int, Int, Int)) { 7 | self.items = items 8 | self.op = op 9 | self.test = test 10 | } 11 | } 12 | 13 | func extractNumbersFromStr(_ str: String) -> [Int] { 14 | let regex = try! Regex("[0-9]+") 15 | return str.ranges(of: regex).map { Int(str[$0])! } 16 | } 17 | 18 | func parseMonkey(data: [String]) -> Monkey { 19 | let items = extractNumbersFromStr(data[1]) 20 | 21 | let operation = data[2].split(separator: "=")[1].trimmingPrefix { $0.isWhitespace } 22 | let opParts = operation.split(separator: " ") 23 | let op: (Int) -> Int = switch opParts[1] { 24 | case "+": 25 | { $0 + Int(opParts[2])! } 26 | case "*": 27 | opParts[2] == "old" ? { $0 * $0 } : { $0 * Int(opParts[2])! } 28 | default: 29 | { $0 } 30 | } 31 | 32 | let test = extractNumbersFromStr(data[3])[0] 33 | let pass = extractNumbersFromStr(data[4])[0] 34 | let fail = extractNumbersFromStr(data[5])[0] 35 | 36 | return Monkey(items: items, op: op, test: (test, pass, fail)) 37 | } 38 | 39 | func simulateMonkeyBusines(monkeys: [Monkey], rounds: Int, worryLevelTransform: (Int) -> Int) -> [Int] { 40 | var monkeyBusiness = Array(repeating: 0, count: monkeys.count) 41 | 42 | for _ in 1...rounds { 43 | for (id, monkey) in monkeys.enumerated() { 44 | while !monkey.items.isEmpty { 45 | let worryLevel = monkey.items.removeFirst() 46 | let newWorryLevel = worryLevelTransform(monkey.op(worryLevel)) 47 | let (c, t, f) = monkey.test 48 | 49 | monkeyBusiness[id] += 1 50 | 51 | monkeys[newWorryLevel % c == 0 ? t : f].items.append(newWorryLevel) 52 | } 53 | } 54 | } 55 | 56 | return monkeyBusiness 57 | } 58 | 59 | func part1(monkeys: [Monkey]) -> Int { 60 | return simulateMonkeyBusines(monkeys: monkeys, rounds: 20) { 61 | $0 / 3 62 | }.sorted().suffix(2).reduce(1, *) 63 | } 64 | 65 | func part2(monkeys: [Monkey]) -> Int { 66 | return simulateMonkeyBusines(monkeys: monkeys, rounds: 10000) { 67 | $0 % monkeys.map { $0.test.0 }.reduce(1, *) 68 | }.sorted().suffix(2).reduce(1, *) 69 | } 70 | 71 | var lines: [String] = [String]() 72 | 73 | while let line = readLine() { 74 | lines.append(line) 75 | } 76 | 77 | let monkeyDatas = lines.split { $0.isEmpty }.map { Array($0) } 78 | 79 | print(part1(monkeys: monkeyDatas.map { parseMonkey(data: $0) })) 80 | print(part2(monkeys: monkeyDatas.map { parseMonkey(data: $0) })) 81 | -------------------------------------------------------------------------------- /2022/Sources/day12/main.swift: -------------------------------------------------------------------------------- 1 | func part1(grid: [[Int]], start: (Int, Int), end: (Int, Int)) -> Int { 2 | var q = [(start, 0)] 3 | var visited = Set<[Int]>() 4 | 5 | while !q.isEmpty { 6 | let ((r, c), s) = q.removeFirst() 7 | 8 | if (r, c) == end { 9 | return s 10 | } 11 | 12 | if !visited.insert([r, c]).inserted { 13 | continue 14 | } 15 | 16 | for (vr, vc) in [(-1, 0), (1, 0), (0, -1), (0, 1)] { 17 | let nr = r + vr 18 | let nc = c + vc 19 | 20 | if nr < 0 || nr >= grid.count || nc < 0 || nc >= grid[r].count { 21 | continue 22 | } 23 | 24 | if grid[nr][nc] - grid[r][c] <= 1 { 25 | q.append(((nr, nc), s + 1)) 26 | } 27 | } 28 | } 29 | 30 | return Int.max 31 | } 32 | 33 | func part2(grid: [[Int]], start: (Int, Int), end: (Int, Int)) -> Int { 34 | var minimum = Int.max 35 | 36 | for (ri, r) in grid.enumerated() { 37 | for (ci, _) in r.enumerated() { 38 | if grid[ri][ci] == 0 { 39 | minimum = min(minimum, part1(grid: grid, start: (ri, ci), end: end)) 40 | } 41 | } 42 | } 43 | 44 | return minimum 45 | } 46 | 47 | var grid: [[Int]] = [] 48 | var start: (Int, Int) = (-1, -1) 49 | var end: (Int, Int) = (-1, -1) 50 | 51 | var l = 0 52 | while let line = readLine() { 53 | grid.append(line.enumerated().map { (idx, c) in 54 | var cur = c 55 | 56 | if cur == "S" { 57 | start = (l, idx) 58 | cur = "a" 59 | } else if cur == "E" { 60 | end = (l, idx) 61 | cur = "z" 62 | } 63 | 64 | return Int(cur.unicodeScalars.first!.value - UnicodeScalar("a").value) 65 | }) 66 | 67 | l += 1 68 | } 69 | 70 | print(part1(grid: grid, start: start, end: end)) 71 | print(part2(grid: grid, start: start, end: end)) 72 | -------------------------------------------------------------------------------- /2022/Sources/day14/main.swift: -------------------------------------------------------------------------------- 1 | func simulate(grid: inout [[String]], start: [Int]) -> Bool { 2 | var coords = (start[0], start[1]) 3 | 4 | if(grid[coords.1][coords.0] != ".") { 5 | return false 6 | } 7 | 8 | while true { 9 | var placed = false 10 | for vc in [(0, 1), (-1, 1), (1, 1)] { 11 | let nx = coords.0 + vc.0 12 | let ny = coords.1 + vc.1 13 | 14 | if ny >= grid.count || nx < 0 || nx >= grid[ny].count { 15 | return false 16 | } 17 | 18 | if grid[ny][nx] == "." { 19 | placed = true 20 | coords = (nx, ny) 21 | break 22 | } 23 | } 24 | 25 | if !placed { 26 | grid[coords.1][coords.0] = "o" 27 | return true 28 | } 29 | } 30 | } 31 | 32 | func solve(lines: [String], floor: Bool) -> Int { 33 | let rocks = lines.map { 34 | $0.split(separator: " -> ").map { 35 | $0.split(separator: ",").map { Int($0)! } 36 | } 37 | } 38 | 39 | let xVals = rocks.flatMap { $0.map { $0[0] } } 40 | let xBoundary = (xVals.min()!, xVals.max()!) 41 | 42 | let yMax = rocks.flatMap { $0.map { $0[1] } }.max()! 43 | 44 | let xExtent = yMax + 2 45 | let row = Array(repeating: ".", count: xBoundary.1 - xBoundary.0 + 1 + 2 * xExtent) 46 | var map = Array(repeating: row, count: yMax + 1) 47 | 48 | if floor { 49 | map.append(contentsOf: [row, row.map { _ in "#" }]) 50 | } 51 | 52 | for points in rocks { 53 | for i in 0.. p2[ci] { 59 | swap(&p1, &p2) 60 | } 61 | 62 | for n in p1[ci]...p2[ci] { 63 | let x = ci == 1 ? p1[0] : n 64 | let y = ci == 1 ? n : p1[1] 65 | map[y][x - xBoundary.0 + xExtent] = "#" 66 | } 67 | } 68 | } 69 | 70 | return (0...).first(where: ) { _ in 71 | !simulate(grid: &map, start: [500 - xBoundary.0 + xExtent, 0]) 72 | }! 73 | } 74 | 75 | func part1(lines: [String]) -> Int { 76 | solve(lines: lines, floor: false) 77 | } 78 | 79 | func part2(lines: [String]) -> Int { 80 | solve(lines: lines, floor: true) 81 | } 82 | 83 | var lines: [String] = [String]() 84 | 85 | while let line = readLine() { 86 | lines.append(line) 87 | } 88 | 89 | print(part1(lines: lines)) 90 | print(part2(lines: lines)) 91 | -------------------------------------------------------------------------------- /2022/Sources/day15/main.swift: -------------------------------------------------------------------------------- 1 | func coveredXRanges(beacons: [(Int, Int, Int)], y: Int) -> [(Int, Int)] { 2 | var coveredXRanges: [(Int, Int)] = [] 3 | 4 | for beacon in beacons { 5 | let yDist = abs(beacon.1 - y) 6 | let xDiff = beacon.2 - yDist 7 | 8 | if xDiff >= 0 { 9 | var beaconCovered = (beacon.0 - xDiff, beacon.0 + xDiff) 10 | 11 | var tmpRanges: [(Int, Int)] = [] 12 | for range in coveredXRanges { 13 | let b = max(range.0, beaconCovered.0) 14 | let e = min(range.1, beaconCovered.1) 15 | 16 | if b <= e { 17 | beaconCovered.0 = min(range.0, beaconCovered.0) 18 | beaconCovered.1 = max(range.1, beaconCovered.1) 19 | } else { 20 | tmpRanges.append(range) 21 | } 22 | } 23 | 24 | tmpRanges.append(beaconCovered) 25 | coveredXRanges = tmpRanges 26 | } 27 | } 28 | 29 | return coveredXRanges 30 | } 31 | 32 | func part1(beacons: [(Int, Int, Int)]) -> Int { 33 | return coveredXRanges(beacons: beacons, y: 2000000).map { $0.1 - $0.0 }.reduce(0, +) 34 | } 35 | 36 | func part2(beacons: [(Int, Int, Int)]) -> Int { 37 | for y in 0...4000000 { 38 | let ranges = coveredXRanges(beacons: beacons, y: y) 39 | 40 | if ranges.count > 1 { 41 | let x = ranges.sorted { $0.0 < $1.0 }[0].1 + 1 42 | return x * 4000000 + y 43 | } 44 | } 45 | 46 | return 0 47 | } 48 | 49 | var lines: [String] = [String]() 50 | 51 | while let line = readLine() { 52 | lines.append(line) 53 | } 54 | 55 | var beacons: [(Int, Int, Int)] = lines.map { 56 | str in str.ranges(of: try! Regex("(-?[0-9]+)")).map { Int(str[$0])! } 57 | }.map { 58 | ($0[0], $0[1], abs($0[0] - $0[2]) + abs($0[1] - $0[3])) 59 | } 60 | 61 | print(part1(beacons: beacons)) 62 | print(part2(beacons: beacons)) 63 | -------------------------------------------------------------------------------- /2022/Sources/day18/main.swift: -------------------------------------------------------------------------------- 1 | let dirs = [(1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)] 2 | 3 | func inBounds(grid: [[[Int]]], coord: (Int, Int, Int)) -> Bool { 4 | coord.0 >= 0 && coord.0 < grid.count && 5 | coord.1 >= 0 && coord.1 < grid[0].count && 6 | coord.2 >= 0 && coord.2 < grid[0][0].count 7 | } 8 | 9 | func calculateSurfaceArea(_ grid: [[[Int]]]) -> Int { 10 | let (mx, my, mz) = (grid.count, grid[0].count, grid[0][0].count) 11 | var area = 0 12 | 13 | for x in 0.. Int { 37 | calculateSurfaceArea(grid) 38 | } 39 | 40 | func part2(grid: [[[Int]]]) -> Int { 41 | var excavatedDroplet = grid.map { $0.map { $0.map { _ in 1 } } } 42 | var q = [(0, 0, 0)] 43 | 44 | while !q.isEmpty { 45 | let (x, y, z) = q.removeFirst() 46 | 47 | if excavatedDroplet[x][y][z] != 1 { 48 | continue 49 | } 50 | 51 | excavatedDroplet[x][y][z] = 0 52 | 53 | for (dx, dy, dz) in dirs { 54 | let (nx, ny, nz) = (x + dx, y + dy, z + dz) 55 | 56 | if inBounds(grid: grid, coord: (nx, ny, nz)) && grid[nx][ny][nz] == 0 { 57 | q.append((nx, ny, nz)) 58 | } 59 | } 60 | } 61 | 62 | return calculateSurfaceArea(excavatedDroplet) 63 | } 64 | 65 | var lines: [String] = [String]() 66 | while let line = readLine() { 67 | lines.append(line) 68 | } 69 | 70 | let intCoords = lines.map { str in str.split(separator: ",").map { Int($0)! } } 71 | 72 | let maxX = intCoords.map { $0[0] }.max()! + 1 73 | let maxY = intCoords.map { $0[1] }.max()! + 1 74 | let maxZ = intCoords.map { $0[2] }.max()! + 1 75 | 76 | var grid = Array(repeating: Array(repeating: Array(repeating: 0, count: maxZ), count: maxY), count: maxX) 77 | 78 | intCoords.forEach { 79 | grid[$0[0]][$0[1]][$0[2]] = 1 80 | } 81 | 82 | print(part1(grid: grid)) 83 | print(part2(grid: grid)) 84 | -------------------------------------------------------------------------------- /2022/Sources/day2/main.swift: -------------------------------------------------------------------------------- 1 | func calculateScore(s1: UInt32, s2: UInt32) -> UInt32 { 2 | var score = s2 + 1 3 | 4 | if s1 == s2 { 5 | score += 3 6 | } else if (s2 > s1 && (s1 != 0 || s2 != 2)) || (s2 == 0 && s1 == 2) { 7 | score += 6 8 | } 9 | 10 | return score 11 | } 12 | 13 | func part1(lines: [String]) -> UInt32 { 14 | lines.map { line in 15 | let signs = line.split(separator: " ") 16 | 17 | let s1 = signs[0].unicodeScalars.first!.value - UnicodeScalar("A").value 18 | let s2 = signs[1].unicodeScalars.first!.value - UnicodeScalar("X").value 19 | 20 | return calculateScore(s1: s1, s2: s2) 21 | }.reduce(0, +) 22 | } 23 | 24 | func part2(lines: [String]) -> UInt32 { 25 | lines.map { line in 26 | let signs = line.split(separator: " ") 27 | 28 | let s1 = signs[0].unicodeScalars.first!.value - UnicodeScalar("A").value 29 | var s2: UInt32 = 0 30 | 31 | switch signs[1] { 32 | case "X": 33 | s2 = s1 == 0 ? 2 : s1 - 1 34 | case "Y": 35 | s2 = s1 36 | case "Z": 37 | s2 = (s1 + 1) % 3 38 | 39 | default: 40 | assert(false, "unreachable") 41 | } 42 | 43 | return calculateScore(s1: s1, s2: s2) 44 | }.reduce(0, +) 45 | } 46 | 47 | var lines: [String] = [String]() 48 | 49 | while let line = readLine() { 50 | lines.append(line) 51 | } 52 | 53 | print(part1(lines: lines)) 54 | print(part2(lines: lines)) 55 | -------------------------------------------------------------------------------- /2022/Sources/day20/main.swift: -------------------------------------------------------------------------------- 1 | func solve(numbers: [Int], decryptionKey: Int, mixTimes: Int) -> Int { 2 | let numbers = numbers.map({n in n * decryptionKey}) 3 | var workingNumbers = numbers.enumerated().reduce([], {(arr, e) in arr + [[e.offset, e.element]]}) 4 | 5 | for _ in 0 ..< mixTimes { 6 | for (i, n) in numbers.enumerated() { 7 | var ptr = workingNumbers.firstIndex(of: [i, n])! 8 | 9 | for _ in 0 ..< abs(n % (numbers.count - 1)) { 10 | let tmp = ptr + (n < 0 ? -1 : 1) 11 | let other = tmp < 0 ? workingNumbers.count - 1 : tmp % workingNumbers.count 12 | 13 | workingNumbers.swapAt(ptr, other) 14 | ptr = other 15 | } 16 | } 17 | } 18 | 19 | let idx = workingNumbers.firstIndex(of: [numbers.firstIndex(of: 0)!, 0])! 20 | return [1000, 2000, 3000].reduce(0, {(r, n) in r + workingNumbers[(idx + n) % workingNumbers.count][1]}) 21 | } 22 | 23 | func part1(_ numbers: [Int]) -> Int { 24 | solve(numbers: numbers, decryptionKey: 1, mixTimes: 1) 25 | } 26 | 27 | func part2(_ numbers: [Int]) -> Int { 28 | solve(numbers: numbers, decryptionKey: 811589153, mixTimes: 10) 29 | } 30 | 31 | var numbers = [Int]() 32 | while let line = readLine() { 33 | numbers.append(Int(line)!) 34 | } 35 | 36 | print(part1(numbers)) 37 | print(part2(numbers)) 38 | -------------------------------------------------------------------------------- /2022/Sources/day23/main.swift: -------------------------------------------------------------------------------- 1 | struct Coord : Hashable { 2 | let r: Int 3 | let c: Int 4 | } 5 | 6 | func solve(_ map: [[Character]], _ part2: Bool) -> Int { 7 | var elves = Set() 8 | for (r, row) in map.enumerated() { 9 | for (c, col) in row.enumerated() { 10 | if col == "#" { 11 | elves.insert(Coord(r: r, c: c)) 12 | } 13 | } 14 | } 15 | 16 | // [NE, N, NW, W, SW, S, SE, E] 17 | let dirs = [(-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1), (0, 1)] 18 | let dirIndices = [1, 5, 3, 7] 19 | var firstMoveIdx = 0 20 | 21 | var round = 1 22 | while round <= 10 || part2 { 23 | var proposedMoves = [Coord : [Coord]]() 24 | for elf in elves { 25 | let isFieldEmpty = {(vR, vC) in !elves.contains(Coord(r: elf.r + vR, c: elf.c + vC))} 26 | 27 | let hasNoAdjacentElf = dirs.allSatisfy(isFieldEmpty) 28 | if hasNoAdjacentElf { 29 | proposedMoves[elf] = [elf] 30 | continue 31 | } 32 | 33 | for n in (0..<4) { 34 | let dirIdx = dirIndices[(firstMoveIdx + n) % 4] 35 | if [dirIdx - 1, dirIdx, (dirIdx + 1) % dirs.count].map({i in dirs[i]}).allSatisfy(isFieldEmpty) { 36 | let (vR, vC) = dirs[dirIdx] 37 | let move = Coord(r: elf.r + vR, c: elf.c + vC) 38 | 39 | let elvesProposedTheSameMove = proposedMoves[move] 40 | proposedMoves[move] = (elvesProposedTheSameMove == nil ? [] : elvesProposedTheSameMove!) + [elf] 41 | break 42 | } 43 | 44 | if(n == 3) { 45 | proposedMoves[elf] = [elf] 46 | } 47 | } 48 | } 49 | 50 | let newElves = Set(proposedMoves.flatMap({(dest, elves) in elves.count == 1 ? [dest] : elves})) 51 | if(newElves == elves) { 52 | return round 53 | } 54 | 55 | firstMoveIdx = (firstMoveIdx + 1) % dirIndices.count 56 | elves = newElves 57 | round += 1 58 | } 59 | 60 | let rowCoords = elves.map({c in c.r}) 61 | let colCoords = elves.map({c in c.c}) 62 | 63 | return (rowCoords.max()! - rowCoords.min()! + 1) * (colCoords.max()! - colCoords.min()! + 1) - elves.count 64 | } 65 | 66 | func part1(_ map: [[Character]]) -> Int { 67 | solve(map, false) 68 | } 69 | 70 | func part2(_ map: [[Character]]) -> Int { 71 | solve(map, true) 72 | } 73 | 74 | var map = [[Character]]() 75 | while let line = readLine() { 76 | map.append(Array(line)) 77 | } 78 | 79 | print(part1(map)) 80 | print(part2(map)) 81 | -------------------------------------------------------------------------------- /2022/Sources/day25/main.swift: -------------------------------------------------------------------------------- 1 | func addSNAFU(lhs: [Character], rhs: [Character]) -> [Character] { 2 | let digits: [Character] = ["=", "-", "0", "1", "2"] 3 | let values = [-2, -1, 0, 1, 2] 4 | 5 | var rLhs = Array(lhs.reversed()) 6 | var rRhs = Array(rhs.reversed()) 7 | 8 | if rLhs.count < rRhs.count { 9 | swap(&rLhs, &rRhs) 10 | } 11 | 12 | var idx = 0 13 | var carry = 0 14 | var sum = "" 15 | 16 | while idx < rLhs.count { 17 | let lIdx = digits.firstIndex(of: rLhs[idx])! 18 | let rVal = idx >= rRhs.count ? 0 : values[digits.firstIndex(of: rRhs[idx])!] 19 | 20 | let nIdx = lIdx + rVal + carry 21 | carry = nIdx < 0 ? -1 : (nIdx >= digits.count ? 1 : 0) 22 | 23 | let mod = nIdx % digits.count 24 | let sIdx = mod < 0 ? digits.count + mod : mod 25 | sum.append(digits[sIdx]) 26 | 27 | idx += 1 28 | } 29 | 30 | if carry == 1 { 31 | sum.append("1") 32 | } 33 | 34 | return sum.reversed() 35 | } 36 | 37 | func part1(_ numbers: [[Character]]) -> String { 38 | String(numbers.reduce(["0"], addSNAFU)) 39 | } 40 | 41 | func part2() -> Int { 42 | 0 43 | } 44 | 45 | var numbers = [[Character]]() 46 | while let line = readLine() { 47 | numbers.append(Array(line)) 48 | } 49 | 50 | print(part1(numbers)) 51 | print(part2()) -------------------------------------------------------------------------------- /2022/Sources/day3/main.swift: -------------------------------------------------------------------------------- 1 | func calculateScore(c: Character) -> Int { 2 | switch c { 3 | case "a"..."z": 4 | return Int(c.unicodeScalars.first!.value - UnicodeScalar("a").value + 1) 5 | case "A"..."Z": 6 | return Int(c.unicodeScalars.first!.value - UnicodeScalar("A").value + 27) 7 | default: 8 | assertionFailure() 9 | return 0 10 | } 11 | } 12 | 13 | func part1(lines: [String]) -> Int { 14 | lines.map { line in 15 | let s1 = Set(line.prefix(line.count / 2)) 16 | let s2 = Set(line.suffix(line.count / 2)) 17 | 18 | let intersection = s1.intersection(s2) 19 | assert(intersection.count == 1) 20 | 21 | return calculateScore(c: intersection.first!) 22 | }.reduce(0, +) 23 | } 24 | 25 | func part2(lines: [String]) -> Int { 26 | stride(from: 0, to: lines.count, by: 3).map { i in 27 | let s1 = Set(lines[i]) 28 | let s2 = Set(lines[i+1]) 29 | let s3 = Set(lines[i+2]) 30 | 31 | let intersection = s1.intersection(s2).intersection(s3) 32 | assert(intersection.count == 1) 33 | 34 | return calculateScore(c: intersection.first!) 35 | }.reduce(0,+) 36 | } 37 | 38 | var lines: [String] = [String]() 39 | 40 | while let line = readLine() { 41 | lines.append(line) 42 | } 43 | 44 | print(part1(lines: lines)) 45 | print(part2(lines: lines)) 46 | -------------------------------------------------------------------------------- /2022/Sources/day4/main.swift: -------------------------------------------------------------------------------- 1 | func part1(ranges: [(ClosedRange, ClosedRange)]) -> Int { 2 | ranges.map { (r1, r2) in 3 | (r1.contains(r2) || r2.contains(r1)) ? 1 : 0 4 | }.reduce(0, +) 5 | } 6 | 7 | func part2(ranges: [(ClosedRange, ClosedRange)]) -> Int { 8 | ranges.map { (r1, r2) in 9 | r1.overlaps(r2) ? 1 : 0 10 | }.reduce(0, +) 11 | } 12 | 13 | var lines: [String] = [String]() 14 | 15 | while let line = readLine() { 16 | lines.append(line) 17 | } 18 | 19 | let ranges = lines.map { line in 20 | let nums = line.ranges(of: try! Regex("[0-9]+")).map { Int(line[$0])! } 21 | 22 | return (nums[0]...nums[1], nums[2]...nums[3]) 23 | } 24 | 25 | print(part1(ranges: ranges)) 26 | print(part2(ranges: ranges)) 27 | -------------------------------------------------------------------------------- /2022/Sources/day5/main.swift: -------------------------------------------------------------------------------- 1 | func part1(_ crates: [[Character]], _ instructions: [[Int]]) -> String { 2 | var newCrates = crates 3 | 4 | // inst: [repeat, from, to], indexed from 1 5 | for inst in instructions { 6 | for _ in 0.. String { 15 | var newCrates = crates 16 | 17 | // inst: [repeat, from, to], indexed from 1 18 | for inst in instructions { 19 | var tmp = [Character]() 20 | 21 | for _ in 0.. Int { 2 | var window = line.prefix(distinct) 3 | 4 | var n = distinct; 5 | while true { 6 | 7 | if Set(window).count == distinct { 8 | return n; 9 | } 10 | 11 | let _ = window.popFirst() 12 | window.append(line[line.index(line.startIndex, offsetBy: n)]) 13 | 14 | if(n >= line.count) { 15 | return -1 16 | } 17 | 18 | n += 1 19 | } 20 | } 21 | 22 | func part1(_ line: String) -> Int { 23 | eatUntilNDistinct(line, 4) 24 | } 25 | 26 | func part2(_ line: String) -> Int { 27 | eatUntilNDistinct(line, 14) 28 | } 29 | 30 | let line = readLine()! 31 | 32 | print(part1(line)) 33 | print(part2(line)) 34 | -------------------------------------------------------------------------------- /2022/Sources/day7/main.swift: -------------------------------------------------------------------------------- 1 | class Directory { 2 | let name: String 3 | let parent: Directory? 4 | 5 | var fileSize: UInt64 = 0 6 | var subdirs: [Directory] = [] 7 | 8 | init(name: String, parent: Directory?) { 9 | self.name = name 10 | self.parent = parent 11 | } 12 | 13 | func size() -> UInt64 { 14 | subdirs.reduce(0, { $0 + $1.size() }) + fileSize 15 | } 16 | } 17 | 18 | func parseFileSystem(_ commands: [String]) -> Directory { 19 | let root = Directory(name: "/", parent: nil) 20 | 21 | var curDir = root 22 | for command in commands { 23 | let split = command.split(separator: " ") 24 | 25 | switch split[0] { 26 | case "$": 27 | if split[1] == "cd" { 28 | if split[2] == ".." { 29 | curDir = curDir.parent! 30 | } else { 31 | curDir = curDir.subdirs.first { $0.name == split[2] } ?? curDir 32 | } 33 | } 34 | case "dir": 35 | curDir.subdirs.append(Directory(name: String(split[1]), parent: curDir)) 36 | default: 37 | curDir.fileSize += UInt64(split[0])! 38 | } 39 | } 40 | 41 | return root 42 | } 43 | 44 | func part1(_ dir: Directory) -> UInt64 { 45 | var size: UInt64 = dir.size() < 100000 ? dir.size() : 0 46 | 47 | for subdir in dir.subdirs { 48 | size += part1(subdir) 49 | } 50 | 51 | return size 52 | } 53 | 54 | func part2(_ dir: Directory, _ limit: UInt64) -> UInt64 { 55 | var size: UInt64 = dir.size() > limit ? dir.size() : 70000000 56 | 57 | for subdir in dir.subdirs { 58 | size = min(size, part2(subdir, limit)) 59 | } 60 | 61 | return size 62 | } 63 | 64 | var lines: [String] = [String]() 65 | 66 | while let line = readLine() { 67 | lines.append(line) 68 | } 69 | 70 | let root = parseFileSystem(lines) 71 | 72 | print(part1(root)) 73 | print(part2(root, 30000000 - (70000000 - root.size()))) 74 | -------------------------------------------------------------------------------- /2022/Sources/day8/main.swift: -------------------------------------------------------------------------------- 1 | let dirs = [(-1, 0), (1, 0), (0, -1), (0, 1)] 2 | 3 | func walk(grid: [[Int]], elem: (Int, Int), dir: (Int, Int)) -> (Bool, Int) { 4 | var (r, c) = elem 5 | let (vr, vc) = dir 6 | 7 | var steps = 0 8 | while true { 9 | r = r + vr 10 | c = c + vc 11 | 12 | if r < 0 || r >= grid.count || c < 0 || c >= grid.count { 13 | return (true, steps) 14 | } 15 | 16 | steps += 1 17 | 18 | if grid[r][c] >= grid[elem.0][elem.1] { 19 | return (false, steps) 20 | } 21 | } 22 | } 23 | 24 | func part1(_ grid: [[Int]]) -> Int { 25 | var visible = 0 26 | 27 | for (r, row) in grid.enumerated() { 28 | for (c, _) in row.enumerated() { 29 | visible += dirs.contains { walk(grid: grid, elem: (r, c), dir: $0).0 } ? 1 : 0 30 | } 31 | } 32 | 33 | return visible 34 | } 35 | 36 | func part2(_ grid: [[Int]]) -> Int { 37 | var highest = 0 38 | 39 | for (r, row) in grid.enumerated() { 40 | for (c, _) in row.enumerated() { 41 | highest = max(highest, dirs.map { walk(grid: grid, elem: (r, c), dir: $0).1 }.reduce(1, *)) 42 | } 43 | } 44 | 45 | return highest 46 | } 47 | 48 | var grid: [[Int]] = [] 49 | 50 | while let line = readLine() { 51 | grid.append(line.map { $0.wholeNumberValue! }) 52 | } 53 | 54 | print(part1(grid)) 55 | print(part2(grid)) 56 | -------------------------------------------------------------------------------- /2022/Sources/day9/main.swift: -------------------------------------------------------------------------------- 1 | struct Position : Hashable { 2 | var x: Int 3 | var y: Int 4 | } 5 | 6 | func solve(movements: [String], ropeLength: Int) -> Int { 7 | var knots = Array(repeating: Position(x: 0, y: 0), count: ropeLength) 8 | var uniqueTailPositions = Set() 9 | 10 | let dirs = ["R": (1, 0), "L": (-1, 0), "D": (0, -1), "U": (0, 1)] 11 | 12 | for movement in movements { 13 | let split = movement.split(separator: " ") 14 | 15 | let (dx, dy) = dirs[String(split[0])]! 16 | let cnt = Int(split[1])! 17 | 18 | for _ in 0..= 2 || abs(newParentPos.y - currentPos.y) >= 2 { 27 | if newParentPos.x != currentPos.x { 28 | newPos.x += newParentPos.x > knots[i].x ? 1 : -1 29 | } 30 | 31 | if newParentPos.y != currentPos.y { 32 | newPos.y += newParentPos.y > knots[i].y ? 1 : -1 33 | } 34 | } 35 | 36 | newKnots.append(newPos) 37 | } 38 | 39 | knots = newKnots 40 | uniqueTailPositions.insert(knots.last!) 41 | } 42 | } 43 | 44 | return uniqueTailPositions.count 45 | } 46 | 47 | func part1(lines: [String]) -> Int { 48 | solve(movements: lines, ropeLength: 2) 49 | } 50 | 51 | func part2(lines: [String]) -> Int { 52 | solve(movements: lines, ropeLength: 10) 53 | } 54 | 55 | var lines: [String] = [String]() 56 | 57 | while let line = readLine() { 58 | lines.append(line) 59 | } 60 | 61 | print(part1(lines: lines)) 62 | print(part2(lines: lines)) 63 | -------------------------------------------------------------------------------- /2023/c++/.clangd: -------------------------------------------------------------------------------- 1 | CompileFlags: 2 | Add: [-std=c++20] 3 | -------------------------------------------------------------------------------- /2023/c++/day1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | bool isDigit(char c) { return c >= '0' && c <= '9'; }; 10 | 11 | size_t part1(const std::vector &lines) { 12 | auto lineToSum = [](const std::string &line) { 13 | auto view = line // 14 | | std::ranges::views::filter(isDigit) // 15 | | std::ranges::views::transform([](char c) { return c - '0'; }); 16 | 17 | return 10 * view.front() + view.back(); 18 | }; 19 | 20 | auto partialSums = lines | std::ranges::views::transform(lineToSum); 21 | return std::accumulate(partialSums.begin(), partialSums.end(), 22 | static_cast(0)); 23 | } 24 | 25 | size_t part2(const std::vector &lines) { 26 | static const std::map keywords = { 27 | {"one", 1}, {"two", 2}, {"three", 3}, {"four", 4}, {"five", 5}, 28 | {"six", 6}, {"seven", 7}, {"eight", 8}, {"nine", 9}}; 29 | 30 | size_t sum = 0; 31 | for (auto &&line : lines) { 32 | std::vector digits; 33 | 34 | size_t i = 0; 35 | auto indexedView = line | std::ranges::views::transform([&](char c) { 36 | return std::make_pair(c, i++); 37 | }); 38 | 39 | for (auto &&[c, idx] : indexedView) { 40 | if (isDigit(c)) { 41 | digits.emplace_back(c - '0'); 42 | continue; 43 | } 44 | 45 | for (auto n : {3, 4, 5}) { 46 | const auto &substr = line.substr(idx, n); 47 | 48 | if (keywords.count(substr)) { 49 | digits.emplace_back(keywords.at(substr)); 50 | break; 51 | } 52 | } 53 | } 54 | 55 | sum += 10 * digits.front() + digits.back(); 56 | } 57 | 58 | return sum; 59 | }; 60 | 61 | int main() { 62 | std::vector lines; 63 | 64 | std::string line; 65 | while (std::getline(std::cin, line)) { 66 | lines.emplace_back(line); 67 | } 68 | 69 | std::cout << part1(lines) << '\n'; 70 | std::cout << part2(lines) << '\n'; 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /2023/c++/day11.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | size_t part1(const std::vector &grid) { 8 | std::vector> galaxies; 9 | std::set occupiedCols; 10 | std::set occupiedRows; 11 | 12 | for (size_t i = 0; i < grid.size(); ++i) { 13 | for (size_t j = 0; j < grid[i].size(); ++j) { 14 | if (grid[i][j] == '#') { 15 | galaxies.emplace_back(i, j); 16 | occupiedRows.emplace(i); 17 | occupiedCols.emplace(j); 18 | } 19 | } 20 | } 21 | 22 | size_t totalDist = 0; 23 | for (size_t i = 0; i < galaxies.size(); ++i) { 24 | for (size_t j = i + 1; j < galaxies.size(); ++j) { 25 | auto [a, b] = galaxies[i]; 26 | auto [c, d] = galaxies[j]; 27 | 28 | if (a > c) 29 | std::swap(a, c); 30 | 31 | if (b > d) 32 | std::swap(b, d); 33 | 34 | size_t dist = c - a + d - b; 35 | 36 | for (int k = a; k < c; ++k) 37 | dist += !occupiedRows.contains(k); 38 | for (int k = b; k < d; ++k) 39 | dist += !occupiedCols.contains(k); 40 | 41 | totalDist += dist; 42 | } 43 | } 44 | 45 | return totalDist; 46 | }; 47 | 48 | size_t part2(const std::vector &grid) { 49 | std::vector> galaxies; 50 | std::set occupiedCols; 51 | std::set occupiedRows; 52 | 53 | for (size_t i = 0; i < grid.size(); ++i) { 54 | for (size_t j = 0; j < grid[i].size(); ++j) { 55 | if (grid[i][j] == '#') { 56 | galaxies.emplace_back(i, j); 57 | occupiedRows.emplace(i); 58 | occupiedCols.emplace(j); 59 | } 60 | } 61 | } 62 | 63 | size_t totalDist = 0; 64 | for (size_t i = 0; i < galaxies.size(); ++i) { 65 | for (size_t j = i + 1; j < galaxies.size(); ++j) { 66 | auto [a, b] = galaxies[i]; 67 | auto [c, d] = galaxies[j]; 68 | 69 | if (a > c) 70 | std::swap(a, c); 71 | 72 | if (b > d) 73 | std::swap(b, d); 74 | 75 | size_t dist = c - a + d - b; 76 | 77 | for (int k = a; k < c; ++k) { 78 | if (!occupiedRows.contains(k)) 79 | dist += 1e6 - 1; 80 | } 81 | 82 | for (int k = b; k < d; ++k) { 83 | if (!occupiedCols.contains(k)) 84 | dist += 1e6 - 1; 85 | } 86 | 87 | totalDist += dist; 88 | } 89 | } 90 | 91 | return totalDist; 92 | }; 93 | 94 | int main() { 95 | 96 | std::vector grid; 97 | std::string line; 98 | 99 | while (std::getline(std::cin, line)) 100 | grid.emplace_back(std::move(line)); 101 | 102 | std::cout << part1(grid) << '\n'; 103 | std::cout << part2(grid) << '\n'; 104 | 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /2023/c++/day15.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | size_t part1(const std::string &sequence) { 7 | size_t res = 0; 8 | 9 | for (auto &&s : sequence | std::views::split(',')) { 10 | std::string_view sv(&s.front(), std::ranges::distance(s)); 11 | 12 | size_t hash = 0; 13 | for (auto &&c : sv) { 14 | hash += c; 15 | hash *= 17; 16 | hash %= 256; 17 | } 18 | res += hash; 19 | } 20 | 21 | return res; 22 | }; 23 | 24 | size_t part2(const std::string &sequence) { 25 | std::vector> boxes[256]; 26 | 27 | for (auto &&s : sequence | std::views::split(',')) { 28 | std::string_view sv(&s.front(), std::ranges::distance(s)); 29 | 30 | char delim = sv.back() == '-' ? '-' : '='; 31 | auto labelSplit = std::views::split(sv, delim).front(); 32 | auto label = std::string_view(&labelSplit.front(), 33 | std::ranges::distance(labelSplit)); 34 | 35 | size_t hash = 0; 36 | for (auto &&c : label) { 37 | hash += c; 38 | hash *= 17; 39 | hash %= 256; 40 | } 41 | 42 | auto &box = boxes[hash]; 43 | 44 | auto it = box.begin(); 45 | while (it != box.end() && it->first != label) 46 | ++it; 47 | 48 | if (sv.back() == '-') { 49 | if (it != box.end()) 50 | box.erase(it); 51 | 52 | continue; 53 | } 54 | 55 | int val = sv.back() - '0'; 56 | if (it != box.end()) 57 | it->second = val; 58 | else 59 | box.emplace_back(label, val); 60 | } 61 | 62 | size_t total = 0; 63 | for (auto &&i : std::views::iota(0, 256)) { 64 | if (boxes[i].empty()) 65 | continue; 66 | 67 | for (auto &&j : std::views::iota(0U, boxes[i].size())) 68 | total += (i + 1) * (j + 1) * boxes[i][j].second; 69 | } 70 | 71 | return total; 72 | }; 73 | 74 | int main() { 75 | 76 | std::string line; 77 | std::getline(std::cin, line); 78 | 79 | std::cout << part1(line) << '\n'; 80 | std::cout << part2(line) << '\n'; 81 | 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /2023/c++/day2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct CubeCount { 7 | int red{}, green{}, blue{}; 8 | 9 | void setDefaults() { 10 | red = 12; 11 | green = 13; 12 | blue = 14; 13 | } 14 | 15 | bool isValid() { return red >= 0 && green >= 0 && blue >= 0; } 16 | }; 17 | 18 | int part1(const std::string &line) { 19 | std::stringstream ss(line); 20 | 21 | // Game X: 22 | int id = 0; 23 | std::string str; 24 | ss >> str >> id; 25 | ss.get(); 26 | 27 | CubeCount cc; 28 | cc.setDefaults(); 29 | 30 | int n = 0; 31 | while (ss >> n >> str) { 32 | if (str.starts_with("red")) { 33 | cc.red -= n; 34 | } else if (str.starts_with("green")) { 35 | cc.green -= n; 36 | } else if (str.starts_with("blue")) { 37 | cc.blue -= n; 38 | } 39 | 40 | if (str.back() == ';' || str.back() != ',') { 41 | if (!cc.isValid()) { 42 | return 0; 43 | } 44 | 45 | cc.setDefaults(); 46 | } 47 | } 48 | 49 | return id; 50 | }; 51 | 52 | int part2(const std::string &line) { 53 | 54 | std::stringstream ss(line); 55 | 56 | CubeCount cc; 57 | int n = 0; 58 | std::string str; 59 | 60 | ss >> str >> n; 61 | ss.get(); 62 | 63 | while (ss >> n >> str) { 64 | if (str.starts_with("red")) { 65 | cc.red = std::max(cc.red, n); 66 | } else if (str.starts_with("blue")) { 67 | cc.blue = std::max(cc.blue, n); 68 | } else if (str.starts_with("green")) { 69 | cc.green = std::max(cc.green, n); 70 | } 71 | } 72 | 73 | return cc.red * cc.green * cc.blue; 74 | } 75 | 76 | int main() { 77 | 78 | int part1Sum = 0; 79 | int part2Sum = 0; 80 | 81 | std::string line; 82 | while (std::getline(std::cin, line)) { 83 | part1Sum += part1(line); 84 | part2Sum += part2(line); 85 | } 86 | 87 | std::cout << part1Sum << '\n'; 88 | std::cout << part2Sum << '\n'; 89 | 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /2023/c++/day21.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | size_t getReachedPlotCount(const std::vector &garden, 9 | size_t steps) { 10 | std::pair start; 11 | for (size_t r = 0; r < garden.size(); ++r) { 12 | if (auto c = garden[r].find('S'); c != std::string::npos) { 13 | start = {r, c}; 14 | break; 15 | } 16 | } 17 | 18 | std::set> visited; 19 | std::queue> q; 20 | 21 | q.emplace(start); 22 | q.emplace(-1, -1); 23 | 24 | size_t step = 0; 25 | while (!q.empty()) { 26 | auto [r, c] = q.front(); 27 | q.pop(); 28 | 29 | if (r == -1 && c == -1) { 30 | if (step == steps) 31 | break; 32 | 33 | ++step; 34 | visited.clear(); 35 | 36 | if (!q.empty()) 37 | q.emplace(-1, -1); 38 | 39 | continue; 40 | } 41 | 42 | if (!visited.emplace(r, c).second) 43 | continue; 44 | 45 | const static std::pair dirs[] = { 46 | {1, 0}, {-1, 0}, {0, -1}, {0, 1}}; 47 | 48 | for (auto &&[dr, dc] : dirs) { 49 | int nr = r + dr; 50 | int nc = c + dc; 51 | 52 | if (nr < 0 || nr >= garden.size() || nc < 0 || nc >= garden[0].size() || 53 | garden[nr][nc] == '#') 54 | continue; 55 | 56 | q.emplace(nr, nc); 57 | } 58 | } 59 | 60 | return visited.size(); 61 | } 62 | 63 | size_t part1(const std::vector &garden) { 64 | return getReachedPlotCount(garden, 64); 65 | } 66 | 67 | size_t part2(const std::vector &garden) { 68 | assert(garden.size() == 131 && garden[0].size() == 131 && 69 | "unexpected input size"); 70 | 71 | size_t n = 26501365; 72 | 73 | size_t oddCovered = getReachedPlotCount(garden, 131); 74 | size_t oddDiamond = getReachedPlotCount(garden, 65); 75 | 76 | size_t evenCovered = getReachedPlotCount(garden, 130); 77 | size_t evenDiamond = getReachedPlotCount(garden, 64); 78 | 79 | size_t repetition = (2 * n + 1) / garden[0].size(); 80 | size_t dist = (repetition - 1) / 2; 81 | 82 | size_t totalOdd = dist + 1 + (dist + 1) * dist; 83 | size_t totalEven = dist + (dist - 1) * dist; 84 | 85 | return totalOdd * oddCovered + totalEven * evenCovered - 86 | (dist + 1) * (oddCovered - oddDiamond) + 87 | dist * (evenCovered - evenDiamond); 88 | } 89 | 90 | int main() { 91 | std::vector garden; 92 | 93 | std::string buffer; 94 | while (std::getline(std::cin, buffer)) 95 | garden.emplace_back(buffer); 96 | 97 | std::cerr << part1(garden) << '\n'; 98 | std::cerr << part2(garden) << '\n'; 99 | 100 | return 0; 101 | } 102 | -------------------------------------------------------------------------------- /2023/c++/day25.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | size_t part1(const std::map> &components) { 11 | 12 | std::map node2id; 13 | for (auto &&[node, _] : components) 14 | node2id[node] = node2id.size(); 15 | 16 | std::vector> originalSubsets; 17 | std::vector> originalEdges; 18 | 19 | for (auto &&[n, es] : components) { 20 | originalSubsets.push_back({node2id[n]}); 21 | 22 | for (auto &&e : es) 23 | originalEdges.emplace_back(node2id[n], node2id[e]); 24 | } 25 | 26 | std::mt19937 mt(std::random_device{}()); 27 | while (true) { 28 | std::vector> subsets = originalSubsets; 29 | std::vector> edges = originalEdges; 30 | 31 | while (subsets.size() > 2) { 32 | int n = std::uniform_int_distribution(0, edges.size() - 1)(mt); 33 | 34 | auto [b, e] = edges[n]; 35 | edges.erase(edges.begin() + n); 36 | 37 | int s1 = -1; 38 | int s2 = -1; 39 | 40 | for (auto i : std::views::iota(0U, subsets.size())) { 41 | if (subsets[i].contains(b)) 42 | s1 = i; 43 | if (subsets[i].contains(e)) 44 | s2 = i; 45 | } 46 | 47 | if (s1 == s2) 48 | continue; 49 | 50 | subsets[s1].insert(subsets[s2].begin(), subsets[s2].end()); 51 | subsets.erase(subsets.begin() + s2); 52 | } 53 | 54 | size_t cuts = 0; 55 | for (auto &&[b, e] : originalEdges) 56 | if (subsets[0].contains(b) && subsets[1].contains(e)) 57 | ++cuts; 58 | 59 | if (cuts == 3) 60 | return subsets[0].size() * subsets[1].size(); 61 | } 62 | } 63 | 64 | size_t part2() { return 0; } 65 | 66 | int main() { 67 | std::map> components; 68 | 69 | std::string buffer; 70 | while (std::getline(std::cin, buffer)) { 71 | std::stringstream ss(buffer); 72 | ss >> buffer; 73 | 74 | buffer.pop_back(); 75 | std::string head = buffer; 76 | 77 | while (ss >> buffer) { 78 | components[head].emplace(buffer); 79 | components[buffer].emplace(head); 80 | } 81 | } 82 | 83 | std::cerr << part1(components) << '\n'; 84 | std::cerr << part2() << '\n'; 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /2023/c++/day4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | size_t part1(const std::string &line) { 8 | std::stringstream ss(line); 9 | std::set winningNumbers; 10 | 11 | // Game X: 12 | int id = 0; 13 | std::string str; 14 | ss >> str >> id; 15 | ss.get(); 16 | 17 | size_t score = 0; 18 | 19 | int n = 0; 20 | while (ss >> n) { 21 | winningNumbers.emplace(n); 22 | } 23 | 24 | ss.clear(); 25 | ss >> str; 26 | 27 | while (ss >> n) { 28 | if (winningNumbers.contains(n)) { 29 | score = score == 0 ? 1 : (score << 1); 30 | } 31 | } 32 | 33 | return score; 34 | }; 35 | 36 | size_t part2(const std::string &line, size_t totalLines) { 37 | std::stringstream ss(line); 38 | 39 | // Game X: 40 | int id = 0; 41 | std::string str; 42 | ss >> str >> id; 43 | ss.get(); 44 | 45 | static std::vector cache(totalLines + 1, 0); 46 | ++cache[id]; 47 | 48 | std::set winningNumbers; 49 | 50 | int n = 0; 51 | while (ss >> n) { 52 | winningNumbers.emplace(n); 53 | } 54 | 55 | ss.clear(); 56 | ss >> str; 57 | 58 | int cnt = 1; 59 | while (ss >> n) { 60 | if (winningNumbers.contains(n) && id + cnt <= totalLines) { 61 | cache[id + cnt] += cache[id]; 62 | ++cnt; 63 | } 64 | } 65 | 66 | return cache[id]; 67 | } 68 | 69 | int main() { 70 | std::vector lines; 71 | 72 | std::string line; 73 | while (std::getline(std::cin, line)) { 74 | lines.emplace_back(line); 75 | } 76 | 77 | size_t part1Sum = 0; 78 | size_t part2Sum = 0; 79 | 80 | for (auto &&line : lines) { 81 | part1Sum += part1(line); 82 | part2Sum += part2(line, lines.size()); 83 | } 84 | 85 | std::cout << part1Sum << '\n'; 86 | std::cout << part2Sum << '\n'; 87 | 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /2023/c++/day5.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | unsigned part1(const std::vector &lines) { 7 | std::stringstream ss(lines[0]); 8 | 9 | std::string dummy; 10 | ss >> dummy; 11 | 12 | std::vector nums; 13 | unsigned n = 0; 14 | while (ss >> n) { 15 | nums.emplace_back(n); 16 | } 17 | 18 | std::vector tmp = nums; 19 | for (unsigned i = 1; i < lines.size(); ++i) { 20 | ss.clear(); 21 | ss.str(lines[i]); 22 | 23 | unsigned dst = 0, src = 0, len = 0; 24 | if (!(ss >> dst >> src >> len)) { 25 | nums = tmp; 26 | ++i; 27 | continue; 28 | } 29 | 30 | for (unsigned i = 0; i < nums.size(); ++i) { 31 | auto prev = nums[i]; 32 | if (prev >= src && prev < src + len) { 33 | tmp[i] = dst + prev - src; 34 | } 35 | } 36 | } 37 | 38 | return *std::min_element(tmp.begin(), tmp.end()); 39 | }; 40 | 41 | // Brute-force 42 | unsigned part2(const std::vector &lines) { 43 | std::stringstream ss(lines[0]); 44 | 45 | std::string dummy; 46 | ss >> dummy; 47 | 48 | std::vector nums; 49 | unsigned b = 0, l = 0; 50 | while (ss >> b >> l) { 51 | 52 | std::cerr << '(' << b << ", " << l << ")\n"; 53 | 54 | for (unsigned i = 0; i < l; ++i) { 55 | nums.emplace_back(b + i); 56 | } 57 | } 58 | 59 | std::vector tmp = nums; 60 | for (unsigned i = 1; i < lines.size(); ++i) { 61 | 62 | std::cerr << lines[i] << '\n'; 63 | 64 | ss.clear(); 65 | ss.str(lines[i]); 66 | 67 | unsigned dst = 0, src = 0, len = 0; 68 | if (!(ss >> dst >> src >> len)) { 69 | nums = tmp; 70 | 71 | ++i; 72 | continue; 73 | } 74 | 75 | for (unsigned i = 0; i < nums.size(); ++i) { 76 | auto prev = nums[i]; 77 | if (prev >= src && prev < src + len) { 78 | tmp[i] = dst + prev - src; 79 | } 80 | } 81 | } 82 | 83 | return *std::min_element(tmp.begin(), tmp.end()); 84 | } 85 | 86 | int main() { 87 | std::vector lines; 88 | 89 | std::string line; 90 | while (std::getline(std::cin, line)) { 91 | lines.emplace_back(line); 92 | } 93 | 94 | std::cout << part1(lines) << '\n'; 95 | std::cout << part2(lines) << '\n'; 96 | 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /2023/c++/day6.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | size_t part1(const std::vector &lines) { 7 | std::vector times; 8 | std::vector distances; 9 | 10 | std::string category; 11 | int n = 0; 12 | 13 | std::stringstream ss(lines[0]); 14 | ss >> category; 15 | while (ss >> n) { 16 | times.emplace_back(n); 17 | } 18 | 19 | ss = std::stringstream(lines[1]); 20 | ss >> category; 21 | while (ss >> n) { 22 | distances.emplace_back(n); 23 | } 24 | 25 | size_t out = 1; 26 | for (size_t i = 0; i < times.size(); ++i) { 27 | size_t cnt = 0; 28 | 29 | for (int j = 0; j <= times[i]; ++j) { 30 | if ((times[i] - j) * j > distances[i]) { 31 | ++cnt; 32 | } 33 | } 34 | 35 | out *= cnt; 36 | } 37 | 38 | return out; 39 | }; 40 | 41 | size_t part2(const std::vector &lines) { 42 | std::vector times; 43 | std::vector distances; 44 | 45 | std::stringstream ss(lines[0]); 46 | std::string category; 47 | ss >> category; 48 | 49 | std::string time; 50 | std::string partial; 51 | while (ss >> partial) { 52 | time += partial; 53 | } 54 | 55 | ss = std::stringstream(lines[1]); 56 | ss >> category; 57 | 58 | std::string distance; 59 | while (ss >> partial) { 60 | distance += partial; 61 | } 62 | 63 | size_t out = 0; 64 | 65 | size_t t = std::stol(time); 66 | size_t d = std::stol(distance); 67 | for (int j = 0; j <= t; ++j) { 68 | if ((t - j) * j > d) { 69 | ++out; 70 | } 71 | } 72 | 73 | return out; 74 | } 75 | 76 | int main() { 77 | std::vector lines; 78 | 79 | std::string line; 80 | while (std::getline(std::cin, line)) { 81 | lines.emplace_back(line); 82 | } 83 | 84 | std::cout << part1(lines) << '\n'; 85 | std::cout << part2(lines) << '\n'; 86 | 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /2023/c++/day8.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | size_t part1(const std::vector &lines) { 11 | std::string dirs = lines[0]; 12 | std::map> m; 13 | 14 | for (auto &&line : lines | std::ranges::views::drop(2)) { 15 | std::stringstream ss(line); 16 | std::string base; 17 | std::string left; 18 | std::string right; 19 | 20 | ss >> base; 21 | // eat ' = ' 22 | ss >> left; 23 | 24 | ss >> left; 25 | ss >> right; 26 | 27 | left = left.substr(1, left.size() - 2); 28 | right.pop_back(); 29 | 30 | m[base] = {left, right}; 31 | } 32 | 33 | std::string curNode = "AAA"; 34 | size_t i = 0; 35 | size_t steps = 0; 36 | 37 | while (curNode != "ZZZ") { 38 | curNode = (dirs[i] == 'L') ? m[curNode].first : m[curNode].second; 39 | 40 | i = (i + 1) % dirs.size(); 41 | ++steps; 42 | } 43 | 44 | return steps; 45 | }; 46 | 47 | size_t part2(const std::vector &lines) { 48 | std::string dirs = lines[0]; 49 | std::map> m; 50 | std::vector curNodes; 51 | 52 | for (auto &&line : lines | std::ranges::views::drop(2)) { 53 | std::stringstream ss(line); 54 | std::string base; 55 | std::string left; 56 | std::string right; 57 | 58 | ss >> base; 59 | // eat ' = ' 60 | ss >> left; 61 | 62 | ss >> left; 63 | ss >> right; 64 | 65 | left = left.substr(1, left.size() - 2); 66 | right.pop_back(); 67 | 68 | m[base] = {left, right}; 69 | 70 | if (base[2] == 'A') { 71 | curNodes.emplace_back(base); 72 | } 73 | } 74 | 75 | size_t lcm = 1; 76 | 77 | for (auto &node : curNodes) { 78 | size_t i = 0; 79 | size_t steps = 0; 80 | 81 | while (node[2] != 'Z') { 82 | node = (dirs[i] == 'L') ? m[node].first : m[node].second; 83 | 84 | i = (i + 1) % dirs.size(); 85 | ++steps; 86 | } 87 | 88 | lcm = std::lcm(lcm, steps); 89 | } 90 | 91 | return lcm; 92 | } 93 | 94 | int main() { 95 | std::vector lines; 96 | 97 | std::string line; 98 | while (std::getline(std::cin, line)) { 99 | lines.emplace_back(line); 100 | } 101 | 102 | std::cout << part1(lines) << '\n'; 103 | std::cout << part2(lines) << '\n'; 104 | 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /2023/c++/day9.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int part1(const std::vector> &sequences) { 8 | int out = 0; 9 | for (auto &&s : sequences) 10 | out += s.back(); 11 | 12 | return out; 13 | }; 14 | 15 | int part2(const std::vector> &sequences) { 16 | int out = 0; 17 | for (auto &&s : sequences | std::ranges::views::reverse) 18 | out = s.front() - out; 19 | 20 | return out; 21 | } 22 | 23 | int main() { 24 | 25 | int part1Sum = 0; 26 | int part2Sum = 0; 27 | 28 | std::string line; 29 | while (std::getline(std::cin, line)) { 30 | std::stringstream ss(line); 31 | 32 | std::vector> sequences(1); 33 | 34 | int n = 0; 35 | while (ss >> n) { 36 | sequences[0].emplace_back(n); 37 | } 38 | 39 | while (true) { 40 | std::vector tmp; 41 | bool allZeroes = true; 42 | 43 | for (size_t i = 0; i < sequences.back().size() - 1; ++i) { 44 | int diff = sequences.back()[i + 1] - sequences.back()[i]; 45 | allZeroes &= diff == 0; 46 | tmp.emplace_back(diff); 47 | } 48 | 49 | if (allZeroes) 50 | break; 51 | 52 | sequences.emplace_back(std::move(tmp)); 53 | } 54 | 55 | part1Sum += part1(sequences); 56 | part2Sum += part2(sequences); 57 | } 58 | 59 | std::cout << part1Sum << '\n'; 60 | std::cout << part2Sum << '\n'; 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /2023/mojo/day1.🔥: -------------------------------------------------------------------------------- 1 | from python import Python 2 | 3 | 4 | fn assignFirstLast(inout first: Int, inout last: Int, val: Int): 5 | if first == -1: 6 | first = val 7 | else: 8 | last = val 9 | 10 | 11 | fn part1(line: String) -> Int: 12 | var first = -1 13 | var last = -1 14 | 15 | for i in range(len(line)): 16 | let asciiVal = ord(line[i]) 17 | 18 | if isdigit(asciiVal): 19 | assignFirstLast(first, last, asciiVal - ord("0")) 20 | 21 | if last == -1: 22 | last = first 23 | 24 | return 10 * first + last 25 | 26 | 27 | fn getKeywordValue(s: String) -> Int: 28 | if s == "one": 29 | return 1 30 | if s == "two": 31 | return 2 32 | if s == "three": 33 | return 3 34 | if s == "four": 35 | return 4 36 | if s == "five": 37 | return 5 38 | if s == "six": 39 | return 6 40 | if s == "seven": 41 | return 7 42 | if s == "eight": 43 | return 8 44 | if s == "nine": 45 | return 9 46 | return -1 47 | 48 | 49 | fn part2(line: String) -> Int: 50 | var first = -1 51 | var last = -1 52 | 53 | for i in range(len(line)): 54 | let asciiVal = ord(line[i]) 55 | 56 | if isdigit(asciiVal): 57 | assignFirstLast(first, last, asciiVal - ord("0")) 58 | continue 59 | 60 | for n in range(3, 6): 61 | let keywordVal = getKeywordValue(line[i : i + n]) 62 | if keywordVal != -1: 63 | assignFirstLast(first, last, keywordVal) 64 | 65 | if last == -1: 66 | last = first 67 | 68 | return 10 * first + last 69 | 70 | 71 | def main(): 72 | var py = Python() 73 | let sys = py.import_module("sys") 74 | 75 | var part1Sum = 0 76 | var part2Sum = 0 77 | for line in sys.stdin: 78 | part1Sum += part1(py.__str__(line)) 79 | part2Sum += part2(py.__str__(line)) 80 | 81 | print(part1Sum) 82 | print(part2Sum) 83 | -------------------------------------------------------------------------------- /2023/mojo/day15.🔥: -------------------------------------------------------------------------------- 1 | from collections.vector import DynamicVector 2 | 3 | 4 | # Not generic, to prevent a lifetime analysis crash. 5 | @value 6 | struct Pair(CollectionElement): 7 | var a: String 8 | var b: Int 9 | 10 | 11 | fn filter(v: DynamicVector[Pair], elem: String) -> DynamicVector[Pair]: 12 | var tmp = DynamicVector[Pair]() 13 | 14 | for i in range(0, len(v)): 15 | if v[i].a != elem: 16 | tmp.push_back(v[i]) 17 | 18 | return tmp 19 | 20 | 21 | fn part1(line: String) raises -> Int: 22 | var res = 0 23 | 24 | let steps = line.split(",") 25 | for i in range(0, len(steps)): 26 | var hash = 0 27 | for j in range(0, len(steps[i])): 28 | hash += int(steps[i]._buffer[j]) 29 | hash *= 17 30 | hash %= 256 31 | res += hash 32 | 33 | return res 34 | 35 | 36 | fn part2(line: String) raises -> Int: 37 | var boxes = DynamicVector[DynamicVector[Pair]]() 38 | boxes.resize(256, DynamicVector[Pair]()) 39 | 40 | let steps = line.split(",") 41 | for i in range(0, len(steps)): 42 | let cur = steps[i] 43 | let rm = cur[len(cur) - 1] == "-" 44 | 45 | let delim: String 46 | if rm: 47 | delim = "-" 48 | else: 49 | delim = "=" 50 | 51 | let label = cur.split(delim)[0] 52 | 53 | var hash = 0 54 | for j in range(0, len(label)): 55 | hash += int(label._buffer[j]) 56 | hash *= 17 57 | hash %= 256 58 | 59 | if rm: 60 | boxes[hash] = filter(boxes[hash], label) 61 | continue 62 | 63 | let val = atol(cur[len(cur) - 1]) 64 | for j in range(0, len(boxes[hash])): 65 | if boxes[hash][j].a == label: 66 | boxes[hash][j].b = val 67 | break 68 | else: 69 | boxes[hash].push_back(Pair(label, val)) 70 | 71 | var total = 0 72 | for i in range(0, 256): 73 | if len(boxes[i]) == 0: 74 | continue 75 | 76 | for j in range(0, len(boxes[i])): 77 | total += (i + 1) * (j + 1) * boxes[i][j].b 78 | 79 | return total 80 | 81 | 82 | fn main() raises: 83 | with open("/dev/stdin", "r") as stdin: 84 | let input = stdin.read() 85 | 86 | print(part1(input)) 87 | print(part2(input)) 88 | -------------------------------------------------------------------------------- /2023/mojo/day4.🔥: -------------------------------------------------------------------------------- 1 | from collections.vector import DynamicVector 2 | from collections.vector import InlinedFixedVector 3 | from python import Python 4 | 5 | 6 | fn part1(line: String) raises -> Int: 7 | var py = Python() 8 | 9 | let headerSplit = PythonObject(line).split(":") 10 | 11 | let nums = headerSplit[1].split("|") 12 | var winningStr = nums[0].split(" ") 13 | var ownStr = nums[1].split("\n")[0].split(" ") 14 | 15 | var winningNums = DynamicVector[Int]() 16 | var ownNums = DynamicVector[Int]() 17 | 18 | for w in winningStr: 19 | let s = py.__str__(w) 20 | if len(s) > 0: 21 | winningNums.push_back(atol(s)) 22 | 23 | for o in ownStr: 24 | let s = py.__str__(o) 25 | if len(s) > 0: 26 | ownNums.push_back(atol(s)) 27 | 28 | var score = 0 29 | for i in range(0, len(ownNums)): 30 | for j in range(0, len(winningNums)): 31 | if ownNums[i] == winningNums[j]: 32 | if score == 0: 33 | score = 1 34 | else: 35 | score <<= 1 36 | 37 | return score 38 | 39 | 40 | fn part2(line: String, inout cache: InlinedFixedVector[Int]) raises -> Int: 41 | var py = Python() 42 | 43 | let headerSplit = PythonObject(line).split(":") 44 | 45 | let nums = headerSplit[1].split("|") 46 | var winningStr = nums[0].split(" ") 47 | var ownStr = nums[1].split("\n")[0].split(" ") 48 | 49 | var winningNums = DynamicVector[Int]() 50 | var ownNums = DynamicVector[Int]() 51 | 52 | for w in winningStr: 53 | let s = py.__str__(w) 54 | if len(s) > 0: 55 | winningNums.push_back(atol(s)) 56 | 57 | for o in ownStr: 58 | let s = py.__str__(o) 59 | if len(s) > 0: 60 | ownNums.push_back(atol(s)) 61 | 62 | let id = atol(py.__str__(headerSplit[0].split(" ")[-1])) 63 | cache[id] += 1 64 | 65 | var cnt = 1 66 | for i in range(0, len(ownNums)): 67 | for j in range(0, len(winningNums)): 68 | if ownNums[i] == winningNums[j] and id + cnt < len(cache): 69 | cache[id + cnt] += cache[id] 70 | cnt += 1 71 | 72 | return cache[id] 73 | 74 | 75 | fn main() raises: 76 | var py = Python() 77 | let sys = py.import_module("sys") 78 | 79 | var lines = PythonObject([]) 80 | 81 | var cnt = 0 82 | for line in sys.stdin: 83 | _ = lines.append(line) 84 | cnt += 1 85 | 86 | var part1Sum = 0 87 | var part2Sum = 0 88 | 89 | var cache = InlinedFixedVector[Int](cnt + 1) 90 | for i in range(0, cnt + 1): 91 | cache.append(0) 92 | 93 | for line in lines: 94 | part1Sum += part1(py.__str__(line)) 95 | part2Sum += part2(py.__str__(line), cache) 96 | 97 | print(part1Sum) 98 | print(part2Sum) 99 | -------------------------------------------------------------------------------- /2023/mojo/day6.🔥: -------------------------------------------------------------------------------- 1 | from math import min, max 2 | from math.limit import max_finite 3 | from python import Python 4 | from collections.vector import DynamicVector 5 | 6 | 7 | fn part1(input: String) raises -> Int: 8 | let lines = input.split("\n") 9 | 10 | var times = DynamicVector[Int]() 11 | var distances = DynamicVector[Int]() 12 | 13 | let timeStrs = lines[0].split(":")[1].split(" ") 14 | for i in range(0, len(timeStrs)): 15 | if len(timeStrs[i]) > 0: 16 | times.append(atol(timeStrs[i])) 17 | 18 | let distStrs = lines[1].split(":")[1].split(" ") 19 | for i in range(0, len(distStrs)): 20 | if len(distStrs[i]) > 0: 21 | distances.append(atol(distStrs[i])) 22 | 23 | var out = 1 24 | for i in range(0, len(times)): 25 | var cnt = 0 26 | let t = times[i] 27 | let d = distances[i] 28 | 29 | for j in range(0, t + 1): 30 | if (t - j) * j > d: 31 | cnt += 1 32 | 33 | out *= cnt 34 | 35 | return out 36 | 37 | 38 | fn part2(input: String) raises -> Int: 39 | let lines = input.split("\n") 40 | 41 | var filteredTimeStr = String() 42 | var filteredDistanceStr = String() 43 | 44 | let timeStrs = lines[0].split(":")[1].split(" ") 45 | for i in range(0, len(timeStrs)): 46 | if len(timeStrs[i]) > 0: 47 | filteredTimeStr += timeStrs[i] 48 | 49 | let distStrs = lines[1].split(":")[1].split(" ") 50 | for i in range(0, len(distStrs)): 51 | if len(distStrs[i]) > 0: 52 | filteredDistanceStr += distStrs[i] 53 | 54 | var out = 0 55 | let t = atol(filteredTimeStr) 56 | let d = atol(filteredDistanceStr) 57 | 58 | for j in range(0, t + 1): 59 | if (t - j) * j > d: 60 | out += 1 61 | 62 | return out 63 | 64 | 65 | fn main() raises: 66 | let py = Python() 67 | let sys = py.import_module("sys") 68 | 69 | let input = sys.stdin.read().__str__() 70 | 71 | print(part1(input)) 72 | print(part2(input)) 73 | -------------------------------------------------------------------------------- /2023/mojo/day8.🔥: -------------------------------------------------------------------------------- 1 | from collections.vector import DynamicVector 2 | from math import lcm 3 | 4 | 5 | @value 6 | struct Pair(CollectionElement, Stringable): 7 | var a: String 8 | var b: String 9 | 10 | fn __str__(self) -> String: 11 | return "(" + str(self.a) + ", " + str(self.b) + ")" 12 | 13 | 14 | fn part1(input: String) raises -> Int: 15 | let lines = input.split("\n") 16 | 17 | var nodes = DynamicVector[String]() 18 | var paths = DynamicVector[Pair]() 19 | 20 | for i in range(2, len(lines)): 21 | if len(lines[i]) == 0: 22 | continue 23 | 24 | let split = lines[i].split(" = ") 25 | let lr = split[1].split(", ") 26 | 27 | nodes.append(split[0]) 28 | paths.append(Pair(lr[0][1:], lr[1][:-1])) 29 | 30 | var node: String = "AAA" 31 | var i = 0 32 | var size = 0 33 | while node != "ZZZ": 34 | var nodeIdx = -1 35 | for i in range(0, len(nodes)): 36 | if nodes[i] == node: 37 | nodeIdx = i 38 | break 39 | 40 | if lines[0][i] == "L": 41 | node = paths[nodeIdx].a 42 | else: 43 | node = paths[nodeIdx].b 44 | 45 | i = (i + 1) % len(lines[0]) 46 | size += 1 47 | 48 | return size 49 | 50 | 51 | fn part2(input: String) raises -> Int: 52 | let lines = input.split("\n") 53 | 54 | var nodes = DynamicVector[String]() 55 | var paths = DynamicVector[Pair]() 56 | var starts = DynamicVector[String]() 57 | 58 | for i in range(2, len(lines)): 59 | if len(lines[i]) == 0: 60 | continue 61 | 62 | let split = lines[i].split(" = ") 63 | let lr = split[1].split(", ") 64 | 65 | nodes.append(split[0]) 66 | paths.append(Pair(lr[0][1:], lr[1][:-1])) 67 | 68 | if split[0][2] == "A": 69 | starts.append(split[0]) 70 | 71 | var out: Int = 1 72 | for si in range(0, len(starts)): 73 | var node = starts[si] 74 | var i = 0 75 | var size = 0 76 | while node[2] != "Z": 77 | var nodeIdx = -1 78 | for i in range(0, len(nodes)): 79 | if nodes[i] == node: 80 | nodeIdx = i 81 | break 82 | 83 | if lines[0][i] == "L": 84 | node = paths[nodeIdx].a 85 | else: 86 | node = paths[nodeIdx].b 87 | 88 | i = (i + 1) % len(lines[0]) 89 | size += 1 90 | out = lcm(out, size) 91 | 92 | return out 93 | 94 | 95 | fn main() raises: 96 | let input: String 97 | with open("/dev/stdin", "r") as stdin: 98 | input = stdin.read() 99 | 100 | print(part1(input)) 101 | print(part2(input)) 102 | -------------------------------------------------------------------------------- /2023/mojo/day9.🔥: -------------------------------------------------------------------------------- 1 | fn part1(sequences: DynamicVector[DynamicVector[Int]]) -> Int: 2 | var out = 0 3 | for i in range(0, len(sequences)): 4 | let vec = sequences[i] 5 | out += vec[len(vec) - 1] 6 | 7 | return out 8 | 9 | 10 | fn part2(sequences: DynamicVector[DynamicVector[Int]]) -> Int: 11 | var out = 0 12 | for i in range(len(sequences) - 1, -1, -1): 13 | out = sequences[i][0] - out 14 | 15 | return out 16 | 17 | 18 | def main(): 19 | var part1Sum = 0 20 | var part2Sum = 0 21 | 22 | with open("/dev/stdin", "r") as stdin: 23 | let input = stdin.read() 24 | let lines = input.split("\n") 25 | 26 | for i in range(0, len(lines)): 27 | if len(lines[i]) == 0: 28 | continue 29 | 30 | var sequences = DynamicVector[DynamicVector[Int]]() 31 | var s = DynamicVector[Int]() 32 | 33 | let nums = lines[i].split(" ") 34 | for j in range(0, len(nums)): 35 | s.push_back(atol(nums[j])) 36 | 37 | sequences.push_back(s ^) 38 | 39 | while True: 40 | var tmp = DynamicVector[Int]() 41 | var allZeros = True 42 | 43 | let back = sequences[len(sequences) - 1] 44 | for j in range(0, len(back) - 1): 45 | let diff = back[j + 1] - back[j] 46 | allZeros = allZeros and diff == 0 47 | tmp.push_back(diff) 48 | 49 | if allZeros: 50 | break 51 | 52 | sequences.push_back(tmp ^) 53 | 54 | part1Sum += part1(sequences) 55 | part2Sum += part2(sequences) 56 | 57 | print(part1Sum) 58 | print(part2Sum) 59 | -------------------------------------------------------------------------------- /2023/rust/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /2023/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "day1", 5 | "day2", 6 | "day3", 7 | "day4", 8 | "day5", 9 | "day6", 10 | "day7", 11 | "day8", 12 | "day9", 13 | "day10", 14 | "day11", 15 | "day12", 16 | "day13", 17 | "day14", 18 | "day15", 19 | "day16", 20 | "day17", 21 | "day18", 22 | "day19", 23 | "day20", 24 | "day21", 25 | "day22", 26 | "day23", 27 | "day24", 28 | "day25", 29 | ] 30 | resolver = "2" 31 | -------------------------------------------------------------------------------- /2023/rust/day1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day1" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day1/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, io}; 2 | 3 | fn part1(lines: &[String]) -> u32 { 4 | let line_to_sum = |line: &String| { 5 | let mut it = line 6 | .chars() 7 | .filter(|c| c.is_ascii_digit()) 8 | .map(|d| d.to_digit(10).unwrap()); 9 | 10 | let first = it.next().unwrap(); 11 | let last = it.last().unwrap_or(first); 12 | 13 | 10 * first + last 14 | }; 15 | 16 | lines.iter().map(line_to_sum).sum() 17 | } 18 | 19 | fn part2(lines: &[String]) -> u32 { 20 | let keywords = [ 21 | ("one", 1), 22 | ("two", 2), 23 | ("three", 3), 24 | ("four", 4), 25 | ("five", 5), 26 | ("six", 6), 27 | ("seven", 7), 28 | ("eight", 8), 29 | ("nine", 9), 30 | ]; 31 | 32 | let keyword_map: HashMap = keywords 33 | .into_iter() 34 | .map(|(s, i)| (String::from(s), i)) 35 | .collect(); 36 | 37 | let mut sum = 0; 38 | for line in lines { 39 | let digits: Vec = line 40 | .chars() 41 | .enumerate() 42 | .filter_map(|(idx, c)| -> Option { 43 | if c.is_ascii_digit() { 44 | return c.to_digit(10); 45 | } 46 | 47 | for n in 3..=5 { 48 | let substr: String = line.chars().skip(idx).take(n).collect(); 49 | if keyword_map.contains_key(&substr) { 50 | return Some(*keyword_map.get(&substr).unwrap()); 51 | } 52 | } 53 | 54 | None 55 | }) 56 | .collect(); 57 | sum += 10 * digits.first().unwrap() + digits.last().unwrap(); 58 | } 59 | 60 | sum 61 | } 62 | 63 | fn main() { 64 | let mut lines = Vec::::new(); 65 | 66 | let stdin = io::stdin(); 67 | for line in stdin.lines() { 68 | lines.push(line.unwrap()); 69 | } 70 | 71 | println!("{}", part1(&lines)); 72 | println!("{}", part2(&lines)); 73 | } 74 | -------------------------------------------------------------------------------- /2023/rust/day10/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day10" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day11/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day11" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day12/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day12" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | memoize = "0.4.1" 10 | -------------------------------------------------------------------------------- /2023/rust/day12/src/main.rs: -------------------------------------------------------------------------------- 1 | use memoize::memoize; 2 | use std::io; 3 | 4 | #[memoize] 5 | fn part1(line: String, groups: Vec, mut li: usize, gi: usize) -> usize { 6 | if li >= line.len() { 7 | return (gi == groups.len()) as usize; 8 | } 9 | 10 | while li < line.len() { 11 | let base_char = line.chars().nth(li).unwrap(); 12 | 13 | if base_char == '.' { 14 | li += 1; 15 | continue; 16 | } 17 | 18 | if gi == groups.len() { 19 | if base_char == '#' { 20 | return 0; 21 | } 22 | 23 | li += 1; 24 | continue; 25 | } 26 | 27 | let base_idx = li; 28 | while li < line.len() && (line.as_bytes()[li] == b'?' || line.as_bytes()[li] == b'#') { 29 | li += 1; 30 | } 31 | 32 | let next_group = groups[gi]; 33 | let can_replace = li - base_idx >= next_group 34 | && (base_idx + next_group == line.len() 35 | || line.chars().nth(base_idx + next_group).unwrap() != '#'); 36 | 37 | let replaced = if can_replace { 38 | part1( 39 | line.clone(), 40 | groups.clone(), 41 | base_idx + 1 + next_group, 42 | gi + 1, 43 | ) 44 | } else { 45 | 0 46 | }; 47 | 48 | let skipped = if base_char == '?' { 49 | part1(line, groups, base_idx + 1, gi) 50 | } else { 51 | 0 52 | }; 53 | 54 | return replaced + skipped; 55 | } 56 | 57 | (gi == groups.len()) as usize 58 | } 59 | 60 | fn main() { 61 | let mut part1_sum = 0; 62 | let mut part2_sum = 0; 63 | 64 | let stdin = io::stdin(); 65 | for line in stdin.lines() { 66 | let split = line 67 | .unwrap() 68 | .split(' ') 69 | .map(|s| s.to_string()) 70 | .collect::>(); 71 | 72 | let springs = split[0].to_string(); 73 | let groups = split[1] 74 | .split(',') 75 | .map(|s| s.parse::().unwrap()) 76 | .collect::>(); 77 | 78 | let expanded_springs = vec![&springs; 4] 79 | .into_iter() 80 | .fold(String::from(&springs), |acc, s| acc + "?" + &s); 81 | 82 | let mut expanded_groups = groups.clone(); 83 | for _ in 0..4 { 84 | expanded_groups.append(&mut groups.clone()); 85 | } 86 | 87 | part1_sum += part1(springs, groups, 0, 0); 88 | part2_sum += part1(expanded_springs, expanded_groups, 0, 0); 89 | } 90 | 91 | println!("{}", part1_sum); 92 | println!("{}", part2_sum); 93 | } 94 | -------------------------------------------------------------------------------- /2023/rust/day13/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day13" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day14/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day14" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day15/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day15" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day15/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | fn part1(line: &str) -> usize { 4 | line.split(',') 5 | .map(|s| { 6 | let mut hash: usize = 0; 7 | 8 | for b in s.as_bytes() { 9 | hash += *b as usize; 10 | hash *= 17; 11 | hash %= 256; 12 | } 13 | 14 | hash 15 | }) 16 | .sum() 17 | } 18 | 19 | fn part2(line: &str) -> usize { 20 | let mut boxes = vec![Vec::<(String, i32)>::new(); 256]; 21 | 22 | for s in line.split(',') { 23 | let last = *s.as_bytes().last().unwrap(); 24 | let rm = last == b'-'; 25 | let delim = if rm { '-' } else { '=' }; 26 | let label = s.split(delim).next().unwrap(); 27 | 28 | let mut hash: usize = 0; 29 | for b in label.as_bytes() { 30 | hash += *b as usize; 31 | hash *= 17; 32 | hash %= 256; 33 | } 34 | 35 | let b = &mut boxes[hash]; 36 | if rm { 37 | *b = b.iter().filter(|&(l, _)| l != label).cloned().collect(); 38 | continue; 39 | } 40 | 41 | let val = (last - b'0') as i32; 42 | if let Some(i) = b.iter().position(|(l, _)| l == label) { 43 | b[i].1 = val; 44 | } else { 45 | b.push((label.to_string(), val)); 46 | } 47 | } 48 | 49 | boxes 50 | .iter() 51 | .enumerate() 52 | .filter(|(_, b)| !b.is_empty()) 53 | .map(|(i, b)| { 54 | b.iter() 55 | .enumerate() 56 | .map(|(j, &(_, v))| (i + 1) * (j + 1) * (v as usize)) 57 | .sum::() 58 | }) 59 | .sum() 60 | } 61 | 62 | fn main() { 63 | let stdin = io::stdin(); 64 | 65 | for res_line in stdin.lines() { 66 | let line = res_line.unwrap(); 67 | 68 | println!("{}", part1(&line)); 69 | println!("{}", part2(&line)); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /2023/rust/day16/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day16" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day17/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day17" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day18/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day18" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day19/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day19" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day2/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cmp, 3 | io::{self}, 4 | }; 5 | 6 | struct CubeCount { 7 | red: i32, 8 | green: i32, 9 | blue: i32, 10 | } 11 | 12 | impl Default for CubeCount { 13 | fn default() -> Self { 14 | Self { 15 | red: 12, 16 | green: 13, 17 | blue: 14, 18 | } 19 | } 20 | } 21 | 22 | impl CubeCount { 23 | fn is_valid(&self) -> bool { 24 | self.red >= 0 && self.green >= 0 && self.blue >= 0 25 | } 26 | } 27 | 28 | fn part1(line: &str) -> i32 { 29 | let tokens: Vec<&str> = line.split(':').collect(); 30 | 31 | let valid = tokens[1] 32 | .split(';') 33 | .map(|s| { 34 | let mut cc = CubeCount::default(); 35 | 36 | s.split(',').for_each(|s| { 37 | let cubes: Vec<&str> = s.trim().split(' ').collect(); 38 | let n: i32 = cubes[0].parse::().unwrap(); 39 | 40 | match cubes[1] { 41 | "red" => cc.red -= n, 42 | "green" => cc.green -= n, 43 | "blue" => cc.blue -= n, 44 | _ => panic!("invalid color"), 45 | } 46 | }); 47 | 48 | cc.is_valid() 49 | }) 50 | .fold(true, |init, v| init & v); 51 | 52 | if !valid { 53 | return 0; 54 | } 55 | 56 | tokens[0] 57 | .trim() 58 | .split(' ') 59 | .nth(1) 60 | .unwrap() 61 | .parse::() 62 | .unwrap() 63 | } 64 | 65 | fn part2(line: &str) -> i32 { 66 | let tokens: Vec<&str> = line.split(':').collect(); 67 | 68 | let mut cc = CubeCount { 69 | red: 0, 70 | green: 0, 71 | blue: 0, 72 | }; 73 | 74 | tokens[1].split(';').for_each(|s| { 75 | s.split(',').for_each(|s| { 76 | let cubes: Vec<&str> = s.trim().split(' ').collect(); 77 | let n: i32 = cubes[0].parse::().unwrap(); 78 | 79 | match cubes[1] { 80 | "red" => cc.red = cmp::max(cc.red, n), 81 | "green" => cc.green = cmp::max(cc.green, n), 82 | "blue" => cc.blue = cmp::max(cc.blue, n), 83 | _ => panic!("invalid color"), 84 | } 85 | }) 86 | }); 87 | 88 | cc.red * cc.green * cc.blue 89 | } 90 | 91 | fn main() { 92 | let mut part1_sum = 0; 93 | let mut part2_sum = 0; 94 | 95 | let stdin = io::stdin(); 96 | for line in stdin.lines() { 97 | part1_sum += part1(line.as_ref().unwrap()); 98 | part2_sum += part2(line.as_ref().unwrap()); 99 | } 100 | 101 | println!("{}", part1_sum); 102 | println!("{}", part2_sum); 103 | } 104 | -------------------------------------------------------------------------------- /2023/rust/day20/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day20" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day21/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day21" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day22/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day22" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day23/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day23" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day24/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day24" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day25/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day25" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.5" 10 | -------------------------------------------------------------------------------- /2023/rust/day3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day3" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day4" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day4/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | fn part1(line: &str) -> i32 { 4 | let nums: Vec> = line 5 | .split(':') 6 | .next_back() 7 | .unwrap() 8 | .split('|') 9 | .map(|str| { 10 | str.split(' ') 11 | .filter(|s| !s.is_empty()) 12 | .map(|s| s.parse::().unwrap()) 13 | .collect::>() 14 | }) 15 | .collect(); 16 | 17 | nums[1].iter().filter(|n| nums[0].contains(n)).fold( 18 | 0, 19 | |acc, _| { 20 | if acc == 0 { 21 | 1 22 | } else { 23 | acc << 1 24 | } 25 | }, 26 | ) 27 | } 28 | 29 | fn part2(line: &str, cache: &mut Vec) -> i32 { 30 | let mut split_header = line.split(':'); 31 | 32 | let id = split_header 33 | .next() 34 | .unwrap() 35 | .split(' ') 36 | .next_back() 37 | .unwrap() 38 | .parse::() 39 | .unwrap(); 40 | 41 | let nums: Vec> = line 42 | .split(':') 43 | .next_back() 44 | .unwrap() 45 | .split('|') 46 | .map(|str| { 47 | str.split(' ') 48 | .filter(|s| !s.is_empty()) 49 | .map(|s| s.parse::().unwrap()) 50 | .collect::>() 51 | }) 52 | .collect(); 53 | 54 | cache[id] += 1; 55 | 56 | nums[1] 57 | .iter() 58 | .filter(|n| nums[0].contains(n)) 59 | .enumerate() 60 | .for_each(|(i, _)| { 61 | if id + i + 1 < cache.len() { 62 | cache[id + i + 1] += cache[id] 63 | } 64 | }); 65 | 66 | cache[id] 67 | } 68 | 69 | fn main() { 70 | let mut lines = Vec::::new(); 71 | 72 | let stdin = io::stdin(); 73 | for line in stdin.lines() { 74 | lines.push(line.unwrap()); 75 | } 76 | 77 | let mut part1_sum = 0; 78 | let mut part2_sum = 0; 79 | let mut cache: Vec = vec![0; lines.len() + 1]; 80 | 81 | for line in lines.iter() { 82 | part1_sum += part1(line); 83 | part2_sum += part2(line, &mut cache); 84 | } 85 | 86 | println!("{}", part1_sum); 87 | println!("{}", part2_sum); 88 | } 89 | -------------------------------------------------------------------------------- /2023/rust/day5/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day5" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day5/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | fn part1(lines: &[String]) -> u32 { 4 | let mut nums: Vec = lines 5 | .get(0) 6 | .unwrap() 7 | .split(':') 8 | .next_back() 9 | .unwrap() 10 | .trim() 11 | .split(' ') 12 | .map(|s| s.parse::().unwrap()) 13 | .collect(); 14 | 15 | let mut tmp = nums.clone(); 16 | for line in lines.iter().skip(1) { 17 | if line.is_empty() || line.split(' ').count() < 3 { 18 | nums = tmp.clone(); 19 | continue; 20 | } 21 | 22 | let mapping = line 23 | .trim() 24 | .split(' ') 25 | .map(|s| s.parse::().unwrap()) 26 | .collect::>(); 27 | 28 | for (idx, &n) in nums.iter().enumerate() { 29 | let end: usize = mapping[1] as usize + mapping[2] as usize; 30 | if n >= mapping[1] && (n as usize) < end { 31 | tmp[idx] = mapping[0] + (n - mapping[1]); 32 | } 33 | } 34 | } 35 | 36 | tmp.into_iter().min().unwrap() 37 | } 38 | 39 | fn part2(lines: &[String]) -> u32 { 40 | let ranges: Vec = lines 41 | .get(0) 42 | .unwrap() 43 | .split(':') 44 | .next_back() 45 | .unwrap() 46 | .trim() 47 | .split(' ') 48 | .map(|s| s.parse::().unwrap()) 49 | .collect(); 50 | 51 | let mut nums = Vec::::new(); 52 | for i in (0..ranges.len()).step_by(2) { 53 | for n in 0..ranges[i + 1] { 54 | nums.push(ranges[i] + n); 55 | } 56 | } 57 | drop(ranges); 58 | 59 | let mut tmp = nums.clone(); 60 | 61 | for line in lines.iter().skip(1) { 62 | if line.is_empty() || line.split(' ').count() < 3 { 63 | nums.clear(); 64 | nums.shrink_to_fit(); 65 | nums = tmp.clone(); 66 | continue; 67 | } 68 | 69 | let mapping = line 70 | .trim() 71 | .split(' ') 72 | .map(|s| s.parse::().unwrap()) 73 | .collect::>(); 74 | 75 | for (idx, &n) in nums.iter().enumerate() { 76 | let end: usize = mapping[1] as usize + mapping[2] as usize; 77 | if n >= mapping[1] && (n as usize) < end { 78 | tmp[idx] = mapping[0] + (n - mapping[1]); 79 | } 80 | } 81 | } 82 | 83 | tmp.into_iter().min().unwrap() 84 | } 85 | 86 | fn main() { 87 | let mut lines = Vec::::new(); 88 | 89 | let stdin = io::stdin(); 90 | for line in stdin.lines() { 91 | lines.push(line.unwrap()); 92 | } 93 | 94 | println!("{}", part1(&lines)); 95 | println!("{}", part2(&lines)); 96 | } 97 | -------------------------------------------------------------------------------- /2023/rust/day6/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day6" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day6/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | fn part1(lines: &[String]) -> u32 { 4 | let nums = lines 5 | .iter() 6 | .map(|line| { 7 | line.split(':') 8 | .nth(1) 9 | .unwrap() 10 | .split(' ') 11 | .filter(|s| !s.is_empty()) 12 | .map(|s| s.parse::().unwrap()) 13 | .collect::>() 14 | }) 15 | .collect::>>(); 16 | 17 | let mut out = 1; 18 | for i in 0..nums[0].len() { 19 | let t = nums[0][i]; 20 | let d = nums[1][i]; 21 | 22 | let mut cnt = 0; 23 | for j in 0..=t { 24 | if (t - j) * j > d { 25 | cnt += 1; 26 | } 27 | } 28 | out *= cnt; 29 | } 30 | 31 | out 32 | } 33 | 34 | fn part2(lines: &[String]) -> u32 { 35 | let nums = lines 36 | .iter() 37 | .map(|line| { 38 | line.split(':') 39 | .nth(1) 40 | .unwrap() 41 | .chars() 42 | .filter(|c| !c.is_whitespace()) 43 | .collect::() 44 | .parse::() 45 | .unwrap() 46 | }) 47 | .collect::>(); 48 | 49 | let mut out = 0; 50 | let t = nums[0]; 51 | let d = nums[1]; 52 | 53 | for j in 0..=t { 54 | if (t - j) * j > d { 55 | out += 1; 56 | } 57 | } 58 | 59 | out 60 | } 61 | 62 | fn main() { 63 | let mut lines = Vec::::new(); 64 | 65 | let stdin = io::stdin(); 66 | for line in stdin.lines() { 67 | lines.push(line.unwrap()); 68 | } 69 | 70 | println!("{}", part1(&lines)); 71 | println!("{}", part2(&lines)); 72 | } 73 | -------------------------------------------------------------------------------- /2023/rust/day7/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day7" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day8/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day8" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day8/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::io; 3 | 4 | fn gcd(mut a: usize, mut b: usize) -> usize { 5 | while a != b { 6 | if a > b { 7 | a -= b 8 | } else { 9 | b -= a 10 | } 11 | } 12 | 13 | a 14 | } 15 | 16 | fn part1(lines: &[String]) -> i32 { 17 | let mut m: HashMap = HashMap::new(); 18 | 19 | lines.iter().skip(2).for_each(|l| { 20 | let split = l.split('=').collect::>(); 21 | let dests = split[1].split(',').collect::>(); 22 | 23 | m.insert( 24 | split[0].trim().to_owned(), 25 | ( 26 | dests[0].trim()[1..].to_string(), 27 | dests[1].trim()[..3].to_string(), 28 | ), 29 | ); 30 | }); 31 | 32 | let dirs = &lines[0]; 33 | let mut node = "AAA"; 34 | let mut i = 0; 35 | let mut size = 0; 36 | 37 | while node != "ZZZ" { 38 | node = if dirs.as_bytes()[i] == b'L' { 39 | &m[node].0 40 | } else { 41 | &m[node].1 42 | }; 43 | 44 | i = (i + 1) % dirs.len(); 45 | size += 1; 46 | } 47 | 48 | size 49 | } 50 | 51 | fn part2(lines: &[String]) -> usize { 52 | let mut m: HashMap = HashMap::new(); 53 | 54 | lines.iter().skip(2).for_each(|l| { 55 | let split = l.split('=').collect::>(); 56 | let dests = split[1].split(',').collect::>(); 57 | 58 | m.insert( 59 | split[0].trim().to_owned(), 60 | ( 61 | dests[0].trim()[1..].to_string(), 62 | dests[1].trim()[..3].to_string(), 63 | ), 64 | ); 65 | }); 66 | 67 | let dirs = &lines[0]; 68 | m.iter() 69 | .filter(|(k, _)| k.as_bytes()[2] == b'A') 70 | .map(|(k, _)| { 71 | let mut i = 0; 72 | let mut size = 0; 73 | let mut node = k; 74 | 75 | while node.as_bytes()[2] != b'Z' { 76 | node = if dirs.as_bytes()[i] == b'L' { 77 | &m[node].0 78 | } else { 79 | &m[node].1 80 | }; 81 | 82 | i = (i + 1) % dirs.len(); 83 | size += 1; 84 | } 85 | 86 | size 87 | }) 88 | .fold(1_usize, |init, a| init * a / gcd(init, a)) 89 | } 90 | 91 | fn main() { 92 | let mut lines = Vec::::new(); 93 | 94 | let stdin = io::stdin(); 95 | for line in stdin.lines() { 96 | lines.push(line.unwrap()); 97 | } 98 | 99 | println!("{}", part1(&lines)); 100 | println!("{}", part2(&lines)); 101 | } 102 | -------------------------------------------------------------------------------- /2023/rust/day9/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day9" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /2023/rust/day9/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | fn part1(sequences: &[Vec]) -> i32 { 4 | sequences.iter().map(|v| v.last().unwrap()).sum::() 5 | } 6 | 7 | fn part2(sequences: &[Vec]) -> i32 { 8 | sequences 9 | .iter() 10 | .rev() 11 | .map(|v| v.first().unwrap()) 12 | .fold(0, |init, n| n - init) 13 | } 14 | 15 | fn main() { 16 | let mut part1_sum = 0; 17 | let mut part2_sum = 0; 18 | 19 | let stdin = io::stdin(); 20 | for line in stdin.lines() { 21 | let initial_seq: Vec = line 22 | .unwrap() 23 | .split(' ') 24 | .map(|s| s.parse::().unwrap()) 25 | .collect(); 26 | 27 | let mut sequences: Vec> = Vec::new(); 28 | sequences.push(initial_seq); 29 | 30 | loop { 31 | let s = &sequences.last().unwrap(); 32 | let mut prev = s[0]; 33 | let tmp: Vec = s 34 | .iter() 35 | .skip(1) 36 | .map(|&n| { 37 | let tmp = n - prev; 38 | prev = n; 39 | tmp 40 | }) 41 | .collect(); 42 | 43 | if tmp.iter().sum::() == 0 { 44 | break; 45 | } 46 | 47 | sequences.push(tmp); 48 | } 49 | 50 | part1_sum += part1(&sequences); 51 | part2_sum += part2(&sequences); 52 | } 53 | 54 | println!("{}", part1_sum); 55 | println!("{}", part2_sum); 56 | } 57 | -------------------------------------------------------------------------------- /2024/day1.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def part1(left, right) 4 | (left.sort.zip right.sort).map { |l, r| (l - r).abs }.sum 0 5 | end 6 | 7 | def part2(left, right) 8 | left.map { |e| (right.count e) * e }.sum 0 9 | end 10 | 11 | left = []; right = [] 12 | 13 | ARGF.each_line do |line| 14 | nums = line.split(/\s+/).map(&:to_i) 15 | left << nums[0]; right << nums[1] 16 | end 17 | 18 | puts part1(left, right) 19 | puts part2(left, right) 20 | -------------------------------------------------------------------------------- /2024/day10.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def traverse(map, pos) 4 | r, c = pos 5 | 6 | return [[r, c]] if map[r][c] == 9 7 | 8 | tops = [] 9 | [[0, 1], [1, 0], [0, -1], [-1, 0]].each do |dr, dc| 10 | nr = r + dr 11 | nc = c + dc 12 | 13 | next if nr.negative? || nc.negative? || nr >= map.length || nc >= map[nr].length 14 | next unless map[nr][nc] == map[r][c] + 1 15 | 16 | tops << traverse(map, [nr, nc]) 17 | end 18 | 19 | tops.flatten(1) 20 | end 21 | 22 | def part1(map, zeroes) 23 | zeroes.map { |z| traverse(map.map { |r| r.chars.map(&:to_i) }, z).uniq.count }.sum 24 | end 25 | 26 | def part2(map, zeroes) 27 | zeroes.map { |z| traverse(map.map { |r| r.chars.map(&:to_i) }, z).count }.sum 28 | end 29 | 30 | map = readlines(chomp: true) 31 | 32 | zeroes = map.each_with_index.flat_map do |row, i| 33 | row.chars.each_with_index.filter { |c, _| c == '0' }.map { |_, j| [i, j] } 34 | end 35 | 36 | puts part1 map, zeroes 37 | puts part2 map, zeroes 38 | -------------------------------------------------------------------------------- /2024/day11.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def sim(stone, limit, time, cache) 4 | cached = cache[stone][time] 5 | return cached if cached 6 | 7 | if time == limit 8 | cache[stone][time] = 1 9 | return 1 10 | end 11 | 12 | res = if stone.zero? 13 | sim(1, limit, time + 1, cache) 14 | elsif (digits = Math.log10(stone).to_i + 1) && digits.even? 15 | fst, snd = stone.divmod(10**(digits / 2)) 16 | sim(fst, limit, time + 1, cache) + sim(snd, limit, time + 1, cache) 17 | else 18 | sim(stone * 2024, limit, time + 1, cache) 19 | end 20 | 21 | cache[stone][time] = res 22 | res 23 | end 24 | 25 | def part1(stones) 26 | cache = Hash.new { |hsh, key| hsh[key] = {} } 27 | stones.map { |stone| sim stone, 25, 0, cache }.sum 28 | end 29 | 30 | def part2(stones) 31 | cache = Hash.new { |hsh, key| hsh[key] = {} } 32 | stones.map { |stone| sim stone, 75, 0, cache }.sum 33 | end 34 | 35 | stones = readline(chomp: true).split.map(&:to_i) 36 | 37 | puts part1 stones 38 | puts part2 stones 39 | -------------------------------------------------------------------------------- /2024/day12.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def solve(grid) 4 | visited = {} 5 | 6 | (0...grid.length).to_a.product((0...grid[0].length).to_a).filter_map do |i, j| 7 | next if visited[[i, j]] 8 | 9 | area = 0 10 | perimeter = [] 11 | 12 | q = [[i, j]] 13 | until q.empty? 14 | r, c = q.shift 15 | 16 | next if visited[[r, c]] 17 | 18 | visited[[r, c]] = true 19 | area += 1 20 | 21 | [[1, 0], [-1, 0], [0, -1], [0, 1]].each do |vr, vc| 22 | nr = r + vr 23 | nc = c + vc 24 | 25 | if nr.negative? || nr >= grid.length || nc.negative? || nc >= grid[0].length || grid[nr][nc] != grid[i][j] 26 | perimeter << [nr, nc, grid[i][j], [vr, vc]] 27 | next 28 | end 29 | 30 | q << [nr, nc] 31 | end 32 | end 33 | 34 | [area, perimeter] 35 | end 36 | end 37 | 38 | def part1(grid) 39 | solve(grid).map { |area, perimeter| area * perimeter.length }.sum 40 | end 41 | 42 | def fence_lines(perimeter) 43 | visited = {} 44 | 45 | perimeter.count do |p| 46 | next false if visited[p] 47 | 48 | q = [p] 49 | until q.empty? 50 | p = q.shift 51 | 52 | next if visited[p] 53 | 54 | visited[p] = true 55 | 56 | [[1, 0], [-1, 0], [0, -1], [0, 1]].each do |vr, vc| 57 | next if p[3][0].zero? == vr.zero? 58 | 59 | np = p.dup 60 | np[0] += vr 61 | np[1] += vc 62 | 63 | q << np if perimeter.include? np 64 | end 65 | end 66 | 67 | true 68 | end 69 | end 70 | 71 | def part2(grid) 72 | solve(grid).sum { |area, perimeter| area * fence_lines(perimeter) } 73 | end 74 | 75 | grid = readlines(chomp: true).map(&:chars) 76 | 77 | puts part1 grid 78 | puts part2 grid 79 | -------------------------------------------------------------------------------- /2024/day13.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def solve(equation) 4 | a1, a2, b1, b2, c1, c2 = equation 5 | 6 | # a1 * x + b1 * y = c1 7 | # -> b1 * y = c1 - a1 * x 8 | # -> y = (c1 - a1 * x) / b1 9 | # a2 * x + b2 * y = c2 10 | # -> a2 * x + b2 * (c1 - a1 * x) / b1 = c2 11 | # -> a2 * x + (b2 * c1 - b2 * a1 * x) / b1 = c2 12 | # -> b1 * a2 * x + b2 * c1 - b2 * a1 * x = c2 * b1 13 | # -> b1 * a2 * x - b2 * a1 * x = c2 * b1 - b2 * c1 14 | # -> x (b1 * a2 - b2 * a1) = c2 * b1 - b2 * c1 15 | # -> x = (c2 * b1 - b2 * c1) / (b1 * a2 - b2 * a1) 16 | 17 | x, m = (c2 * b1 - b2 * c1).divmod(b1 * a2 - b2 * a1) 18 | return nil if m != 0 19 | 20 | y, m = (c1 - a1 * x).divmod b1 21 | return nil if m != 0 22 | 23 | 3 * x + y 24 | end 25 | 26 | def part1(equations) 27 | equations.filter_map { |e| solve e }.sum 28 | end 29 | 30 | def part2(equations) 31 | equations.map { |e| e[0..3] + e[4..5].map { |c| c + 10_000_000_000_000 } }.filter_map { |e| solve e }.sum 32 | end 33 | 34 | equations = readlines(chomp: true).each_slice(4).map(&:join).map { |e| e.scan(/(\d+)/).flatten.map(&:to_i) } 35 | 36 | puts part1 equations 37 | puts part2 equations 38 | -------------------------------------------------------------------------------- /2024/day14.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def step(robots, width, height, time) 4 | robots.map { |px, py, vx, vy| [(px + vx * time) % width, (py + vy * time) % height] } 5 | end 6 | 7 | def part1(robots, wide, tall, time) 8 | final = step(robots, wide, tall, time) 9 | 10 | half_x = wide / 2 11 | half_y = tall / 2 12 | 13 | quadrants = [(0...half_x), (half_x + 1...wide)].product([(0...half_y), (half_y + 1...tall)]) 14 | quadrants.map { |rx, ry| final.count { |x, y| rx.include?(x) && ry.include?(y) } }.reduce(:*) 15 | end 16 | 17 | def part2(robots, wide, tall) 18 | (0...).find do |i| 19 | tiles = Array.new(tall, 0).map { |_| Array.new(wide, '.') } 20 | arrangement = step(robots, wide, tall, i) 21 | arrangement.each { |c, r| tiles[r][c] = '#' } 22 | 23 | tiles.any? { |t| t.join =~ /########/ } 24 | end 25 | end 26 | 27 | robots = readlines(chomp: true).map { |e| e.scan(/(-?\d+)/).flatten.map(&:to_i) } 28 | 29 | puts part1 robots, 101, 103, 100 30 | puts part2 robots, 101, 103 31 | -------------------------------------------------------------------------------- /2024/day15.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'set' 4 | 5 | def extract_grid_data(grid) 6 | walls = Set.new 7 | boxes = Set.new 8 | robot = [] 9 | 10 | grid.each_with_index do |row, y| 11 | row.chars.each_with_index do |col, x| 12 | walls << [x, y] if col == '#' 13 | boxes << [x, x + (col == '[' ? 1 : 0), y] if %w(O [).include?(col) 14 | robot = [x, y] if col == '@' 15 | end 16 | end 17 | 18 | [robot, walls, boxes] 19 | end 20 | 21 | def boxes_to_move(pos, dir, boxes, walls) 22 | queue = [pos] 23 | visited = Set.new 24 | move = Set.new 25 | 26 | until queue.empty? 27 | pos = queue.shift 28 | new_pos = [pos[0] + dir[0], pos[1] + dir[1]] 29 | 30 | return nil if walls.include? new_pos 31 | next if visited.include? new_pos 32 | 33 | visited << pos 34 | 35 | box = boxes.find { |bx, ex, y| bx <= new_pos[0] && new_pos[0] <= ex && y == new_pos[1] } 36 | next unless box 37 | 38 | queue << [box[0], box[2]] << [box[1], box[2]] 39 | move << box 40 | end 41 | 42 | move.uniq 43 | end 44 | 45 | def solve(moves, robot, walls, boxes) 46 | moves.chars.each do |move| 47 | dir = { '^' => [0, -1], 'v' => [0, 1], '>' => [1, 0], '<' => [-1, 0] }[move] 48 | boxes_to_move = boxes_to_move robot, dir, boxes, walls 49 | next unless boxes_to_move 50 | 51 | boxes_to_move.each { |box| boxes.delete box } 52 | boxes_to_move.each { |bx, ex, y| boxes << [bx + dir[0], ex + dir[0], y + dir[1]] } 53 | 54 | robot = [robot[0] + dir[0], robot[1] + dir[1]] 55 | end 56 | 57 | boxes.map { |x, _, y| 100 * y + x }.sum 58 | end 59 | 60 | def part1(grid, moves) 61 | solve moves, *extract_grid_data(grid) 62 | end 63 | 64 | def part2(grid, moves) 65 | grid = grid.map do |row| 66 | row.chars.map do |cell| 67 | next '##' if cell == '#' 68 | next '[]' if cell == 'O' 69 | next '..' if cell == '.' 70 | next '@.' if cell == '@' 71 | end.join 72 | end 73 | 74 | solve moves, *extract_grid_data(grid) 75 | end 76 | 77 | input = readlines(chomp: true) 78 | 79 | split = input.find_index(&:empty?) 80 | grid = input[0...split] 81 | moves = input[split + 1..].join 82 | 83 | puts part1 grid, moves 84 | puts part2 grid, moves 85 | -------------------------------------------------------------------------------- /2024/day16.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def solve(grid, best_tiles = []) 4 | start = grid.each_with_index.map { |row, i| [i, row.chars.find_index('S')] }.filter { |_, c| c }.flatten 5 | 6 | visited = {} 7 | q = [[[start], [0, 1], 0]] 8 | 9 | until q.empty? 10 | q.sort_by! { |_, _, d| d } 11 | path, dir, dist = q.shift 12 | cur = path.last 13 | 14 | return [dist, path, best_tiles.uniq] if grid[cur[0]][cur[1]] == 'E' 15 | 16 | if visited[cur] 17 | best_tiles += path if best_tiles.include? path.last 18 | next 19 | end 20 | 21 | visited[cur] = dist 22 | 23 | [dir, [-dir[1], dir[0]], [dir[1], -dir[0]]].each do |dr, dc| 24 | nr = cur[0] + dr 25 | nc = cur[1] + dc 26 | 27 | next if grid[nr][nc] == '#' 28 | 29 | q << [path + [[nr, nc]], [dr, dc], dist + (dir == [dr, dc] ? 1 : 1001)] 30 | end 31 | end 32 | end 33 | 34 | def part1(grid) 35 | solve(grid)[0] 36 | end 37 | 38 | def part2(grid) 39 | solve(grid, solve(grid)[1])[2].length 40 | end 41 | 42 | grid = readlines(chomp: true) 43 | 44 | puts part1 grid 45 | puts part2 grid 46 | -------------------------------------------------------------------------------- /2024/day17.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def combo(registers, operand) 4 | case operand 5 | when 0..3 6 | operand 7 | when 4..6 8 | registers[operand - 4] 9 | else 10 | throw NotImplementedError 11 | end 12 | end 13 | 14 | def exec(registers, program) 15 | dv = proc { |operand| registers[0] / (1 << combo(registers, operand)) } 16 | out = [] 17 | 18 | ip = 0 19 | while ip < program.length 20 | op, operand = program[ip] 21 | 22 | case op 23 | when 0 24 | registers[0] = dv.call operand 25 | when 1 26 | registers[1] ^= operand 27 | when 2 28 | registers[1] = combo(registers, operand) % 8 29 | when 3 30 | next ip = operand if registers[0] != 0 31 | when 4 32 | registers[1] ^= registers[2] 33 | when 5 34 | out << combo(registers, operand) % 8 35 | when 6 36 | registers[1] = dv.call operand 37 | when 7 38 | registers[2] = dv.call operand 39 | else 40 | throw NotImplementedError 41 | end 42 | 43 | ip += 1 44 | end 45 | 46 | out 47 | end 48 | 49 | def part1(registers, program) 50 | exec(registers, program).join ',' 51 | end 52 | 53 | def part2(program) 54 | p = program.flatten.reverse 55 | d = 1 << program.filter_map { |o, c| c if o.zero? }[0] 56 | 57 | q = [[0, 0]] 58 | until q.empty? 59 | ans, i = q.shift 60 | 61 | return ans if i == p.length 62 | 63 | (0..d - 1).map { |m| ans * d + m }.filter { |a| exec([a, 0, 0], program).first == p[i] }.each { |a| q << [a, i + 1] } 64 | end 65 | end 66 | 67 | nums = readlines(chomp: true).join.scan(/\d+/).map(&:to_i) 68 | registers = nums[0...3].map(&:to_i) 69 | program = nums[3..].each_slice(2).to_a 70 | 71 | puts part1 registers, program 72 | puts part2 program 73 | -------------------------------------------------------------------------------- /2024/day18.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'set' 4 | 5 | def part1(bytes, max_coords, fallen_bytes) 6 | w, h = max_coords 7 | fallen = bytes.first(fallen_bytes).to_set 8 | 9 | q = [[0, 0, 0]] 10 | visited = Set.new 11 | 12 | until q.empty? 13 | x, y, d = q.shift 14 | 15 | return d if x == w && y == h 16 | next if visited.include?([x, y]) 17 | 18 | visited << [x, y] 19 | 20 | [[0, 1], [0, -1], [1, 0], [-1, 0]].each do |dx, dy| 21 | nx = x + dx 22 | ny = y + dy 23 | 24 | next if nx.negative? || ny.negative? || nx > w || ny > h || fallen.include?([nx, ny]) 25 | 26 | q << [nx, ny, d + 1] 27 | end 28 | end 29 | 30 | 0 31 | end 32 | 33 | def part2(bytes, max_coords) 34 | bytes[(0...bytes.length).find { |i| part1(bytes, max_coords, i).zero? } - 1].join(',') 35 | end 36 | 37 | bytes = readlines(chomp: true).map { |l| l.split(',').map(&:to_i) } 38 | 39 | puts part1 bytes, [70, 70], 1024 40 | puts part2 bytes, [70, 70] 41 | -------------------------------------------------------------------------------- /2024/day19.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def try_parse(patterns, towel, cache) 4 | return 1 if towel.empty? 5 | return cache[towel] if cache[towel] 6 | 7 | cnt = patterns.filter { |p| towel.start_with? p }.map { |p| try_parse patterns, towel.delete_prefix(p), cache }.sum 8 | cache[towel] = cnt 9 | 10 | cnt 11 | end 12 | 13 | def part1(patterns, towels) 14 | towels.count { |t| try_parse(patterns, t, {}) != 0 } 15 | end 16 | 17 | def part2(patterns, towels) 18 | towels.sum { |t| try_parse patterns, t, {} } 19 | end 20 | 21 | input = readlines(chomp: true) 22 | 23 | patterns = input[0].split ', ' 24 | towels = input[2..] 25 | 26 | puts part1 patterns, towels 27 | puts part2 patterns, towels 28 | -------------------------------------------------------------------------------- /2024/day2.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def safe?(report) 4 | diffs = report.each_cons(2).map { |x, y| y - x } 5 | diffs.map { |x| x <=> 0 }.uniq.length == 1 && diffs.all? { |e| (1..3).member? e.abs } 6 | end 7 | 8 | def part1(reports) 9 | reports.count { |r| safe? r } 10 | end 11 | 12 | def part2(reports) 13 | reports.count { |r| (0...r.length).any? { |i| safe?(r[0...i] + r[i + 1..]) } } 14 | end 15 | 16 | reports = ARGF.each_line.map { |line| line.split(/\s+/).map(&:to_i) } 17 | 18 | puts part1 reports 19 | puts part2 reports 20 | -------------------------------------------------------------------------------- /2024/day20.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def get_path(grid, pos) 4 | visited = {} 5 | path = [pos] 6 | 7 | loop do 8 | r, c = path.last 9 | visited[[r, c]] = true 10 | 11 | break if grid[r][c] == 'E' 12 | 13 | [[0, 1], [0, -1], [1, 0], [-1, 0]].each do |dr, dc| 14 | nr = r + dr 15 | nc = c + dc 16 | 17 | next if grid[nr][nc] == '#' || visited[[nr, nc]] 18 | 19 | path << [nr, nc] 20 | end 21 | end 22 | 23 | path.each_with_index.map { |p, i| [p, path.length - i - 1] }.to_h 24 | end 25 | 26 | def solve(grid, cheat_duration) 27 | start = grid.each_with_index.map { |row, i| [i, row.chars.find_index('S')] }.filter { |_, c| c }.flatten 28 | time_saves = Hash.new { |h, k| h[k] = 0 } 29 | 30 | path = get_path grid, start 31 | path.each_key do |pos| 32 | q = [[pos, 0]] 33 | visited = {} 34 | 35 | until q.empty? 36 | p, d = q.shift 37 | 38 | next if visited[p] || d > cheat_duration 39 | 40 | visited[p] = true 41 | time_saves[(path[p] - path[pos]).abs - d] += 1 if path.include? p 42 | 43 | [[0, 1], [0, -1], [1, 0], [-1, 0]].each do |dr, dc| 44 | nr = p[0] + dr 45 | nc = p[1] + dc 46 | 47 | next if nr.negative? || nc.negative? || nr >= grid.length || nc >= grid[0].length 48 | 49 | q << [[nr, nc], d + 1] 50 | end 51 | end 52 | end 53 | 54 | time_saves.filter_map { |k, v| v / 2 if k >= 100 }.sum 55 | end 56 | 57 | def part1(grid) 58 | solve grid, 2 59 | end 60 | 61 | def part2(grid) 62 | solve grid, 20 63 | end 64 | 65 | grid = readlines(chomp: true) 66 | 67 | puts part1 grid 68 | puts part2 grid 69 | -------------------------------------------------------------------------------- /2024/day21.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def sequences_to_button(pos, button, keypad) 4 | q = [[pos, [], []]] 5 | 6 | sequences = [] 7 | until q.empty? 8 | pos, visited, path = q.shift 9 | 10 | if keypad[pos[0]][pos[1]] == button 11 | sequences << [pos, path + ['A']] 12 | next 13 | end 14 | 15 | [[-1, 0, '^'], [1, 0, 'v'], [0, 1, '>'], [0, -1, '<']].filter_map do |dr, dc, dir| 16 | nr = pos[0] + dr 17 | nc = pos[1] + dc 18 | 19 | next if nr.negative? || nc.negative? || nr >= keypad.length || nc >= keypad[0].length || keypad[nr][nc] == '#' 20 | next if visited.include? [nr, nc] 21 | 22 | q << [[nr, nc], [pos] + visited, path + [dir]] 23 | end 24 | end 25 | 26 | sequences 27 | end 28 | 29 | def shortest_sequences_for_pattern(pattern, start, keypad) 30 | shortest_sequences = [] 31 | 32 | q = [[start, 0, []]] 33 | until q.empty? 34 | pos, idx, moves = q.shift 35 | 36 | if idx == pattern.length 37 | shortest_sequences << moves.join 38 | next 39 | end 40 | 41 | shortest_paths_to_cur_btn = sequences_to_button(pos, pattern[idx], keypad).group_by { |_, m| m.length }.min[1] 42 | shortest_paths_to_cur_btn.each { |p, path| q << [p, idx + 1, moves + path] } 43 | end 44 | 45 | shortest_sequences 46 | end 47 | 48 | def shortest_numeric_keypad_sequences(code) 49 | keypad = [ 50 | %w[7 8 9], 51 | %w[4 5 6], 52 | %w[1 2 3], 53 | %w[# 0 A] 54 | ] 55 | 56 | shortest_sequences_for_pattern(code, [3, 2], keypad) 57 | end 58 | 59 | def shortest_directional_keypad_sequences(sequence) 60 | keypad = [ 61 | %w[# ^ A], 62 | %w[< v >] 63 | ] 64 | 65 | shortest_sequences_for_pattern(sequence, [0, 2], keypad) 66 | end 67 | 68 | def expand_sequence_depth_first(move, limit, cache, depth = 0) 69 | return move.length if depth == limit 70 | return cache[[move, depth]] if cache.include? [move, depth] 71 | 72 | parts = move.length == 1 ? [move] : move.split('A').map { |p| "#{p}A" } 73 | 74 | parts = parts.map do |part| 75 | moves = shortest_directional_keypad_sequences(part) 76 | moves.map { |m| expand_sequence_depth_first(m, limit, cache, depth + 1) }.min 77 | end 78 | 79 | cache[[move, depth]] = parts.sum 80 | cache[[move, depth]] 81 | end 82 | 83 | def min_sequence_length(code, robots) 84 | cache = {} 85 | shortest_numeric_keypad_sequences(code).map { |m| expand_sequence_depth_first(m, robots, cache) }.min 86 | end 87 | 88 | def part1(codes) 89 | codes.map { |c| min_sequence_length(c, 2) * c[0..2].to_i }.sum 90 | end 91 | 92 | def part2(codes) 93 | codes.map { |c| min_sequence_length(c, 25) * c[0..2].to_i }.sum 94 | end 95 | 96 | codes = readlines(chomp: true) 97 | 98 | puts part1 codes 99 | puts part2 codes 100 | -------------------------------------------------------------------------------- /2024/day22.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | MOD = 16_777_216 4 | 5 | def evolve_secret(num) 6 | num ^= num * 64 7 | num ^= (num % MOD) / 32 8 | num ^= (num % MOD) * 2048 9 | num % MOD 10 | end 11 | 12 | def part1(secrets) 13 | secrets.map { |s| 2000.times.map { s = evolve_secret(s) }.last }.sum 14 | end 15 | 16 | def part2(secrets) 17 | prices = secrets.map { |s| ([s] + 2000.times.map { s = evolve_secret(s) }[...-1]).map { |n| n % 10 } } 18 | diffs = prices.map { |p| p.each_cons(2).map { |a, b| b - a } } 19 | 20 | cache = Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = [] } } 21 | diffs.each_with_index { |d, di| d.each_cons(4).each_with_index { |e, i| cache[e][di] << prices[di][i + 4] } } 22 | 23 | cache.map { |_, ps| ps.map { |_, p| p[0] }.sum }.max 24 | end 25 | 26 | secrets = readlines(chomp: true).map(&:to_i) 27 | 28 | puts part1 secrets 29 | puts part2 secrets 30 | -------------------------------------------------------------------------------- /2024/day23.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'set' 4 | 5 | def part1(connections) 6 | maybe_historians = connections.filter { |k, _| k.start_with? 't' } 7 | 8 | triples = maybe_historians.flat_map do |f, to| 9 | to.flat_map { |t| to.filter_map { |t2| [f, t, t2] if t != t2 && connections[t].include?(t2) } } 10 | end 11 | 12 | triples.map(&:sort).uniq.length 13 | end 14 | 15 | def part2(connections) 16 | largest_set = Set.new 17 | 18 | all_visited = Set.new 19 | connections.each_key do |from| 20 | q = [[Set.new([from]), Set.new([from] + connections[from])]] 21 | 22 | until q.empty? 23 | visited, intersection = q.shift 24 | 25 | if visited.length == intersection.length 26 | largest_set = intersection if intersection.length > largest_set.length 27 | next 28 | end 29 | next if intersection.empty? || all_visited.include?([intersection, visited.length]) 30 | 31 | all_visited << [intersection, visited.length] 32 | intersection.each { |i| q << [visited + [i], intersection & ([i] + connections[i])] unless visited.include? i } 33 | end 34 | end 35 | 36 | largest_set.sort.join ',' 37 | end 38 | 39 | input = readlines(chomp: true).map { |c| c.split('-') } 40 | 41 | connections = Hash.new { |h, k| h[k] = [] } 42 | input.each do |from, to| 43 | connections[from] << to 44 | connections[to] << from 45 | end 46 | 47 | puts part1 connections 48 | puts part2 connections 49 | -------------------------------------------------------------------------------- /2024/day24.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def topological_sort(n, connections, sorted) 4 | return if sorted.include? n 5 | 6 | connections.filter { |_, v| v.include?(n) }.each_key { |k| topological_sort(k, connections, sorted) } 7 | sorted << n 8 | end 9 | 10 | def part1(values, connections) 11 | sorted = [] 12 | values.each_key { |v| topological_sort v, connections, sorted } 13 | 14 | ops = { 'AND' => :&, 'OR' => :|, 'XOR' => :^ } 15 | sorted.reverse.each do |n| 16 | next unless connections.include? n 17 | 18 | l, op, r = connections[n] 19 | values[n] = values[l].send(ops[op], values[r]) 20 | end 21 | 22 | values.filter { |k, _| k.start_with? 'z' }.sort.reverse.map { |a| a[1] }.join.to_i(2) 23 | end 24 | 25 | def find_pattern(connections, pattern) 26 | connections.find { |_, v| v.sort == pattern.sort }&.at(0) 27 | end 28 | 29 | def swap_connections(connections, from, to) 30 | connections[from], connections[to] = connections[to], connections[from] 31 | [to, from] 32 | end 33 | 34 | # Hand-tailored for inputs in the form of 35 | # z0 = x0 ^ y0 36 | # c = x0 & y0 37 | # 38 | # repeat 39 | # tz = x_n ^ y_n 40 | # z_n = tz ^ c 41 | # tc1 = x_n & y_n 42 | # tc2 = tz & c 43 | # c = tc1 | tc2 44 | def part2(values, connections) 45 | swaps = [] 46 | 47 | c = find_pattern(connections, ['AND', 'x00', 'y00']) 48 | 49 | values.keys.filter { |k| k.start_with?('x') && k != 'x00' }.each do |x| 50 | y = "y#{x[1..]}" 51 | 52 | tz = find_pattern(connections, ['XOR', x, y]) 53 | z = find_pattern(connections, ['XOR', tz, c]) 54 | 55 | # hand-tailored case #1 - z_n is swapped with some other node 56 | swaps << swap_connections(connections, z, "z#{x[1..]}") if z && !z.start_with?('z') 57 | 58 | tc1 = find_pattern(connections, ['AND', x, y]) 59 | 60 | # hand-tailored case #2 - tz and tc1 are swapped 61 | swaps << [(tc1, tz = swap_connections(connections, tc1, tz))] unless z 62 | 63 | tc2 = find_pattern(connections, ['AND', tz, c]) 64 | c = find_pattern(connections, ['OR', tc1, tc2]) 65 | end 66 | 67 | swaps.flatten.sort.join ',' 68 | end 69 | 70 | input = readlines(chomp: true) 71 | empty = input.find_index(&:empty?) 72 | 73 | values = input[0...empty].map { |v| v.split ':' }.to_h.transform_values(&:to_i) 74 | connections = input[empty + 1...].map { |v| v.split ' -> ' }.map { |s| [s[1], s[0].split(' ')] }.to_h 75 | 76 | puts part1 values, connections 77 | puts part2 values, connections 78 | -------------------------------------------------------------------------------- /2024/day25.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def part1(schematics) 4 | heights = schematics.each_slice(8).map { |s| [s[0][0], s[0...7].map(&:chars).transpose.map { |c| c.count('#') - 1 }] } 5 | locks, keys = heights.partition { |h| h[0] == '#' }.map { |p| p.map { |_, h| h } } 6 | keys.product(locks).uniq.count { |p| p.transpose.map { |x| x.reduce(:+) }.all? { |x| x <= 5 } } 7 | end 8 | 9 | def part2 10 | 0 11 | end 12 | 13 | schematics = readlines(chomp: true) 14 | 15 | puts part1 schematics 16 | puts part2 17 | -------------------------------------------------------------------------------- /2024/day3.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def part1(memory) 4 | memory.scan(/mul\((\d*),(\d*)\)/).map { |x, y| x.to_i * y.to_i }.sum 5 | end 6 | 7 | def part2(memory) 8 | part1 "do#{memory}".split(/(don't)|(do)/).drop(1).each_slice(2).filter { |e, _| e != "don't" }.join 9 | end 10 | 11 | memory = readlines.join 12 | 13 | puts part1 memory 14 | puts part2 memory 15 | -------------------------------------------------------------------------------- /2024/day4.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def diagonal(word_search, start_row, start_col) 4 | row_it_count = word_search.count - (start_row + 1) 5 | col_it_count = word_search[0].length - (start_col + 1) 6 | it_count = [row_it_count, col_it_count].min 7 | 8 | (0..it_count).collect { |i| word_search[start_row + i][start_col + i] }.flatten 9 | end 10 | 11 | def inv_diagonal(word_search, start_row, start_col) 12 | row_it_count = word_search.count - (start_row + 1) 13 | col_it_count = start_col 14 | it_count = [row_it_count, col_it_count].min 15 | 16 | (0..it_count).collect { |i| word_search[start_row + i][start_col - i] }.flatten 17 | end 18 | 19 | def part1(ws) 20 | scan_xmas = proc { |l| l.scan(/XMAS/).length } 21 | get_diagonals = proc { |i| [diagonal(ws, 0, i), diagonal(ws, i, 0)].map(&:join) } 22 | get_inv_diagonals = proc { |i| [inv_diagonal(ws, i, ws.count - 1), inv_diagonal(ws, 0, i)].map(&:join) } 23 | 24 | lines = ws.flatten 25 | cols = ws.map(&:chars).transpose.map(&:join).flatten 26 | diags = (0...ws.count).collect { |i| [get_diagonals.call(i), get_inv_diagonals.call(i)] }.flatten[1...-1] 27 | 28 | to_check = [lines, cols, diags].flatten 29 | to_check.map { |l| scan_xmas.call(l) + scan_xmas.call(l.reverse) }.sum 30 | end 31 | 32 | def part2(ws) 33 | maybe_valid = ws.each_with_index.map do |l, i| 34 | l.chars.each_with_index.filter_map do |c, j| 35 | r = j - 1..j + 1 36 | in_range = i - 1 >= 0 && i + 1 < ws.count && !r.begin.negative? && r.end < ws.count 37 | [ws[i - 1][r], ws[i][r], ws[i + 1][r]] if in_range && c == 'A' 38 | end 39 | end.flatten(1) 40 | 41 | maybe_valid.count do |m| 42 | d = diagonal(m, 0, 0).join 43 | id = inv_diagonal(m, 0, 2).join 44 | 45 | scan_mas = proc { |l| l =~ /MAS/ } 46 | [d, id].all? { |e| scan_mas.call(e) || scan_mas.call(e.reverse) } 47 | end 48 | end 49 | 50 | word_search = readlines.map(&:chomp) 51 | 52 | puts part1 word_search 53 | puts part2 word_search 54 | -------------------------------------------------------------------------------- /2024/day5.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def part1(rules, updates) 4 | right = updates.filter { |u| u.all? { |n| rules[n].filter_map { |s| u.index(s) }.all? { |i| u.index(n) < i } } } 5 | right.map { |r| r[r.length / 2.0].to_i }.sum 6 | end 7 | 8 | def part2(rules, updates) 9 | wrong = updates.filter { |u| u.any? { |n| rules[n].filter_map { |s| u.index(s) }.any? { |i| u.index(n) > i } } } 10 | ordered = wrong.map { |w| w.map { |n| [n, rules[n].filter_map { |s| w.index(s) }.length] } } 11 | fixed = ordered.map { |o| o.sort_by! { |_, n| n }.reverse!.map { |a, _| a} } 12 | fixed.map { |r| r[r.length / 2.0].to_i }.sum 13 | end 14 | 15 | lines = readlines(chomp: true).filter { |l| !l.empty? } 16 | 17 | rules_to_process, updates = lines.partition { |l| l.include? '|' } 18 | updates = updates.map { |u| u.split ',' } 19 | 20 | rules = Hash.new { |hash, key| hash[key] = [] } 21 | rules_to_process.map { |r| r.split('|') }.each { |l, r| rules[l] << r } 22 | 23 | puts part1 rules, updates 24 | puts part2 rules, updates 25 | -------------------------------------------------------------------------------- /2024/day6.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'set' 4 | 5 | def part1(map, guard_pos) 6 | dir = [-1, 0] 7 | oob = proc { |r, c| r.negative? || c.negative? || r >= map.size || c >= map[0].size } 8 | loop_desc = [[0, 0], 0] 9 | visited = Set[] 10 | 11 | loop do 12 | !visited.include?(guard_pos) ? loop_desc = [guard_pos, 1] : loop_desc[1] += 1 13 | return nil if loop_desc[1] > 1 && loop_desc[0] == guard_pos 14 | 15 | visited << guard_pos 16 | 17 | nr, nc = [guard_pos, dir].transpose.map(&:sum) 18 | break if oob.call(nr, nc) 19 | 20 | (0..3).each do |_| 21 | break if map[nr][nc] != '#' 22 | 23 | dir = [dir[1], -dir[0]] 24 | nr, nc = [guard_pos, dir].transpose.map(&:sum) 25 | end 26 | 27 | guard_pos = [nr, nc] 28 | end 29 | 30 | visited.length 31 | end 32 | 33 | def part2(map, guard_pos) 34 | cnt = 0 35 | (0...map.size).each do |i| 36 | (0...map[0].size).each do |j| 37 | next if map[i][j] != '.' 38 | 39 | map[i][j] = '#' 40 | cnt += 1 unless part1 map, guard_pos 41 | map[i][j] = '.' 42 | end 43 | end 44 | cnt 45 | end 46 | 47 | map = readlines(chomp: true).map(&:chars) 48 | guard_pos = map.each_with_index.filter_map { |e, r| (c = e.index('^')) ? [r, c] : nil }.flatten 49 | 50 | puts part1 map, guard_pos 51 | puts part2 map, guard_pos 52 | -------------------------------------------------------------------------------- /2024/day7.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Integer 4 | def concat(other) 5 | (to_s + other.to_s).to_i 6 | end 7 | end 8 | 9 | def solve(result, arr, ops) 10 | return result == arr[0] if arr.length == 1 11 | 12 | ops.map { |o| solve result, arr.drop(2).unshift([arr[0], arr[1]].inject(o)), ops }.any? 13 | end 14 | 15 | def part1(equations) 16 | equations.filter { |r, a| solve r, a, %i[* +] }.map { |r, _| r }.sum 17 | end 18 | 19 | def part2(equations) 20 | equations.filter { |r, a| solve r, a, %i[* + concat] }.map { |r, _| r }.sum 21 | end 22 | 23 | equations = readlines(chomp: true).map { |l| l.split(':') }.map { |s, e| [s.to_i, e.split.map(&:chomp).map(&:to_i)] } 24 | 25 | puts part1 equations 26 | puts part2 equations 27 | -------------------------------------------------------------------------------- /2024/day8.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def get_antinodes(a1, a2, range) 4 | swap a1, a2 if a2[0] < a1[0] 5 | 6 | vx = a2[0] - a1[0] 7 | vy = a2[1] - a1[1] 8 | 9 | range.flat_map { |i| [[a2[0] + i * vx, a2[1] + i * vy], [a1[0] - i * vx, a1[1] - i * vy]] } 10 | end 11 | 12 | def solve(antennas, grid_dimensions, range) 13 | pairs = antennas.each_value.flat_map { |v| v.each_with_index.flat_map { |c, i| v.drop(i + 1).map { |c2| [c, c2] } } } 14 | antinodes = pairs.map { |a1, a2| get_antinodes a1, a2, range }.flatten(1).filter do |r, c| 15 | !r.negative? && r < grid_dimensions[0] && !c.negative? && c < grid_dimensions[1] 16 | end 17 | 18 | antinodes.uniq.length 19 | end 20 | 21 | def part1(antennas, grid_dimensions) 22 | solve antennas, grid_dimensions, (1..1) 23 | end 24 | 25 | def part2(antennas, grid_dimensions) 26 | solve antennas, grid_dimensions, (0..grid_dimensions[0]) 27 | end 28 | 29 | 30 | grid = readlines(chomp: true) 31 | grid_dimensions = [grid.length, grid[0].length] 32 | 33 | points_of_interest = grid.each_with_index.flat_map do |row, i| 34 | row.chars.each_with_index.filter { |c, _| c != '.' }.map { |c, j| [c, i, j] } 35 | end 36 | 37 | antennas = Hash.new { |hash, key| hash[key] = [] } 38 | points_of_interest.each { |a, i, j| antennas[a] << [i, j] } 39 | 40 | puts part1 antennas, grid_dimensions 41 | puts part2 antennas, grid_dimensions 42 | -------------------------------------------------------------------------------- /2024/day9.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | def part1(disk_map) 4 | files, free_space = disk_map.chars.each_with_index.partition { |_, i| i.even? }.map { |a| a.map { |c, _| c.to_i } } 5 | 6 | b = 0 7 | e = files.size - 1 8 | 9 | checksum = [] 10 | until files[b].zero? 11 | checksum += Array.new(files[b], b) 12 | 13 | free_space[b].times do 14 | e -= 1 while files[e].zero? && e > b 15 | break if e == b 16 | 17 | checksum << e 18 | files[e] -= 1 19 | end 20 | 21 | b += 1 22 | end 23 | 24 | checksum.each_with_index.map { |e, i| e * i }.sum 25 | end 26 | 27 | def part2(disk_map) 28 | checksum = disk_map.chars.map(&:to_i).each_with_index.map { |c, i| i.even? ? [c, i / 2] : [c, -1] } 29 | 30 | (0...checksum.size).reverse_each do |i| 31 | file_size, file = checksum[i] 32 | next if file == -1 33 | 34 | hole_index = checksum.index { |s, e| e == -1 && s >= file_size } 35 | next unless hole_index && hole_index < i 36 | 37 | hole_size, = checksum[hole_index] 38 | checksum[hole_index] = [file_size, file] 39 | checksum[i][1] = -1 40 | 41 | checksum.insert(hole_index + 1, [hole_size - file_size, -1]) if hole_size != file_size 42 | end 43 | 44 | checksum.map { |cnt, el| Array.new(cnt, el == -1 ? 0 : el) }.flatten.each_with_index.map { |e, i| e * i }.sum 45 | end 46 | 47 | disk_map = readline(chomp: true) 48 | 49 | puts part1 disk_map 50 | puts part2 disk_map 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advent of Code 2 | Advent of Code Solutions in C++, Rust, Java, Kotlin, Swift, Go, Ruby and Mojo 🔥 3 | 4 | |Year| Language | 5 | |--|--| 6 | | 2015 | Kotlin | 7 | | 2016 | Java | 8 | | 2017 | Go | 9 | | 2022 | Swift | 10 | | 2023 | C++, Rust, Mojo 🔥 | 11 | | 2024 | Ruby | 12 | --------------------------------------------------------------------------------