infinitesimal's recent activity
-
Comment on Steam games will now need to fully disclose kernel-level anti-cheat on store pages in ~games
-
Comment on Switching to Linux, looking for distro recommendations in ~tech
infinitesimal Fedora is not ideal when it comes to basic creature comforts like multimedia codecs and Steam and Nvidia-drivers and all the usual encumbered stuff since you have to fiddle with RPM Fusion and...Fedora is not ideal when it comes to basic creature comforts like multimedia codecs and Steam and Nvidia-drivers and all the usual encumbered stuff since you have to fiddle with RPM Fusion and sometimes the Fedora maintainers don't give the RPM Fusion ones a heads up.
I use both Fedora and Debian don't mind it, but it's a thing that Debian-likes don't have to deal with.
-
Comment on English still rules the world, but that’s not necessarily OK. Is it time to curb its power? in ~humanities.languages
infinitesimal What's wrong with four breads? Sure I have a loaf of bread and a murder of crows, but I only have two hands and one head. Measure words aren't universal in English either!What's wrong with four breads? Sure I have a loaf of bread and a murder of crows, but I only have two hands and one head. Measure words aren't universal in English either!
-
Comment on What small questions do you have that aren’t worth a full topic on their own? in ~talk
infinitesimal I'm breaking the trend here and telling you that I... take my glasses into the shower with me once a week (or whenever the lens have accumulated enough oil/sweat/gunk), give it a quick rinse in...I'm breaking the trend here and telling you that I... take my glasses into the shower with me once a week (or whenever the lens have accumulated enough oil/sweat/gunk), give it a quick rinse in the hot water spray, place it on the bathroom cart outside the shower, and after I'm done showering I then wipe it dry with a normal towel (before I dry myself with the same towel.) This is terrible for all the reasons that other posters have listed below, but... it really doesn't matter. I have been wearing the same pair of cheap plastic frame and slightly less cheap lens during my waking hours for the last 6 years, and I've been wearing glasses most of my life. You might get cosmetic scratches on your lenses doing what I do, but it doesn't affect your field of vision and can only be seen under close examination.
Just don't use any dirty cloth which will really smudge/scratch your lenses, and also avoid actually keeping your glasses in the shower with all the hot water and soaps and cleansers and whatnot, since in my experience that really will wear out your frames and lenses.
-
Comment on Blogging in Djot instead of Markdown in ~comp
infinitesimal You don't even need hard-wrapping to want a blank line preceding lists. Your editor can soft-wrap a piece of text to look like the above example and now it looks all ambiguous if you don't require...You don't even need hard-wrapping to want a blank line preceding lists. Your editor can soft-wrap a piece of text to look like the above example and now it looks all ambiguous if you don't require a blank line to precede a list. In general, I feel that block-level elements should be preceded by a blank line, with the exception of list items (which are even occasionally used as span-level elements in old-timey texts!)
My beef is probably with the tight/loose list distinction, where I don't see how you can specify whether a list with a single list item should be tight or loose. My first thought is that list items should be tight by default unless they contain multiple paragraphs or block-level elements, in which case that item is necessarily loose.
-
Comment on Program your finances: Command-line accounting in ~finance
infinitesimal In case a reader is unfamiliar with it, Fava is a web-based GUI for beancount, another plain text accounting system. The whole thing is just a pip install away. -
Comment on Day 10: Pipe Maze in ~comp.advent_of_code
infinitesimal I thought part 1 was straightforward but tedious (BFS, but every pipe shape has a different neighbor), and then I tapped out on part 2 and probably the rest of AoC2023. I tried to walk around the...I thought part 1 was straightforward but tedious (BFS, but every pipe shape has a different neighbor), and then I tapped out on part 2 and probably the rest of AoC2023. I tried to walk around the loop and look to my right until I hit another loop tile, which worked for the simplest examples. A small issue was that the loop could be oriented the opposite way (I'd just toggle looking left instead of right), but a bigger issue is that I don't think my code worked for corner pipes in some/many situations, and I didn't know how to fix it.
-
Comment on Day 9: Mirage Maintenance in ~comp.advent_of_code
infinitesimal Surprisingly easy, the most trouble I had was failing to parse negative numbers. While trying to diagnose why my answer was wrong, I added some debug statements... that corrupted my data structure...Surprisingly easy, the most trouble I had was failing to parse negative numbers. While trying to diagnose why my answer was wrong, I added some debug statements... that corrupted my data structure because it was mutable and I doubly modified them. After fixing that (by copying everything), I finally got to look at the stack and noticed I was missing all the negative numbers.
Kotlin
object Day09 { fun parse(input: String): List<List<Long>> { return input.lines().map { longsOf(it).toList() } } fun diff1(l: List<Long>) = l.windowed(2).map { it[1] - it[0] } fun diffN(l: List<Long>): List<List<Long>> { val result = mutableListOf(l) while (!result.last().all { it == 0L }) { result.add(diff1(result.last())) } return result } fun fillForward(triangle: List<List<Long>>): List<List<Long>> { val result = mutableListOf<List<Long>>(triangle.last() + listOf(0)) for (i in triangle.size - 2 downTo 0) { result.addFirst(triangle[i] + listOf(triangle[i].last() + result.first().last())) } return result } fun fillBackward(triangle: List<List<Long>>): List<List<Long>> { val result = mutableListOf<List<Long>>(listOf(0L) + triangle.last()) for (i in triangle.size - 2 downTo 0) { result.addFirst(listOf(triangle[i].first() - result.first().first()) + triangle[i]) } return result } fun part1(input: String): Long { val seqs = parse(input) return seqs.sumOf { fillForward(diffN(it)).first().last() } } fun part2(input: String): Long { val seqs = parse(input) return seqs.sumOf { fillBackward(diffN(it)).first().first() } } }
-
Comment on Day 8: Haunted Wasteland in ~comp.advent_of_code
infinitesimal I tried to do part 2 naively at first and when that worked on the example but not the input I tried to do the next simplest thing which then happened to work, but upon reading other comments I...I tried to do part 2 naively at first and when that worked on the example but not the input I tried to do the next simplest thing which then happened to work, but upon reading other comments I realized it wasn't "supposed" to work except the input was specially crafted for it to work.
Kotlin
object Day08 { fun parse(input: String): Pair<String, Map<String, Pair<String, String>>> { val (path0, graph0) = input.split("\n\n") val path = path0.trim() val graph = mutableMapOf<String, Pair<String, String>>() for (line in graph0.lines()) { val (node, left, right) = alnumsOf(line).toList() graph[node] = Pair(left, right) } return Pair(path, graph) } fun cycleOf(path: String) = iterator { var i = 0 while (true) { yield(path[i]) i = (i + 1) % path.length } } fun stepsOf(node: String, path: String, graph: Map<String, Pair<String, String>>): Int { val cycle = cycleOf(path) var numSteps = 0 var location = node while (!location.endsWith('Z')) { location = when (cycle.next()) { 'L' -> graph[location]!!.first 'R' -> graph[location]!!.second else -> error(Unit) } numSteps++ } return numSteps } fun part1(input: String): Int { val (path, graph) = parse(input) return stepsOf("AAA", path, graph) } fun part2(input: String): Long { val (path, graph) = parse(input) val starts = graph.keys.filter { it.endsWith('A') } val steps = starts.map { stepsOf(it, path, graph).toLong() } return steps.reduce { x, y -> lcm(x, y) } } }
-
Comment on Day 7: Camel Cards in ~comp.advent_of_code
infinitesimal Today felt straightforward enough. I initially thought I'd have to handwrite the branches in part 2, but I just tried adding up jokers and then copypasting part 1 logic and it worked so shrug....Today felt straightforward enough. I initially thought I'd have to handwrite the branches in part 2, but I just tried adding up jokers and then copypasting part 1 logic and it worked so shrug.
Kotlin
package aoc2023 object Day7 { enum class Card { Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace } enum class Type { HighCard, OnePair, TwoPair, ThreeKind, FullHouse, FourKind, FiveKind } fun p1TypeOf(cards: List<Card>): Type { val sizes = cards.groupBy { it }.map { it.value.size }.sortedDescending() return when (sizes[0]) { 5 -> Type.FiveKind 4 -> Type.FourKind 3 -> if (sizes[1] == 2) Type.FullHouse else Type.ThreeKind 2 -> if (sizes[1] == 2) Type.TwoPair else Type.OnePair 1 -> Type.HighCard else -> error(Unit) } } fun p2TypeOf(cards: List<Card>): Type { val numJacks = cards.groupBy { it }[Card.Jack]?.size ?: 0 if (numJacks == 5) { return Type.FiveKind } val sizes = cards.groupBy { it }.filter { it.key != Card.Jack }.map { it.value.size }.toMutableList() sizes.sortDescending() sizes[0] += numJacks return when (sizes[0]) { 5 -> Type.FiveKind 4 -> Type.FourKind 3 -> if (sizes[1] == 2) Type.FullHouse else Type.ThreeKind 2 -> if (sizes[1] == 2) Type.TwoPair else Type.OnePair 1 -> Type.HighCard else -> error(Unit) } } fun parse(input: String): List<Pair<List<Card>, Int>> { return input.trim().lines().map { line -> val (hand0, bid0) = line.split(" ") val bid = bid0.trim().toInt() val hand = hand0.trim().map { card0 -> when (card0) { '2' -> Card.Two '3' -> Card.Three '4' -> Card.Four '5' -> Card.Five '6' -> Card.Six '7' -> Card.Seven '8' -> Card.Eight '9' -> Card.Nine 'T' -> Card.Ten 'J' -> Card.Jack 'Q' -> Card.Queen 'K' -> Card.King 'A' -> Card.Ace else -> error(Unit) } } Pair(hand, bid) } } fun part1(input: String): Int { val sorted = parse(input).sortedWith { x0, y0 -> val x = x0.first val y = y0.first x.indices.fold(p1TypeOf(x).compareTo(p1TypeOf(y))) { acc, i -> if (acc != 0) acc else x[i].compareTo(y[i]) } } return sorted.mapIndexed { i, (_, bid) -> (i + 1) * bid }.sum() } fun part2(input: String): Int { val sorted = parse(input).sortedWith { x0, y0 -> val x = x0.first val y = y0.first x.indices.fold(p2TypeOf(x).compareTo(p2TypeOf(y))) { acc, i -> if (acc != 0) acc else { val xiOrd = if (x[i] == Card.Jack) -1 else x[i].ordinal val yiOrd = if (y[i] == Card.Jack) -1 else y[i].ordinal xiOrd.compareTo(yiOrd) } } } return sorted.mapIndexed { i, (_, bid) -> (i + 1) * bid }.sum() } }
-
Comment on Day 6: Wait For It in ~comp.advent_of_code
infinitesimal Nthing comments that this really would've been faster if you just manually typed the inputs and the loop in a REPL. Kotlin package aoc2023 object Day6 { fun parse(input: String): Pair<List<Int>,...Nthing comments that this really would've been faster if you just manually typed the inputs and the loop in a REPL.
Kotlin
package aoc2023 object Day6 { fun parse(input: String): Pair<List<Int>, List<Int>> { fun intsOf(s: String) = Regex("""\d+""").findAll(s).map { it.value.toInt() } val (times, dists) = input.trim().lines().map { intsOf(it).toList() }.toList() return Pair(times, dists) } fun part1(input: String): Int { val (times, dists) = parse(input) fun wins(time0: Int, dist0: Int) = (0..time0).fold(0) { acc, time -> if (time * (time0 - time) > dist0) acc + 1 else acc } return times.indices.map { wins(times[it], dists[it]) }.reduce { x, y -> x * y } } fun part2(input: String): Long { val (times, dists) = parse(input) val time0 = times.joinToString("") { it.toString() }.toLong() val dist0 = dists.joinToString("") { it.toString() }.toLong() fun wins(time0: Long, dist0: Long) = (0..time0).fold(0L) { acc, time -> if (time * (time0 - time) > dist0) acc + 1 else acc } return wins(time0, dist0) } }
-
Comment on Day 5: If You Give A Seed A Fertilizer in ~comp.advent_of_code
infinitesimal I initially used my part 1 solution to write a naive part 2 solution, which worked fine on the example but failed on the input for obvious reasons. After trying (and failing) to write a proper...I initially used my part 1 solution to write a naive part 2 solution, which worked fine on the example but failed on the input for obvious reasons. After trying (and failing) to write a proper solution using intervals and ending up with a bazillion intervals all looking wrong, I slept on it before getting a working solution: change the representation of intervals to (start, end, shift) and augment my intervals with sentinels (with a shift of 0) so that the entire 0 to Long.MAX_VALUE space is partitioned by intervals. The former simplified the arithmetic and the latter reduced 6 cases to 2, and then everything worked. 🎉
Kotlin
package aoc2023 object Day5 { data class Range(val start: Long, val end: Long, val shift: Long) fun parse(input: String): Pair<List<Long>, List<List<Range>>> { fun longsOf(s: String) = Regex("""\d+""").findAll(s).map { it.value.toLong() } fun rangeOf(s: String) = longsOf(s).toList().let { (dest, src, n) -> Range(src, src + n, dest - src) } fun augment(map: List<Range>): List<Range> { val sentinels = mutableListOf<Range>() if (map.first().start != 0L) { sentinels.add(Range(0, map.first().start, 0)) } for (i in map.indices.drop(1)) { if (map[i - 1].end != map[i].start) { sentinels.add(Range(map[i - 1].end, map[i].start, 0)) } } if (map.last().end != Long.MAX_VALUE) { sentinels.add(Range(map.last().end, Long.MAX_VALUE, 0)) } return (map + sentinels).sortedBy { it.start } } fun mapOf(s: String) = s.lines().drop(1).map { rangeOf(it) }.sortedBy { it.start }.let { augment(it) } val parts = input.trim().split("\n\n") val seeds = longsOf(parts.first()).toList() val maps = parts.drop(1).map { mapOf(it) } return Pair(seeds, maps) } fun part1(input: String): Long { val (seeds, maps) = parse(input) fun convert(seed: Long, maps: List<List<Range>>) = maps.fold(seed) { num, map -> val range = map.first { it.start <= num && num < it.end } num + range.shift } return seeds.minOf { convert(it, maps) } } fun part2(input: String): Long { val (seeds, maps) = parse(input) val chunks0 = seeds.chunked(2).map { (start, n) -> Range(start, start + n, 0) } fun convert1(chunk0: Range, map: List<Range>): List<Range> { val inChunks = ArrayDeque<Range>() inChunks.addLast(chunk0) val outChunks = ArrayDeque<Range>() while (inChunks.isNotEmpty()) { val chunk = inChunks.removeFirst() val range = map.first { it.start <= chunk.start && chunk.start < it.end } if (chunk.end <= range.end) { outChunks.addLast(Range(chunk.start + range.shift, chunk.end + range.shift, 0)) } else { outChunks.addLast(Range(chunk.start + range.shift, range.end + range.shift, 0)) inChunks.addLast(Range(range.end, chunk.end, 0)) } } return outChunks } return maps.fold(chunks0) { chunks, map -> chunks.flatMap { chunk -> convert1(chunk, map) } }.minOf { it.start } } }
-
Comment on Day 4: Scratchcards in ~comp.advent_of_code
infinitesimal I can't find Kotlin's integer exponent operator or function. Part 2 also took a whole 1.5 seconds to do naively before I read the first comment by @lily and only tracked the counts after which it...I can't find Kotlin's integer exponent operator or function. Part 2 also took a whole 1.5 seconds to do naively before I read the first comment by @lily and only tracked the counts after which it went back to instantaneous.
Kotlin
import kotlin.math.pow object Day4 { data class Card(val id: Int, val winners: Set<Int>, val havers: Set<Int>) private fun parse(line: String) = let { fun numsOf(s: String) = Regex("""\d+""").findAll(s).map { it.value.toInt() }.toSet() fun idOf(s: String) = Regex("""\d+""").find(s)!!.value.toInt() line.split(":").let { val (winners0, havers0) = it[1].split("|") Card(idOf(it[0]), numsOf(winners0), numsOf(havers0)) } } fun part1(lines: List<String>) = lines.sumOf { line -> val (_, winners, havers) = parse(line) val inter = winners.intersect(havers) if (inter.isNotEmpty()) 2.0.pow(inter.size - 1).toInt() else 0 } fun part2(lines: List<String>) = let { val cards = lines.map { parse(it) } val counts = Array<Int>(cards.size + 1) { if (it == 0) 0 else 1 } cards.forEach { (id, winners, havers) -> ((id + 1)..(id + winners.intersect(havers).size)).forEach { counts[it] += counts[id] } } counts.sum() } val input = javaClass.getResourceAsStream("${javaClass.name}.txt")!!.reader().readLines() } fun main() { with(Day4) { println(part1(input)) println(part2(input)) } }
-
Comment on Day 3: Gear Ratios in ~comp.advent_of_code
infinitesimal Just wondering, do people ever use fancy spatial data structures for these 2D (or eventually 3D) AoC grid problems instead of just nested loops? Kotlin object Day3 { data class Number(val i: Int,...Just wondering, do people ever use fancy spatial data structures for these 2D (or eventually 3D) AoC grid problems instead of just nested loops?
Kotlin
object Day3 { data class Number(val i: Int, val row: Int, val col: IntRange) data class Symbol(val c: Char, val row: Int, val col: Int) data class Schematic(val nums: List<Number>, val syms: List<Symbol>) private fun parse(lines: List<String>) = lines.mapIndexed { row, line -> val nums = Regex("""\d+""").findAll(line).map { Number(it.value.toInt(), row, it.range) } val syms = Regex("""[^\d.]""").findAll(line).map { Symbol(it.value.first(), row, it.range.first) } Schematic(nums.toList(), syms.toList()) }.reduce { prev, next -> Schematic(prev.nums + next.nums, prev.syms + next.syms) } private fun isAdjacent(num: Number, sym: Symbol) = let { val vert = (num.row - 1)..(num.row + 1) val horz = num.col + (num.col.first - 1) + (num.col.last + 1) val neighborhood = vert.flatMap { row -> horz.map { col -> Pair(row, col) } } Pair(sym.row, sym.col) in neighborhood } fun part1(lines: List<String>) = let { val (nums, syms) = parse(lines) nums.filter { num -> syms.any { sym -> isAdjacent(num, sym) } }.sumOf { num -> num.i } } fun part2(lines: List<String>) = let { val (nums, syms) = parse(lines) syms.filter { it.c == '*' }.sumOf { sym -> val neighbors = nums.filter { num -> isAdjacent(num, sym) }.toList() if (neighbors.size == 2) neighbors[0].i * neighbors[1].i else 0 } } }
-
Comment on Day 2: Cube Conundrum in ~comp.advent_of_code
infinitesimal Yesterday I hadn't written a line of Kotlin before. Today I learned more of its conveniences. Kotlin class Day2 { // Parse each line into a game and filter games where all bags are less than the...Yesterday I hadn't written a line of Kotlin before. Today I learned more of its conveniences.
Kotlin
class Day2 { // Parse each line into a game and filter games where all bags are less than the limit. Then sum the game ids. fun part1(lines: List<String>) = let { val limit = mapOf("red" to 12, "green" to 13, "blue" to 14) fun isPossible(game: Game) = game.bags.all { bag -> bag.entries.all { (k, v) -> v <= limit[k]!! } } lines.map { parseGame(it) }.filter { isPossible(it) }.sumOf { it.id } } // Parse each line into a game and take the union of all bags in a game. Take the product of each bag's counts and // then sum the products. fun part2(lines: List<String>) = let { fun Map<String, Int>.union(that: Map<String, Int>) = (this.keys + that.keys).associateWith { (this[it] ?: 0).coerceAtLeast(that[it] ?: 0) } lines.sumOf { val game = parseGame(it) val bag = game.bags.reduce { prev, next -> prev.union(next) } val power = bag.values.reduce { prev, next -> prev * next } power } } data class Game(val id: Int, val bags: List<Map<String, Int>>) private fun parseGame(line: String) = let { fun parseBag(s: String) = Regex("""(\d+) (\w+)""").findAll(s).map { it.groupValues[2] to it.groupValues[1].toInt() }.toMap() fun parseBags(s: String) = s.split(";").map { parseBag(it) } Regex("""Game (\d+): (.*)""").find(line)!!.groupValues.let { Game(it[1].toInt(), parseBags(it[2])) } } val input = javaClass.getResourceAsStream("${javaClass.name}.txt")!!.reader().readLines() } fun main() { val example1 = """ Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green """.trimIndent().lines() val example1Expected1 = 8 val example2Expected2 = 2286 val inputExpected1 = 2331 val inputExpected2 = 71585 val day = Day2() check(day.part1(example1) == example1Expected1) check(day.part2(example1) == example2Expected2) day.part1(day.input).also { println(it) }.also { check(it == inputExpected1) } day.part2(day.input).also { println(it) }.also { check(it == inputExpected2) } }
-
Comment on Day 1: Trebuchet?! in ~comp.advent_of_code
infinitesimal Decided to use Kotlin and see how far I get this year. I got tripped up on part 2 and had to switch from simple replacement to regex: import java.util.regex.Pattern fun main() { /* For each line,...Decided to use Kotlin and see how far I get this year. I got tripped up on part 2 and had to switch from simple replacement to regex:
import java.util.regex.Pattern fun main() { /* For each line, filter the digits, concatenate the first and last digits, and then add it to the result. */ fun part1(input: List<String>): Int { var result = 0 for (line in input) { val digits = line.filter { it.isDigit() }.map { it.digitToInt() } result += digits[0] * 10 + digits[digits.size - 1] } return result } check(part1(readExample(1, 1)) == 142) val output1 = part1(readInput(1)) println(output1) check(output1 == 53334) /* For each line, generate digits using a map, concatenate the first and last digits, and then add it to the result. */ fun part2(input: List<String>): Int { var result = 0 val map = mapOf( "one" to 1, "two" to 2, "three" to 3, "four" to 4, "five" to 5, "six" to 6, "seven" to 7, "eight" to 8, "nine" to 9 ).plus((0 until 10).map { it.toString() to it }) val pattern = Pattern.compile(map.keys.joinToString("|")) for (line in input) { val digits = arrayListOf<Int>() val matcher = pattern.matcher(line) while (matcher.find()) { digits.addLast(map[matcher.group()]) } result += digits[0] * 10 + digits[digits.size - 1] } return result } check(part2(readExample(1, 2)) == 281) val output2 = part2(readInput(1)) println(output2) check(output2 == 52834) }
"Something happening" is an eventuality, but dealing with cheaters is an actuality. Just compare Counter-Strike and Valorant for effectiveness of kernel-level anticheats like Vanguard. People that don't want to deal with more effective anti-cheat in competitive multiplayer games can just not play. (But I do think the new disclosure policy is good and none of this is necessary for single-player games.)