Advent of Code 2021 day 7
12/07/2022, 5:00 AMDavid Whittaker
12/07/2022, 5:38 AMJakub Gwóźdź
12/07/2022, 5:38 AMMichael Böiers
12/07/2022, 5:42 AMDavid Whittaker
12/07/2022, 5:42 AMJacob Moulton
12/07/2022, 5:42 AMDavid Whittaker
12/07/2022, 5:42 AMwhen
..."ls" -> { /* no op */}
Jakub Gwóźdź
12/07/2022, 5:43 AMDavid Whittaker
12/07/2022, 5:43 AMMichael Böiers
12/07/2022, 5:43 AMDavid Whittaker
12/07/2022, 5:43 AMJakub Gwóźdź
12/07/2022, 5:44 AMJacob Moulton
12/07/2022, 5:46 AMMichael Böiers
12/07/2022, 5:46 AMMarcin Wisniowski
12/07/2022, 5:46 AMMichael Böiers
12/07/2022, 5:46 AMpackage aoc2022
fun main() = day07(String(System.`in`.readAllBytes())).forEach(::println)
private fun day07(input: String): List<Any?> {
val currentPath = mutableListOf<String>()
val sizes = mutableMapOf<String, Long>()
var used = 0L
for (line in input.lines()) {
if (line.startsWith("$ cd ")) with(currentPath) {
when (val to = line.substringAfterLast(" ")) {
".." -> removeLast()
"/" -> clear()
else -> add(to)
}
}
else {
val size = line.split(" ").first().toIntOrNull() ?: continue
var dir = "/"
for (e in currentPath) {
if (!dir.endsWith("/")) dir += "/"
dir += e
sizes.compute(dir) { _, prev -> (prev ?: 0L) + size }
}
used += size
}
}
val total = 70000000L
val needed = 30000000L
val free = total - used
val toFree = needed - free
return with(sizes.values) {
listOf(filter { it <= 100000 }.sum(), filter { it >= toFree }.min())
}
}
Dan Fingal-Surma
12/07/2022, 5:46 AMDan Fingal-Surma
12/07/2022, 5:47 AMMarcin Wisniowski
12/07/2022, 5:47 AMDan Fingal-Surma
12/07/2022, 5:47 AMDan Fingal-Surma
12/07/2022, 5:47 AMDan Fingal-Surma
12/07/2022, 5:48 AMMarcin Wisniowski
12/07/2022, 5:48 AMsealed class Node
with Directory
and File
subclasses, then realized File
is not needed.Dan Fingal-Surma
12/07/2022, 5:51 AMDan Fingal-Surma
12/07/2022, 6:02 AMSergei Petunin
12/07/2022, 6:04 AMclass Directory(val parent: Directory?) {
var totalSize: Int = 0
val childDirs: MutableMap<String, Directory> = mutableMapOf()
fun find(predicate: (Directory) -> Boolean): List<Directory> = buildList {
if (predicate(this@Directory)) {
add(this@Directory)
}
addAll(childDirs.values.flatMap { dir -> dir.find(predicate) })
}
fun updateTotalSizes(): Int {
totalSize += childDirs.values.sumOf { it.updateTotalSizes() }
return totalSize
}
}
fun buildGraph(input: List<String>) = Directory(null).apply {
var current: Directory = this
input.forEach { line ->
current = when {
line == "$ cd /" -> this
line == "$ ls" -> current
line == "$ cd .." -> current.parent!!
line.startsWith("$ cd ") -> current.childDirs[line.substringAfter("$ cd ")]!!
line.startsWith("dir ") -> current.apply {
childDirs[line.substringAfter("dir ")] = Directory(current)
}
line[0].isDigit() -> current.apply { totalSize += line.split(" ")[0].toInt() }
else -> throw RuntimeException("Can't parse: $line")
}
}
updateTotalSizes()
}
fun part1(input: List<String>): Int = buildGraph(input)
.find { it.totalSize <= 100000 }
.sumOf { it.totalSize }
fun part2(input: List<String>): Int {
val root = buildGraph(input)
val excess = 30000000 - (70000000 - root.totalSize)
return root.find { it.totalSize >= excess }
.minBy { it.totalSize }
.totalSize
}
David Whittaker
12/07/2022, 6:05 AMDan Fingal-Surma
12/07/2022, 6:07 AMMarcin Wisniowski
12/07/2022, 6:07 AMdir
. Every directory is only entered once, so you can create them as you enter. Can also just drop the first line cd /
.Jakub Gwóźdź
12/07/2022, 6:08 AMsealed interface TreeNode {
val size: Int
val content: List<TreeNode>
}
data class FileNode(override val size: Int) : TreeNode {
override val content: List<TreeNode> get() = emptyList()
}
data class DirNode(override val content: MutableList<TreeNode> = mutableListOf()) : TreeNode {
override val size: Int get() = content.sumOf { it.size }
}
private fun parse(input: String): Collection<DirNode> {
val filesystem = mutableMapOf("/" to DirNode())
input.lineSequence().filterNot(String::isBlank)
.fold("/") { cwd, command ->
if (command.startsWith("$ cd ")) {
cwd.pathResolve(command.substringAfter("$ cd "))
} else {
if (command.startsWith("dir ")) {
val dirNode = DirNode()
filesystem[cwd.pathResolve(command.substringAfter("dir "))] = dirNode
filesystem[cwd]!!.content += dirNode
} else if (command.first().isDigit()) {
filesystem[cwd]!!.content += FileNode(command.substringBefore(" ").toInt())
} else require(command == "$ ls") { "WTF `$command`" }
cwd
}
}
return filesystem.values
}
As you can see, it’s not completely functional, as the .fold()
lambda updates tree
that is defined outside of the closure.
but otherwise, I’m pretty content with that.Dan Fingal-Surma
12/07/2022, 6:09 AMls
the same dir more than once… didn’t guard against that lolMarcin Wisniowski
12/07/2022, 6:10 AMDan Fingal-Surma
12/07/2022, 6:14 AMJonathan Kolberg
12/07/2022, 6:22 AMBrian Hartvigsen
12/07/2022, 6:22 AMTimmy
12/07/2022, 6:29 AMKai Yuan
12/07/2022, 6:53 AMGrzegorz Aniol
12/07/2022, 7:09 AMMonster Brain
12/07/2022, 7:12 AMGrzegorz Aniol
12/07/2022, 7:13 AMMichael de Kaste
12/07/2022, 7:24 AMobject Day7 : Challenge() {
private val root = Directory().also { root ->
input.lineSequence()
.map { it.split(" ") + "" } // Why no destructuring to nullable?
.fold(root) { directory, (a, b, c) ->
when {
a == "$" && b == "cd" && c == "/" -> root
a == "$" && b == "cd" && c == ".." -> directory.parent!!
a == "$" && b == "cd" -> directory.directories.getValue(c)
a == "$" && b == "ls" -> directory
a == "dir" -> directory.apply { directories[b] = Directory(directory) }
else -> directory.apply { updateDirectory(a.toInt()) }
}
}
}
override fun part1() = root.getAllDirectorySizes()
.filter { it <= 100000 }
.sum()
override fun part2() = root.getAllDirectorySizes()
.filter { it >= root.fileSize - 40000000 }
.minOrNull()
}
class Directory(
val parent: Directory? = null,
val directories: MutableMap<String, Directory> = mutableMapOf(),
var fileSize: Long = 0
) {
fun getAllDirectorySizes(): Sequence<Long> = sequenceOf(fileSize) +
directories.values.flatMap(Directory::getAllDirectorySizes)
fun updateDirectory(sizeOfFile: Int) {
fileSize += sizeOfFile
parent?.updateDirectory(sizeOfFile)
}
}
Neil Banman
12/07/2022, 7:25 AMJoris PZ
12/07/2022, 7:26 AMfun searchDirectories(selector: (Directory) -> Boolean): List<Directory> {
val result = mutableListOf<Directory>()
if (selector(this)) result.add(this)
result.addAll(items.values.filterIsInstance<Directory>().flatMap { it.searchDirectories(selector) })
return result
}
Solution for part 1 is then `
root.searchDirectories { it.size <= 100000 }.sumOf { it.size }
and for part 2
root.searchDirectories { it.size >= toDelete }.minBy { it.size }
Cognitive Gear
12/07/2022, 7:27 AMrkechols
12/07/2022, 7:29 AMPoisonedYouth
12/07/2022, 7:47 AMMichael Böiers
12/07/2022, 7:52 AMpackage aoc2022
fun main() = day07(String(System.`in`.readAllBytes())).forEach(::println)
private fun day07(input: String): List<Any?> {
var dir = listOf<String>()
val sizes = mutableMapOf<List<String>, Int>()
var used = 0
fun List<String>.cd(dir: String) = when (dir) {
".." -> subList(0, size - 1)
"/" -> listOf()
else -> plus(dir)
}
fun List<String>.addSize(size: Int) = indices.map { subList(0, it + 1) }.forEach { path ->
sizes[path] = (sizes[path] ?: 0) + size
}
for (line in input.lines()) when {
line.startsWith("$ cd ") -> dir = <http://dir.cd|dir.cd>(line.substringAfterLast(" "))
else -> line.split(" ").first().toIntOrNull()?.also(dir::addSize)?.also { used += it }
}
return with(sizes.values) {
listOf(filter { it <= 100_000 }.sum(), filter { it >= used - 40_000_000 }.min())
}
}
Harald Sperre
12/07/2022, 8:03 AMMichael Böiers
12/07/2022, 8:03 AMDavio
12/07/2022, 8:16 AMprivate fun <T> recursiveVisit(
dir: Directory,
filter: (Directory) -> Boolean,
defaultValue: T,
combinator: (T, T) -> T, operation: (Directory) -> T
): T {
var result = if (filter.invoke(dir)) operation.invoke(dir) else defaultValue
dir.subdirectories.forEach { subdir ->
result = combinator.invoke(result, recursiveVisit(subdir, filter, defaultValue, combinator, operation))
}
return result
}
Davio
12/07/2022, 8:17 AMrecursiveVisit(root, { dir -> dir.getTotalSize() < 100000L }, 0L, { left, right -> left + right }) {
it.getTotalSize()
}
For part 1 and for part 2:
recursiveVisit(root, { dir ->
dir.getTotalSize() >= spaceToFree
}, Long.MAX_VALUE, { left, right -> minOf(left, right) }) {
it.getTotalSize()
}
Davio
12/07/2022, 8:19 AMephemient
12/07/2022, 8:22 AMephemient
12/07/2022, 8:22 AM"/a/b/c"
paths or strip the leading `/`; in the end I decided it was easier to store paths as "a/b/c"
, since this meant that finding the parent directory could simply be .substringBeforeLast('/', "")
Riccardo Lippolis
12/07/2022, 8:23 AMcd /
commands and ls
commands in the same directory, even though that was apparently not needed, robustness FTW! metalDavio
12/07/2022, 8:23 AMcd /
😄Davio
12/07/2022, 8:23 AMephemient
12/07/2022, 8:24 AM$ ls
and dir
, so if a file shows up multiple times I'll count it multiple timesMichael Böiers
12/07/2022, 8:24 AMcd /
, directory names etc. 😇😅ephemient
12/07/2022, 8:24 AMephemient
12/07/2022, 8:25 AM/
or doing cd ..
while at /
works in mineMichael Böiers
12/07/2022, 8:25 AMDavio
12/07/2022, 8:25 AMRiccardo Lippolis
12/07/2022, 8:25 AMzsmb
12/07/2022, 8:25 AMjava.io.File
and its great Kotlin extensions, and actually created all the files 🙃
https://github.com/zsmb13/advent-of-code-2022/blob/main/src/Day07.kt
(Yes I did check beforehand that it would be some reasonable size (~40MB), though the second part ended up ensuring that it’s all quite reasonably sized)Davio
12/07/2022, 8:26 AMMichael Böiers
12/07/2022, 8:26 AMcd ..
while at /
would break my solution, which makes kind of sense. For AoC we build solutions that expect well-formed input.Davio
12/07/2022, 8:26 AMRiccardo Lippolis
12/07/2022, 8:26 AMephemient
12/07/2022, 8:27 AMcd ..
at /
is totally fine thoughephemient
12/07/2022, 8:27 AMDavio
12/07/2022, 8:28 AMcd /../../../../../
Michael Böiers
12/07/2022, 8:28 AMDavio
12/07/2022, 8:28 AMMichael Böiers
12/07/2022, 8:29 AMfun List<String>.cd(dir: String) = when (dir) {
".." -> if (isEmpty()) this else subList(0, size - 1)
"/" -> listOf()
else -> plus(dir)
}
ritesh
12/07/2022, 8:29 AMtree
out of it and performed dfs
ephemient
12/07/2022, 8:31 AM".." -> dropLast(1)
"/" -> emptyList()
but it's really the sameAlex J.
12/07/2022, 8:31 AMDavio
12/07/2022, 8:32 AMDirectory
data class with its subdirectories and files lists, you only need the files with their full paths, kind of like how zip files and git work and then you could always derive the directories from their pathsMichael Böiers
12/07/2022, 8:35 AMxxfast
12/07/2022, 8:48 AMtoString()
because of a stackoverflow?ritesh
12/07/2022, 8:48 AMMichael Böiers
12/07/2022, 8:48 AMMichael Böiers
12/07/2022, 8:50 AMcopy
method - not just the data classes.Michael Böiers
12/07/2022, 8:54 AMDavio
12/07/2022, 9:02 AM@Copyable
class Foo(val s: String)
And it would generate an extension function fun Foo.copy(s: String) = Foo(s = s)
Michael Böiers
12/07/2022, 9:06 AMritesh
12/07/2022, 9:07 AMJan Durovec
12/07/2022, 9:33 AMFsNode
class (with map of children as property) and I'm trying to add a find
function to it accepting a predicate that runs BFS and returns all nested child nodes matching the predicate. I managed to do it in a way that it returns a List
however, I'd like to change it to a Sequence
so that it is lazily evaluated (and I practice how to use sequences/yield).
My current code starts like
fun find(predicate: Predicate<FsNode>): List<FsNode> {
val toExplore = mutableListOf(this)
and I tried changing it to
fun find(predicate: Predicate<FsNode>): Sequence<FsNode> = sequence {
val toExplore = mutableListOf(this)
The problem is that this
now refers to SequenceScope
not to the encapsulating class. Can someone please advise how to access FsNode.this
inside the sequence code or how to generate this as a sequence properly?Davio
12/07/2022, 9:36 AMJan Durovec
12/07/2022, 9:38 AMJakub Gwóźdź
12/07/2022, 9:52 AM.fold()
closure? YES ✅
• does it use bisect to find the best candidate in second part? ALSO YES✅ (talking about optimization priorities here xd)Monster Brain
12/07/2022, 9:59 AMfrascu
12/07/2022, 10:18 AMxxfast
12/07/2022, 10:27 AMJan Durovec
12/07/2022, 11:07 AM.minBy { it.size }.size
can be simplified to .minOf { it.size }
phldavies
12/07/2022, 11:17 AMMarcin Wisniowski
12/07/2022, 11:43 AMPaul Woitaschek
12/07/2022, 12:04 PMPaul Woitaschek
12/07/2022, 12:05 PMdir
and ls
commands completelyRafal Bednarczuk
12/07/2022, 12:19 PMphldavies
12/07/2022, 1:22 PMacc[0] += n
on an ArrayDeque
🙂Michael Böiers
12/07/2022, 1:23 PMpartialWindows
) 🙂wakingrufus
12/07/2022, 1:37 PM..
Within a bigger path, revisiting directories, etc https://github.com/wakingrufus/advent-of-code-2022/blob/main/src/main/kotlin/com/github/wakingrufus/aoc/day7/Day7.ktLuke
12/07/2022, 4:11 PMSequence
, .fold
and unnecessary regexes. I like regexes. Not purely functional thoughNeil Banman
12/07/2022, 4:21 PMOzioma Ogbe
12/07/2022, 4:30 PMAnirudh
12/07/2022, 4:34 PMcurrDir = currDir + nextDir
or currDir = currDir.dropLast(1)
◦ along with a dataclass for subdirs and files. also had global mutable state etc. which felt quite disgusting
• removed all that and put path as string with "/a/b.txt" etc. each pointing to just the size; or 0 for dirs.
• I didn't want to just ignoring dirs in the listing
◦ but before seeing part 2, it's unclear how much we'd need to refactor and retro-refactoring based on part 2's requirements and what is finally not needed at all feels a bit like cheating
• (I noticed that there's no 'cd /' anywhere except at the top)
https://github.com/aneroid/advent-of-code-2022-kotlin/blob/main/src/Day07.ktFredrik Rødland
12/07/2022, 4:57 PMStack
to know which directory I was in at any given time) to Dirs and Files, including their paths.
noticed every directory was only cd-ed
to once.
then I could calculate sizes by just filtering for data. made Node
Dir
and File
classes mostly just to have a conceptual image of the data.
https://github.com/fmmr/advent/blob/master/src/main/kotlin/no/rodland/advent_2022/Day07.ktPoisonedYouth
12/07/2022, 5:39 PMtodd.ginsberg
12/07/2022, 5:40 PMPoisonedYouth
12/07/2022, 5:50 PMtodd.ginsberg
12/07/2022, 6:22 PMAbdul Khaliq
12/07/2022, 6:27 PMAbdul Khaliq
12/07/2022, 6:32 PMLuke Armitage
12/07/2022, 8:40 PMJacob
12/07/2022, 9:11 PMKarloti
12/07/2022, 9:18 PMphldavies
12/07/2022, 9:27 PMPaul Woitaschek
12/07/2022, 9:29 PMphldavies
12/07/2022, 9:40 PMLeon Linhart
12/07/2022, 10:19 PMkotlin.io.path
package is quite convenient! 😅
https://github.com/TheMrMilchmann/AdventOfCode2022/blob/master/src/main/kotlin/days/Day07Silly.ktFredrik Rødland
12/07/2022, 11:10 PMFredrik Rødland
12/07/2022, 11:15 PMwakingrufus
12/07/2022, 11:15 PMwakingrufus
12/07/2022, 11:18 PMDan Fingal-Surma
12/08/2022, 12:34 AMDan Fingal-Surma
12/08/2022, 12:35 AMDan Fingal-Surma
12/08/2022, 12:38 AMDan Fingal-Surma
12/08/2022, 12:50 AMEdward Muller
12/08/2022, 2:53 AM