diff --git a/README.md b/README.md index 798c433..465eb89 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # AoC Lib -This is a semi-private collection of helpers to solve [Advent of code](http://adventofcode.com) puzzles. +This is a semi-private collection of helpers to solve [Advent of code](https://adventofcode.com) puzzles. diff --git a/lib/src/main/kotlin/de/linkel/aoc/utils/IntRangeMixIn.kt b/lib/src/main/kotlin/de/linkel/aoc/utils/IntRangeMixIn.kt new file mode 100644 index 0000000..86e71bc --- /dev/null +++ b/lib/src/main/kotlin/de/linkel/aoc/utils/IntRangeMixIn.kt @@ -0,0 +1,9 @@ +package de.linkel.aoc.utils + +fun IntRange.intersects(other: IntRange): Boolean { + return (this.first <= other.first && this.last >= other.first) || (other.first <= this.first && other.last >= this.first) +} + +fun IntRange.extend(front: Int = 0, back: Int = 0): IntRange { + return IntRange(this.first - front, this.last + back) +} diff --git a/lib/src/main/kotlin/de/linkel/aoc/utils/SequenceMixIn.kt b/lib/src/main/kotlin/de/linkel/aoc/utils/SequenceMixIn.kt new file mode 100644 index 0000000..571a315 --- /dev/null +++ b/lib/src/main/kotlin/de/linkel/aoc/utils/SequenceMixIn.kt @@ -0,0 +1,68 @@ +package de.linkel.aoc.utils + +fun Sequence.prepend(element: T): Sequence { + return if (this is ConcatSequence) { + ConcatSequence(listOf(sequenceOf(element)) + this.sequences) + } else { + ConcatSequence(listOf(sequenceOf(element), this)) + } +} +fun Sequence.prepend(other: Sequence): Sequence { + return if (this is ConcatSequence) { + ConcatSequence(listOf(other) + this.sequences) + } else { + ConcatSequence(listOf(other, this)) + } +} +fun Sequence.append(element: T): Sequence { + return if (this is ConcatSequence) { + ConcatSequence(this.sequences + listOf(sequenceOf(element))) + } else { + ConcatSequence(listOf(this, sequenceOf(element))) + } +} +fun Sequence.append(other: Sequence): Sequence { + return if (this is ConcatSequence) { + ConcatSequence(this.sequences + listOf(other)) + } else { + ConcatSequence(listOf(this, other)) + } +} +operator fun Sequence.plus(other: Sequence): Sequence { + return if (this is ConcatSequence) { + ConcatSequence(this.sequences + listOf(other)) + } else { + ConcatSequence(listOf(this, other)) + } +} + +class ConcatSequence( + val sequences: List> +): Sequence { + override fun iterator(): Iterator { + return ConcatIterator(sequences.map { it.iterator() }) + } +} + +class ConcatIterator( + iterators: List> +): Iterator { + private val parents = iterators.toMutableList() + private var current: Iterator? = null + + private fun cycleIfNecessary() { + while (current?.hasNext() != true && parents.isNotEmpty()) { + current = parents.removeAt(0) + } + } + + override fun hasNext(): Boolean { + cycleIfNecessary() + return current?.hasNext() == true + } + + override fun next(): T { + cycleIfNecessary() + return current?.next() ?: throw NoSuchElementException() + } +} diff --git a/lib/src/test/kotlin/de/linkel/aoc/utils/IntRangeMixInTest.kt b/lib/src/test/kotlin/de/linkel/aoc/utils/IntRangeMixInTest.kt new file mode 100644 index 0000000..56f54b6 --- /dev/null +++ b/lib/src/test/kotlin/de/linkel/aoc/utils/IntRangeMixInTest.kt @@ -0,0 +1,118 @@ +package de.linkel.aoc.utils + +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test + +class IntRangeMixInTest { + @Test + fun `1 to 3 and 4 to 6 do not intersect`() { + Assertions.assertThat( + (1..3).intersects(4..6) + ).isFalse() + } + + @Test + fun `10 to 30 and 4 to 6 do not intersect`() { + Assertions.assertThat( + (10..30).intersects(4..6) + ).isFalse() + } + + @Test + fun `1 to 3 and 2 to 4 intersect`() { + Assertions.assertThat( + (1..3).intersects(2..4) + ).isTrue() + } + + @Test + fun `1 to 3 and 3 to 4 intersect`() { + Assertions.assertThat( + (1..3).intersects(3..4) + ).isTrue() + } + + @Test + fun `-1 to 1 and 0 to 4 intersect`() { + Assertions.assertThat( + (-1..1).intersects(0..4) + ).isTrue() + } + + @Test + fun `-3 to -1 and -2 to 4 intersect`() { + Assertions.assertThat( + (-3..-1).intersects(-2..4) + ).isTrue() + } + + @Test + fun `1 to 5 and 5 to 10 intersect`() { + Assertions.assertThat( + (1..5).intersects(5..10) + ).isTrue() + } + + @Test + fun `1 to 5 and 4 to 5 intersect`() { + Assertions.assertThat( + (1..5).intersects(4..5) + ).isTrue() + } + + @Test + fun `1 to 5 and 3 to 4 intersect`() { + Assertions.assertThat( + (1..5).intersects(3..4) + ).isTrue() + } + + @Test + fun `1 to 5 and 5 to 5 intersect`() { + Assertions.assertThat( + (1..5).intersects(5..5) + ).isTrue() + } + + @Test + fun `2 to 4 extended by 1 at front is 1 to 4`() { + Assertions.assertThat( + (2..4).extend(front = 1) + ).isEqualTo(1..4) + } + + @Test + fun `2 to 4 extended by 2 at front is 0 to 4`() { + Assertions.assertThat( + (2..4).extend(front = 2) + ).isEqualTo(0..4) + } + + @Test + fun `2 to 4 extended by 3 at front is -1 to 4`() { + Assertions.assertThat( + (2..4).extend(front = 3) + ).isEqualTo(-1..4) + } + + @Test + fun `2 to 4 extended by 1 at back is 2 to 5`() { + Assertions.assertThat( + (2..4).extend(back = 1) + ).isEqualTo(2..5) + } + + @Test + fun `2 to 4 extended by 2 at back is 2 to 6`() { + Assertions.assertThat( + (2..4).extend(back = 2) + ).isEqualTo(2..6) + } + + @Test + fun `2 to 4 extended by 2 at front and back is 0 to 6`() { + Assertions.assertThat( + (2..4).extend(front = 2, back = 2) + ).isEqualTo(0..6) + } +} diff --git a/lib/src/test/kotlin/de/linkel/aoc/utils/SequenceConcatTest.kt b/lib/src/test/kotlin/de/linkel/aoc/utils/SequenceConcatTest.kt new file mode 100644 index 0000000..851d7f7 --- /dev/null +++ b/lib/src/test/kotlin/de/linkel/aoc/utils/SequenceConcatTest.kt @@ -0,0 +1,69 @@ +package de.linkel.aoc.utils + +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test + +class SequenceConcatTest { + @Test + fun `1,2,3 can be prepended with 0`() { + Assertions.assertThat( + sequenceOf(1, 2, 3).prepend(0).toList() + ).isEqualTo(listOf(0,1,2,3)) + } + + @Test + fun `1,2,3 can be extended with 4`() { + Assertions.assertThat( + sequenceOf(1, 2, 3).append(4).toList() + ).isEqualTo(listOf(1,2,3,4)) + } + + @Test + fun `1,2,3 can be prepended with 0 and extended with 4`() { + Assertions.assertThat( + sequenceOf(1, 2, 3).prepend(0).append(4).toList() + ).isEqualTo(listOf(0,1,2,3,4)) + } + + @Test + fun `1,2,3 can be extended with 4 and prepended with 0`() { + Assertions.assertThat( + sequenceOf(1, 2, 3).append(4).prepend(0).toList() + ).isEqualTo(listOf(0,1,2,3,4)) + } + + @Test + fun `1,2,3 can be prepended with -1,0`() { + Assertions.assertThat( + sequenceOf(1, 2, 3).prepend(sequenceOf(-1,0)).toList() + ).isEqualTo(listOf(-1,0,1,2,3)) + } + + @Test + fun `1,2,3 can be extended with 4,5`() { + Assertions.assertThat( + sequenceOf(1, 2, 3).append(sequenceOf(4,5)).toList() + ).isEqualTo(listOf(1,2,3,4,5)) + } + + @Test + fun `1,2 + 3 can be extended with 4 and prepended with 0`() { + Assertions.assertThat( + sequenceOf(1, 2).append(3).append(4).prepend(0).toList() + ).isEqualTo(listOf(0,1,2,3,4)) + } + + @Test + fun `1,2 + 3 can be extended with 4,5 and prepended with -1,0`() { + Assertions.assertThat( + sequenceOf(1, 2).append(3).append(sequenceOf(4,5)).prepend(sequenceOf(-1,0)).toList() + ).isEqualTo(listOf(-1,0,1,2,3,4,5)) + } + + @Test + fun `0,1 + 2,3 + 4,5 works`() { + Assertions.assertThat( + (sequenceOf(0, 1) + sequenceOf(2, 3) + sequenceOf(4, 5)).toList() + ).isEqualTo(listOf(0,1,2,3,4,5)) + } +} diff --git a/lib/src/test/kotlin/de/linkel/aoc/utils/TopListTest.kt b/lib/src/test/kotlin/de/linkel/aoc/utils/TopListTest.kt index 4955b10..0330e8d 100644 --- a/lib/src/test/kotlin/de/linkel/aoc/utils/TopListTest.kt +++ b/lib/src/test/kotlin/de/linkel/aoc/utils/TopListTest.kt @@ -5,8 +5,28 @@ import org.junit.jupiter.api.Test class TopListTest { @Test - fun `top 3 list with 3 elements works`() { - val top3 = TopList(3).plus(listOf(1, 2, 5, 3, 4)).toList() + fun `empty top 3 list works`() { + val top3 = TopList(3) + Assertions.assertThat(top3).hasSize(0) + Assertions.assertThat(top3.isEmpty()).isTrue() + } + + @Test + fun `default collection methods work`() { + val top3 = TopList(3).plus(listOf(1, 2, 5, 3, 4)) + Assertions.assertThat(top3).hasSize(3) + Assertions.assertThat(top3).contains(5) + Assertions.assertThat(top3).contains(4) + Assertions.assertThat(top3).contains(3) + Assertions.assertThat(top3.isEmpty()).isFalse() + Assertions.assertThat(top3.contains(3)).isTrue() + Assertions.assertThat(top3.containsAll(listOf(5, 4))).isTrue() + Assertions.assertThat(top3.first()).isEqualTo(5) + } + + @Test + fun `top 3 list with 3 elements in 2 batches works`() { + val top3 = TopList(3).plus(listOf(1, 2, 5)).plus(listOf(3, 4)).toList() Assertions.assertThat(top3).hasSize(3) Assertions.assertThat(top3).contains(5) Assertions.assertThat(top3).contains(4) @@ -16,6 +36,13 @@ class TopListTest { @Test fun `top 3 list with 1 element works`() { + val top3 = TopList(3).plus(23).toList() + Assertions.assertThat(top3).hasSize(1) + Assertions.assertThat(top3).contains(23) + } + + @Test + fun `top 3 list with 1 element in list works`() { val top3 = TopList(3).plus(listOf(23)).toList() Assertions.assertThat(top3).hasSize(1) Assertions.assertThat(top3).contains(23) diff --git a/lib/src/test/kotlin/de/linkel/aoc/utils/grid/AreaTest.kt b/lib/src/test/kotlin/de/linkel/aoc/utils/grid/AreaTest.kt new file mode 100644 index 0000000..eb13bdd --- /dev/null +++ b/lib/src/test/kotlin/de/linkel/aoc/utils/grid/AreaTest.kt @@ -0,0 +1,72 @@ +package de.linkel.aoc.utils.grid + +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test + +class AreaTest { + @Test + fun `basic properties work`() { + val area = Area(1, 2, 10, 5) + Assertions.assertThat(area.origin).isEqualTo(Point(1, 2)) + Assertions.assertThat(area.northWest).isEqualTo(Point(1, 2)) + Assertions.assertThat(area.northEast).isEqualTo(Point(10, 2)) + Assertions.assertThat(area.southWest).isEqualTo(Point(1, 6)) + Assertions.assertThat(area.southEast).isEqualTo(Point(10, 6)) + Assertions.assertThat(area.dimension).isEqualTo(Dimension(10, 5)) + } + + @Test + fun `can extend to into east`() { + val area = Area(0, 0, 3, 3).extendTo(Point(5, 1)) + Assertions.assertThat(area.origin).isEqualTo(Point(0, 0)) + Assertions.assertThat(area.northWest).isEqualTo(Point(0, 0)) + Assertions.assertThat(area.northEast).isEqualTo(Point(5, 0)) + Assertions.assertThat(area.southWest).isEqualTo(Point(0, 2)) + Assertions.assertThat(area.southEast).isEqualTo(Point(5, 2)) + Assertions.assertThat(area.dimension).isEqualTo(Dimension(6, 3)) + } + + @Test + fun `can extend to into west`() { + val area = Area(0, 0, 3, 3).extendTo(Point(-1, 1)) + Assertions.assertThat(area.origin).isEqualTo(Point(-1, 0)) + Assertions.assertThat(area.northWest).isEqualTo(Point(-1, 0)) + Assertions.assertThat(area.northEast).isEqualTo(Point(2, 0)) + Assertions.assertThat(area.southWest).isEqualTo(Point(-1, 2)) + Assertions.assertThat(area.southEast).isEqualTo(Point(2, 2)) + Assertions.assertThat(area.dimension).isEqualTo(Dimension(4, 3)) + } + + @Test + fun `can extend to into north`() { + val area = Area(0, 0, 3, 3).extendTo(Point(0, -1)) + Assertions.assertThat(area.origin).isEqualTo(Point(0, -1)) + Assertions.assertThat(area.northWest).isEqualTo(Point(0, -1)) + Assertions.assertThat(area.northEast).isEqualTo(Point(2, -1)) + Assertions.assertThat(area.southWest).isEqualTo(Point(0, 2)) + Assertions.assertThat(area.southEast).isEqualTo(Point(2, 2)) + Assertions.assertThat(area.dimension).isEqualTo(Dimension(3, 4)) + } + + @Test + fun `can extend to into south`() { + val area = Area(0, 0, 3, 3).extendTo(Point(0, 3)) + Assertions.assertThat(area.origin).isEqualTo(Point(0, 0)) + Assertions.assertThat(area.northWest).isEqualTo(Point(0, 0)) + Assertions.assertThat(area.northEast).isEqualTo(Point(2, 0)) + Assertions.assertThat(area.southWest).isEqualTo(Point(0, 3)) + Assertions.assertThat(area.southEast).isEqualTo(Point(2, 3)) + Assertions.assertThat(area.dimension).isEqualTo(Dimension(3, 4)) + } + + @Test + fun `can be moved`() { + val area = Area(0, 0, 3, 3).plus(Vector(2, 1)) + Assertions.assertThat(area.origin).isEqualTo(Point(2, 1)) + Assertions.assertThat(area.northWest).isEqualTo(Point(2, 1)) + Assertions.assertThat(area.northEast).isEqualTo(Point(4, 1)) + Assertions.assertThat(area.southWest).isEqualTo(Point(2, 3)) + Assertions.assertThat(area.southEast).isEqualTo(Point(4, 3)) + Assertions.assertThat(area.dimension).isEqualTo(Dimension(3, 3)) + } +} diff --git a/lib/src/test/kotlin/de/linkel/aoc/utils/grid/PointTest.kt b/lib/src/test/kotlin/de/linkel/aoc/utils/grid/PointTest.kt new file mode 100644 index 0000000..6b0dedb --- /dev/null +++ b/lib/src/test/kotlin/de/linkel/aoc/utils/grid/PointTest.kt @@ -0,0 +1,27 @@ +package de.linkel.aoc.utils.grid + +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test + +class PointTest { + @Test + fun `basic properties work`() { + val point = Point(1, 2) + Assertions.assertThat(point.x).isEqualTo(1) + Assertions.assertThat(point.y).isEqualTo(2) + } + + @Test + fun `can be moved`() { + val point = Point(1, 2) + Vector(2, -1) + Assertions.assertThat(point.x).isEqualTo(3) + Assertions.assertThat(point.y).isEqualTo(1) + } + + @Test + fun `2 points form a vector`() { + val vector = Point(1, 2) - Point(1,1) + Assertions.assertThat(vector.deltaX).isEqualTo(0) + Assertions.assertThat(vector.deltaY).isEqualTo(1) + } +}