• 2 Posts
  • 94 Comments
Joined 2 years ago
cake
Cake day: June 17th, 2023

help-circle

  • I also solved part2 manually today, by outputting the correct addition and program output in binary and then reversing the path for all wrong outputs.

    Then I just had to compare that to the formulas for a full adder and I found my swaps pretty quickly that way.

    I did all of this in Kotlin and on paper, with a handy helper method to construct the paths.

    It’s at the very bottom of this file on Github.

    I suspect that comparing the bits and then comparing the paths to the full adder formulas can also be done ‘automatically’ decently easily, but I was too lazy to implement that today.


  • Kotlin

    Part1 was pretty simple, just check all neighbors of a node for overlap, then filter out triples which don’t have nodes beginning with ‘t’.

    For part2, I seem to have picked a completely different strategy to everyone else. I was a bit lost, then just boldly assumed, that if I take overlap of all triples with 1 equal node, I might be able to find the answer that way. To my surprise, this worked for my input. I’d be very curious to know if I just got lucky or if the puzzle is designed to work with this approach.

    The full code is also on GitHub.

    Solution
    class Day23 : Puzzle {
    
        private val connections = ArrayList<Pair<String, String>>()
    
        private val tripleCache = HashSet<Triple<String, String, String>>()
    
        override fun readFile() {
            val input = readInputFromFile("src/main/resources/day23.txt")
            for (line in input.lines()) {
                val parts = line.split("-")
                connections.add(Pair(parts[0], parts[1]))
            }
        }
    
        override fun solvePartOne(): String {
            val triples = getConnectionTriples(connections)
            tripleCache.addAll(triples) // for part 2
            val res = triples.count { it.first.startsWith("t") || it.second.startsWith("t") || it.third.startsWith("t") }
            return res.toString()
        }
    
        private fun getConnectionTriples(connectionList: List<Pair<String, String>>): List<Triple<String, String, String>> {
            val triples = ArrayList<Triple<String, String, String>>()
            for (connection in connectionList) {
                val connectionListTemp = getAllConnections(connection.first, connectionList)
                for (i in connectionListTemp.indices) {
                    for (j in i + 1 until connectionListTemp.size) {
                        val con1 = connectionListTemp[i]
                        val con2 = connectionListTemp[j]
                        if (Pair(con1, con2) in connectionList || Pair(con2, con1) in connectionList) {
                            val tripleList = mutableListOf(connection.first, con1, con2)
                            tripleList.sort()
                            triples.add(Triple(tripleList[0], tripleList[1], tripleList[2]))
                        }
                    }
                }
            }
            return triples.distinct()
        }
    
        private fun getAllConnections(connection: String, connectionList: List<Pair<String, String>>): List<String> {
            val res = HashSet<String>()
            for (entry in connectionList) {
                when (connection) {
                    entry.first -> res.add(entry.second)
                    entry.second -> res.add(entry.first)
                }
            }
            return res.toList()
        }
    
        override fun solvePartTwo(): String {
            val pools = getPools(connections)
            println(pools)
            val res = pools.maxByOrNull { it.size }!!
            return res.joinToString(",")
        }
    
        // will get all pools with a minimum size of 4
        // this method makes some naive assumptions, but works for the example and my puzzle input
        private fun getPools(connectionList: List<Pair<String, String>>): List<List<String>> {
            val pools = ArrayList<List<String>>()
            val triples = tripleCache
            val nodes = connectionList.map { listOf(it.first, it.second) }.flatten().toHashSet()
    
            for (node in nodes) {
                val contenders = triples.filter { it.first == node || it.second == node || it.third == node }
                if (contenders.size < 2) continue // expect the minimum result to be 4, for efficiency
    
                // if *all* nodes within *all* triples are interconnected, add to pool
                // this may not work for all inputs!
                val contenderList = contenders.map { listOf(it.first, it.second, it.third) }.flatten().distinct()
                if (checkAllConnections(contenderList, connectionList)) {
                    pools.add(contenderList.sorted())
                }
            }
    
            return pools.distinct()
        }
    
        private fun checkAllConnections(pool: List<String>, connectionList: List<Pair<String, String>>): Boolean {
            for (i in pool.indices) {
                for (j in i + 1 until pool.size) {
                    val con1 = pool[i]
                    val con2 = pool[j]
                    if (Pair(con1, con2) !in connectionList && Pair(con2, con1) !in connectionList) {
                        return false
                    }
                }
            }
            return true
        }
    }
    




  • I was pretty neutral towards Ubuntu, up until an automatic system update removed my deb Firefox and replaced it with the snap version, even though I specifically set the apt repo to a higher priority.

    The entire reason I left Windows is because I don’t want (for example) Edge shoved down my throat after every update, and yet Ubuntu has gone and done the exact same thing with snaps.

    After literal hours of fighting, the only solution I found was to fully disable automatic updates. With Pop OS I have all the benefits of Ubuntu, but I also get a company (System76) that does cool stuff and doesn’t try shoving snaps down my throat.






  • What worked for me at my old school was using a ShadowSocks proxy. Basically what this does, is it takes all your traffic and just makes it look like random https traffic (AFAIK). ShadowSocks is just a proxy. The description fits the Cloak module, mentioned below.

    I believe multiple VPNs support this, for me with PIA VPN it’s in the settings under the name “Multi-Hop” (PIA only supports this on the Desktop App, not on mobile).

    This technique is pretty much impossible to block, unless you ban every single VPN ShadowSocks Proxy IP. If that is the case for you (chances are practically 0), you could also selfhost ShadowSocks in combination with the Cloak module, however this method is a lot more complicated.








  • TVHeadend is the way, I’ve been running it with a USB satellite tuner for 5+ years. Setting it up can be a little confusing, but once it’s running you pretty much never have to touch it again.

    As for clients, there’s a Jellyfin plugin, however it seems to not work for me right now.

    My client of choice is Kodi with the TVHeadend plugin, and that works great. If you still want Jellyfin integration, you could just add your recordings folder as a library in Jellyfin.


  • Could I purchase two different brand drives and use them with btrfs?

    I don’t quite remember the source for this, but I believe I read some time ago that it’s actually a good thing to have separate drives. The reasoning is, if you buy two identical drives (at the same time), the likelyhood of both drives failing around the same time is severely higher.

    This is then amplified by the fact that rebuilding a RAID puts a lot of strain on the non-dead drive, so if ie. drive 1 dies and drive 2 is about to die, the strain you put on drive 2 in order to rebuild your RAID onto drive 3 might kill drive 2 before you even finish rebuilding your RAID.

    Again, this is just from my memory, it might be worth doing some more research on.