Skip to content

Commit

Permalink
Practice some Kotlin by doing 2018 problems
Browse files Browse the repository at this point in the history
  • Loading branch information
markjfisher committed Nov 28, 2023
1 parent 2b714cd commit b0ed1a7
Show file tree
Hide file tree
Showing 34 changed files with 4,368 additions and 19 deletions.
90 changes: 89 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,92 @@ git config --global core.autocrlf input
# if you had checked it out without setting above first, you need to fix files with CRLF into LF:
git rm -rf --cached .
git reset --hard HEAD
```
```

## coding notes

### Reading resources

1. column of data, separator is EOL

Example 2022 02

```kotlin
// simply split into List<String>
resourceLines(2022, 2)

// then can deal with each line
data.fold(0) { acc, l ->
val (a,b) = l.split(" ", limit = 2)
// ... etc
```

1. column data with blank line separator

Example, 2022 01

```text
5538
6760

5212
2842
```

```kotlin
// first a list of strings for each block (which still have \n in them)
private val data by lazy { totals(resourceStrings(2022, 1)) }

// Then split by \n
data.map { block ->
block.split("\n").sumOf { f -> f.toInt() }
}
```

### Parsing data with regex

Example where each line contains information we want to extract parts out of
```text
12-80,12-81
13-94,14-93
...
```
Use `resourceLines` to pull each line, and then map that through a regex "Extractor"
```kotlin
private val assignmentExtractor by lazy { Regex("""(\d+)-(\d+),(\d+)-(\d+)""") }
private val data by lazy { toAssignments(resourceLines(2022, 4)) }

fun toAssignments(data: List<String>): List<Assignments> = data.map { line ->
assignmentExtractor.find(line)?.destructured!!.let { (a, b, c, d) ->
Assignments(IntRange(a.toInt(), b.toInt()), IntRange(c.toInt(), d.toInt()))
}
}
```
Example 2022 04

### Grid data

See GridDataUtils for parsing grid information from lines where each point represents 1 datum, either as numbers:

```text
1234
2256
1978
```

or as characters:

```text
.x.
yz.
```

#### Grid viewing

There are some good examples of looking from a point to a boundary in 2022 Day 08, using `generateSequence` on points in a direction until they hit the boundary.

```kotlin
private fun pointsToBoundaryInDir(p: Point, dir: Direction): Sequence<Point> {
return generateSequence(p + dir) { it + dir }.takeWhileInclusive { !onBoundary(it) }
}
```
1 change: 1 addition & 0 deletions advents/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-engine:$junitJupiterEngineVersion")
testImplementation("org.assertj:assertj-core:$assertJVersion")
testImplementation("io.mockk:mockk:$mockkVersion")
testImplementation("org.junit.jupiter:junit-jupiter:5.8.1")
}

tasks {
Expand Down
8 changes: 8 additions & 0 deletions advents/src/main/kotlin/net/fish/collections/IntRangeExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package net.fish.collections

fun IntRange.fullyContains(t: IntRange): Boolean = this.first <= t.first && this.last >= t.last

fun IntRange.overlaps(t: IntRange): Boolean {
return (this.first in t.first..t.last) || (this.last in t.first..t.last) ||
(t.first in this.first..this.last) || (t.last in this.first..this.last)
}
34 changes: 33 additions & 1 deletion advents/src/main/kotlin/net/fish/geometry/Point.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,36 @@ fun Collection<Point>.bounds() =

fun Point.abs() = Point(kotlin.math.abs(x), kotlin.math.abs(y))
fun Point.max(): Int = maxOf(x, y)
fun Point.sign(): Point = Point(x.sign, y.sign)
fun Point.sign(): Point = Point(x.sign, y.sign)

fun Pair<Point, Point>.edgePoints(): Sequence<Point> {
val upperLeft = first
val lowerRight = second

return sequence {
// Top edge
yieldAll((upperLeft.x..lowerRight.x).map { x -> Point(x, upperLeft.y) })

// Right edge
yieldAll((upperLeft.y + 1 until lowerRight.y).map { y -> Point(lowerRight.x, y) })

// Bottom edge
yieldAll((lowerRight.x downTo upperLeft.x).map { x -> Point(x, lowerRight.y) })

// Left edge
yieldAll((lowerRight.y - 1 downTo upperLeft.y + 1).map { y -> Point(upperLeft.x, y) })
}
}

fun Pair<Point, Point>.points(): Sequence<Point> {
val upperLeft = first
val lowerRight = second

return sequence {
for (y in upperLeft.y..lowerRight.y) {
for (x in upperLeft.x..lowerRight.x) {
yield(Point(x, y))
}
}
}
}
13 changes: 13 additions & 0 deletions advents/src/main/kotlin/net/fish/maths/Permutations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,19 @@ class Permutations(N: Int) : IntCombinations(N) {
}
}

// Creates a list of pairs (list) of integers, e.g. PairCombinations(4) gives:
// listOf(
// listOf(0, 0), listOf(0, 1), listOf(0, 2), listOf(0, 3),
// listOf(1, 0), listOf(1, 1), listOf(1, 2), listOf(1, 3),
// listOf(2, 0), listOf(2, 1), listOf(2, 2), listOf(2, 3),
// listOf(3, 0), listOf(3, 1), listOf(3, 2), listOf(3, 3)
// )

class PairCombinations(size: Int): IntCombinations(2) {
override val state = Array(2) { Ring(size) }.toList()
override fun state() = state.map { it.state() }.reversed()
}

fun main() {
val n = 100
val sumNumbers = Ring(n).sum()
Expand Down
14 changes: 14 additions & 0 deletions advents/src/main/kotlin/net/fish/y2018/AdventOfCode.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@file:JvmName("AdventOfCode2023")
package net.fish.y2018

import net.fish.Reflect
import net.fish.Runner

object AdventOfCode {
val days = Reflect.getDays(2018)
}

fun main(args: Array<String>) {
val day = if(args.isEmpty()) 0 else args[0].toInt()
Runner.run(2018, AdventOfCode.days, day)
}
38 changes: 38 additions & 0 deletions advents/src/main/kotlin/net/fish/y2018/Day01.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package net.fish.y2018

import net.fish.Day
import net.fish.resourceLines

object Day01 : Day {
private val data by lazy { resourceLines(2018, 1).map { it.toInt() } }
private val previousTotals: MutableSet<Int> = mutableSetOf()

override fun part1() = doPart1(data)
override fun part2() = doPart2(data)

fun doPart1(data: List<Int>): Int = data.sum()
fun doPart2(data: List<Int>): Int {
var currentIndex = 0
var foundDuplicate = false
var currentTotal = 0
previousTotals += 0
// find when the total repeats itself by checking when we've seen it previously in the set, or adding to it and repeating
while (!foundDuplicate) {
currentTotal += data[currentIndex]
if (previousTotals.contains(currentTotal)) {
foundDuplicate = true
} else {
previousTotals += currentTotal
currentIndex = (currentIndex + 1) % data.size
}
}
return currentTotal
}

@JvmStatic
fun main(args: Array<String>) {
println(part1())
println(part2())
}

}
48 changes: 48 additions & 0 deletions advents/src/main/kotlin/net/fish/y2018/Day02.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package net.fish.y2018

import net.fish.Day
import net.fish.maths.combinations
import net.fish.maths.permutations
import net.fish.resourceLines

object Day02 : Day {
private val data by lazy { toBoxes(resourceLines(2018, 2)) }

override fun part1() = doPart1(data)
override fun part2() = doPart2(data)

fun hasDuplicateOfCount(code: String, count: Int): Boolean {
val byCharCount = code.toCharArray().toList().groupBy { it }
// do any of the groups have the right size?
return byCharCount.any { it.value.size == count }
}

data class Box(val code: String) {
val size = code.length
val hasPairs: Boolean = hasDuplicateOfCount(code, 2)
val hasTriples: Boolean = hasDuplicateOfCount(code, 3)

fun numDiffs(other: Box): Int = size - code.zip(other.code).count { it.first == it.second }
fun removeDiffs(other: Box): String {
return code.zip(other.code).filter { it.first == it.second }.map { it.first }.joinToString("")
}
}

private fun toBoxes(data: List<String>): List<Box> {
return data.map { Box(it) }
}

fun doPart1(data: List<Box>): Int = data.count { it.hasPairs } * data.count { it.hasTriples }
fun doPart2(data: List<Box>): String {
val x = data.combinations(2)
val pairWith1Diff = x.first { it[0].numDiffs(it[1]) == 1 }
return pairWith1Diff[0].removeDiffs(pairWith1Diff[1])
}

@JvmStatic
fun main(args: Array<String>) {
println(part1())
println(part2())
}

}
64 changes: 64 additions & 0 deletions advents/src/main/kotlin/net/fish/y2018/Day03.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package net.fish.y2018

import net.fish.Day
import net.fish.geometry.Point
import net.fish.resourceLines
import java.lang.Exception

object Day03 : Day {
private val claimExtractor by lazy { Regex("""#(\d+) @ (\d+),(\d+): (\d+)x(\d+)""") }
private val data by lazy { toClaims(resourceLines(2018, 3)) }

override fun part1() = doPart1(data)
override fun part2() = doPart2(data)

data class Claim(val id:Int, val x: Int, val y: Int, val sx: Int, val sy: Int) {
fun allPoints(): List<Point> {
val combinations: MutableList<Point> = mutableListOf()
for (a in (x until (x+sx))) {
for (b in (y until (y+sy))) {
combinations.add(Point(a, b))
}
}
return combinations.toList()
}
}

fun toClaims(data: List<String>): List<Claim> = data.map { line ->
claimExtractor.find(line)?.destructured!!.let { (id, x, y, sx, sy) ->
// println("Adding claim for $id, $x, $y, $sx, $sy")
Claim(id.toInt(), x.toInt(), y.toInt(), sx.toInt(), sy.toInt())
}
}

fun doPart1(claims: List<Claim>): Int = createPointToClaims(claims).values.count { it.size > 1 }

private fun createPointToClaims(claims: List<Claim>): MutableMap<Point, MutableSet<Claim>> {
val mapPointToClaims: MutableMap<Point, MutableSet<Claim>> = mutableMapOf()
claims.forEach { claim ->
claim.allPoints().forEach { p ->
mapPointToClaims.getOrPut(p) { mutableSetOf() } += claim
}
}
return mapPointToClaims
}

fun doPart2(claims: List<Claim>): Int {
// find all claims that have a point in more than one claim, and remove them from all claims. should be left with 1 claim that
// doesn't overlap anything.
val p2c = createPointToClaims(claims)
val claimsThatOverlap = p2c.values.filter { it.size > 1 }.flatten().toSet()
val unique = claims - claimsThatOverlap
if (unique.size != 1) {
throw Exception("Found more than 1 that doesn't overlap: $unique")
}
return unique.first().id
}

@JvmStatic
fun main(args: Array<String>) {
println(part1())
println(part2())
}

}
Loading

0 comments on commit b0ed1a7

Please sign in to comment.