Compare commits
No commits in common. "ba3d6778800a4fc765b7fe4ed06e3f7d6aa262ea" and "c33d708760ce7177009f95eb939d709a84efc736" have entirely different histories.
ba3d677880
...
c33d708760
@ -1,85 +0,0 @@
|
||||
package aoc2023
|
||||
|
||||
import println
|
||||
import readInput
|
||||
|
||||
/*
|
||||
--- Day 6: Wait For It ---
|
||||
The ferry quickly brings you across Island Island. After asking around, you discover that there is indeed normally a large pile of sand somewhere near here, but you don't see anything besides lots of water and the small island where the ferry has docked.
|
||||
As you try to figure out what to do next, you notice a poster on a wall near the ferry dock. "Boat races! Open to the public! Grand prize is an all-expenses-paid trip to Desert Island!" That must be where the sand comes from! Best of all, the boat races are starting in just a few minutes.
|
||||
You manage to sign up as a competitor in the boat races just in time. The organizer explains that it's not really a traditional race - instead, you will get a fixed amount of time during which your boat has to travel as far as it can, and you win if your boat goes the farthest.
|
||||
As part of signing up, you get a sheet of paper (your puzzle input) that lists the time allowed for each race and also the best distance ever recorded in that race. To guarantee you win the grand prize, you need to make sure you go farther in each race than the current record holder.
|
||||
The organizer brings you over to the area where the boat races are held. The boats are much smaller than you expected - they're actually toy boats, each with a big button on top. Holding down the button charges the boat, and releasing the button allows the boat to move. Boats move faster if their button was held longer, but time spent holding the button counts against the total race time. You can only hold the button at the start of the race, and boats don't move until the button is released.
|
||||
For example:
|
||||
Time: 7 15 30
|
||||
Distance: 9 40 200
|
||||
|
||||
This document describes three races:
|
||||
|
||||
The first race lasts 7 milliseconds. The record distance in this race is 9 millimeters.
|
||||
The second race lasts 15 milliseconds. The record distance in this race is 40 millimeters.
|
||||
The third race lasts 30 milliseconds. The record distance in this race is 200 millimeters.
|
||||
|
||||
Your toy boat has a starting speed of zero millimeters per millisecond. For each whole millisecond you spend at the beginning of the race holding down the button, the boat's speed increases by one millimeter per millisecond.
|
||||
So, because the first race lasts 7 milliseconds, you only have a few options:
|
||||
|
||||
Don't hold the button at all (that is, hold it for 0 milliseconds) at the start of the race. The boat won't move; it will have traveled 0 millimeters by the end of the race.
|
||||
Hold the button for 1 millisecond at the start of the race. Then, the boat will travel at a speed of 1 millimeter per millisecond for 6 milliseconds, reaching a total distance traveled of 6 millimeters.
|
||||
Hold the button for 2 milliseconds, giving the boat a speed of 2 millimeters per millisecond. It will then get 5 milliseconds to move, reaching a total distance of 10 millimeters.
|
||||
Hold the button for 3 milliseconds. After its remaining 4 milliseconds of travel time, the boat will have gone 12 millimeters.
|
||||
Hold the button for 4 milliseconds. After its remaining 3 milliseconds of travel time, the boat will have gone 12 millimeters.
|
||||
Hold the button for 5 milliseconds, causing the boat to travel a total of 10 millimeters.
|
||||
Hold the button for 6 milliseconds, causing the boat to travel a total of 6 millimeters.
|
||||
Hold the button for 7 milliseconds. That's the entire duration of the race. You never let go of the button. The boat can't move until you let you of the button. Please make sure you let go of the button so the boat gets to move. 0 millimeters.
|
||||
|
||||
Since the current record for this race is 9 millimeters, there are actually 4 different ways you could win: you could hold the button for 2, 3, 4, or 5 milliseconds at the start of the race.
|
||||
In the second race, you could hold the button for at least 4 milliseconds and at most 11 milliseconds and beat the record, a total of 8 different ways to win.
|
||||
In the third race, you could hold the button for at least 11 milliseconds and no more than 19 milliseconds and still beat the record, a total of 9 ways you could win.
|
||||
To see how much margin of error you have, determine the number of ways you can beat the record in each race; in this example, if you multiply these values together, you get 288 (4 * 8 * 9).
|
||||
Determine the number of ways you could beat the record in each race. What do you get if you multiply these numbers together?
|
||||
|
||||
*/
|
||||
fun main() {
|
||||
|
||||
val inlineTestInput = """
|
||||
Time: 7 15 30
|
||||
Distance: 9 40 200
|
||||
"""
|
||||
|
||||
// Next time, don't be stupid. This is not the right formula for the problem:
|
||||
// distance = (bt * (bt+1))/2 + bt * (tt - 2 * bt)
|
||||
// Spend 30 minutes figuring out why I don't win any race when the boat is actually accelerating!
|
||||
fun part1(input: List<String>): Int {
|
||||
val times = input[0].split(":")[1].split(" ").filter { it.isNotBlank() }.map { it.toInt() }
|
||||
val distances = input[1].split(":")[1].split(" ").filter { it.isNotBlank() }.map { it.toInt() }
|
||||
val result = times.withIndex().map { (i, tt) ->
|
||||
IntRange(1, tt - 1).count {
|
||||
it * (tt - it) > distances[i]
|
||||
}
|
||||
}
|
||||
return result.reduce { acc, i -> acc * i }
|
||||
}
|
||||
|
||||
fun part2(input: List<String>): Int {
|
||||
val tt = input[0].split(":")[1].replace(" ", "").toLong()
|
||||
val distance = input[1].split(":")[1].replace(" ", "").toLong()
|
||||
val result = LongRange(1, tt - 1).count {
|
||||
it * (tt - it) > distance
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// test if implementation meets criteria from the description, like:
|
||||
val testInput = inlineTestInput.trim().reader().readLines()
|
||||
//val testInput = readInput("aoc2023/Day06_test")
|
||||
val testInputPart1Result = part1(testInput)
|
||||
println("Part 1 Test: $testInputPart1Result")
|
||||
val testInputPart2Result = part2(testInput)
|
||||
println("Part 2 Test: $testInputPart2Result")
|
||||
check(testInputPart1Result == 288)
|
||||
check(testInputPart2Result == 71503)
|
||||
|
||||
val input = readInput("aoc2023/Day06")
|
||||
part1(input).println()
|
||||
part2(input).println()
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
package aoc2023
|
||||
|
||||
import println
|
||||
import readInput
|
||||
|
||||
/*
|
||||
--- Day 7: Camel Cards ---
|
||||
Your all-expenses-paid trip turns out to be a one-way, five-minute ride in an airship. (At least it's a cool airship!) It drops you off at the edge of a vast desert and descends back to Island Island.
|
||||
"Did you bring the parts?"
|
||||
You turn around to see an Elf completely covered in white clothing, wearing goggles, and riding a large camel.
|
||||
"Did you bring the parts?" she asks again, louder this time. You aren't sure what parts she's looking for; you're here to figure out why the sand stopped.
|
||||
"The parts! For the sand, yes! Come with me; I will show you." She beckons you onto the camel.
|
||||
After riding a bit across the sands of Desert Island, you can see what look like very large rocks covering half of the horizon. The Elf explains that the rocks are all along the part of Desert Island that is directly above Island Island, making it hard to even get there. Normally, they use big machines to move the rocks and filter the sand, but the machines have broken down because Desert Island recently stopped receiving the parts they need to fix the machines.
|
||||
You've already assumed it'll be your job to figure out why the parts stopped when she asks if you can help. You agree automatically.
|
||||
Because the journey will take a few days, she offers to teach you the game of Camel Cards. Camel Cards is sort of similar to poker except it's designed to be easier to play while riding a camel.
|
||||
In Camel Cards, you get a list of hands, and your goal is to order them based on the strength of each hand. A hand consists of five cards labeled one of A, K, Q, J, T, 9, 8, 7, 6, 5, 4, 3, or 2. The relative strength of each card follows this order, where A is the highest and 2 is the lowest.
|
||||
Every hand is exactly one type. From strongest to weakest, they are:
|
||||
|
||||
Five of a kind, where all five cards have the same label: AAAAA
|
||||
Four of a kind, where four cards have the same label and one card has a different label: AA8AA
|
||||
Full house, where three cards have the same label, and the remaining two cards share a different label: 23332
|
||||
Three of a kind, where three cards have the same label, and the remaining two cards are each different from any other card in the hand: TTT98
|
||||
Two pair, where two cards share one label, two other cards share a second label, and the remaining card has a third label: 23432
|
||||
One pair, where two cards share one label, and the other three cards have a different label from the pair and each other: A23A4
|
||||
High card, where all cards' labels are distinct: 23456
|
||||
|
||||
Hands are primarily ordered based on type; for example, every full house is stronger than any three of a kind.
|
||||
If two hands have the same type, a second ordering rule takes effect. Start by comparing the first card in each hand. If these cards are different, the hand with the stronger first card is considered stronger. If the first card in each hand have the same label, however, then move on to considering the second card in each hand. If they differ, the hand with the higher second card wins; otherwise, continue with the third card in each hand, then the fourth, then the fifth.
|
||||
So, 33332 and 2AAAA are both four of a kind hands, but 33332 is stronger because its first card is stronger. Similarly, 77888 and 77788 are both a full house, but 77888 is stronger because its third card is stronger (and both hands have the same first and second card).
|
||||
To play Camel Cards, you are given a list of hands and their corresponding bid (your puzzle input). For example:
|
||||
32T3K 765
|
||||
T55J5 684
|
||||
KK677 28
|
||||
KTJJT 220
|
||||
QQQJA 483
|
||||
|
||||
This example shows five hands; each hand is followed by its bid amount. Each hand wins an amount equal to its bid multiplied by its rank, where the weakest hand gets rank 1, the second-weakest hand gets rank 2, and so on up to the strongest hand. Because there are five hands in this example, the strongest hand will have rank 5 and its bid will be multiplied by 5.
|
||||
So, the first step is to put the hands in order of strength:
|
||||
|
||||
32T3K is the only one pair and the other hands are all a stronger type, so it gets rank 1.
|
||||
KK677 and KTJJT are both two pair. Their first cards both have the same label, but the second card of KK677 is stronger (K vs T), so KTJJT gets rank 2 and KK677 gets rank 3.
|
||||
T55J5 and QQQJA are both three of a kind. QQQJA has a stronger first card, so it gets rank 5 and T55J5 gets rank 4.
|
||||
|
||||
Now, you can determine the total winnings of this set of hands by adding up the result of multiplying each hand's bid with its rank (765 * 1 + 220 * 2 + 28 * 3 + 684 * 4 + 483 * 5). So the total winnings in this example are 6440.
|
||||
Find the rank of every hand in your set. What are the total winnings?
|
||||
|
||||
*/
|
||||
fun main() {
|
||||
|
||||
val inlineTestInput = """
|
||||
32T3K 765
|
||||
T55J5 684
|
||||
KK677 28
|
||||
KTJJT 220
|
||||
QQQJA 483
|
||||
"""
|
||||
|
||||
val cardTypes = listOf("A", "K", "Q", "J", "T", "9", "8", "7", "6", "5", "4", "3", "2").toTypedArray()
|
||||
val cardTypeString = "AKQJT98765432"
|
||||
val cardTypeString2 = "AKQT98765432J"
|
||||
val cardTypeStringRev = cardTypeString.reversed()
|
||||
val cardTypeString2Rev = cardTypeString2.reversed()
|
||||
|
||||
val charCmp = compareBy<Char> { cardTypeStringRev.indexOf(it) }
|
||||
|
||||
fun rank(s: String): Int {
|
||||
val ss = s.toCharArray().sortedWith(charCmp).toCharArray().concatToString()
|
||||
// five
|
||||
for (c in cardTypes) {
|
||||
if (ss == c.repeat(5)) {
|
||||
return 6
|
||||
}
|
||||
}
|
||||
// four + high card
|
||||
for (c in cardTypes) {
|
||||
if (ss.contains(c.repeat(4))) {
|
||||
return 5
|
||||
}
|
||||
}
|
||||
// full house / three of a kind
|
||||
for (c in cardTypes) {
|
||||
if (ss.contains(c.repeat(3))) {
|
||||
val highCards = ss.replace(c.repeat(3), "")
|
||||
for (c2 in cardTypes) {
|
||||
if (highCards.contains(c2.repeat(2))) {
|
||||
return 4
|
||||
}
|
||||
}
|
||||
// three of a kind
|
||||
return 3
|
||||
}
|
||||
}
|
||||
// two pair / one pair
|
||||
for (c in cardTypes) {
|
||||
if (ss.contains(c.repeat(2))) {
|
||||
val highCards = ss.replace(c.repeat(2), "")
|
||||
for (c2 in cardTypes) {
|
||||
if (highCards.contains(c2.repeat(2))) {
|
||||
return 2
|
||||
}
|
||||
}
|
||||
// one pair
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fun rank2(s: String): Int {
|
||||
val jokers = s.count { it == 'J' }
|
||||
val ss = s.replace("J", "")
|
||||
val good = ss.groupBy { it }.map { it.value.size }.filter { it > 1 }.sortedDescending()
|
||||
if (jokers > 3) return 100 // five
|
||||
if (jokers == 3 && good.isNotEmpty()) return 100 // five
|
||||
if (jokers == 3) return 90 // four
|
||||
if (jokers == 2 && good.isEmpty()) return 70 // triple
|
||||
if (jokers == 2 && good[0] == 3) return 100 // five
|
||||
if (jokers == 2) return 90 // four
|
||||
if (jokers == 1 && good.isEmpty()) return 50 // pair
|
||||
if (jokers == 1 && good.size == 2) return 80 // full house
|
||||
if (jokers == 1 && good[0] == 4) return 100 // five
|
||||
if (jokers == 1 && good[0] == 3) return 90 // four
|
||||
if (jokers == 1 && good[0] == 2) return 70 // triple
|
||||
if (jokers == 0 && good.isEmpty()) return 0 // nothing
|
||||
if (jokers == 0 && good[0] == 4) return 90 // four
|
||||
if (jokers == 0 && good[0] == 3 && good.size == 2) return 80 // full house
|
||||
if (jokers == 0 && good[0] == 3) return 70 // triple
|
||||
if (jokers == 0 && good.size == 2) return 60 // two pairs
|
||||
return 50 // one pair
|
||||
}
|
||||
|
||||
fun part1(input: List<String>): Int {
|
||||
val comparator = compareBy<Pair<String, Int>> { rank(it.first) }
|
||||
val secondComparator = compareBy<Pair<String, Int>> { it.first.mapIndexed { i, c -> cardTypeStringRev.indexOf(c) shl ((5 - i) * 4) }.sum() }
|
||||
return input.map { it -> it.split(" ") }
|
||||
.map { it[0] to it[1].toInt() }
|
||||
.sortedWith(comparator.then(secondComparator))
|
||||
.mapIndexed { index, p -> (index + 1) * p.second }
|
||||
.sum()
|
||||
}
|
||||
|
||||
fun part2(input: List<String>): Int {
|
||||
val comparator = compareBy<Pair<String, Int>> { rank2(it.first) }
|
||||
val secondComparator = compareBy<Pair<String, Int>> { it.first.mapIndexed { i, c -> cardTypeString2Rev.indexOf(c) shl ((5 - i) * 4) }.sum() }
|
||||
return input.map { it -> it.split(" ") }
|
||||
.map { it[0] to it[1].toInt() }
|
||||
.sortedWith(comparator.then(secondComparator))
|
||||
.mapIndexed { index, p -> (index + 1) * p.second }
|
||||
.sum()
|
||||
}
|
||||
|
||||
// test if implementation meets criteria from the description, like:
|
||||
val testInput = inlineTestInput.trim().reader().readLines()
|
||||
//val testInput = readInput("aoc2023/Day07_test")
|
||||
val testInputPart1Result = part1(testInput)
|
||||
println("Part 1 Test: $testInputPart1Result")
|
||||
val testInputPart2Result = part2(testInput)
|
||||
println("Part 2 Test: $testInputPart2Result")
|
||||
check(testInputPart1Result == 6440)
|
||||
check(testInputPart2Result == 5905)
|
||||
|
||||
val input = readInput("aoc2023/Day07")
|
||||
part1(input).println()
|
||||
part2(input).println()
|
||||
}
|
Loading…
Reference in New Issue
Block a user