mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-18 19:13:27 +02:00
142 lines
3.3 KiB
Go
142 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
_ "embed"
|
|
"flag"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/alexchao26/advent-of-code-go/cast"
|
|
"github.com/alexchao26/advent-of-code-go/util"
|
|
)
|
|
|
|
//go:embed input.txt
|
|
var input string
|
|
|
|
func init() {
|
|
// do this in init (not main) so test file has same input
|
|
input = strings.TrimRight(input, "\n")
|
|
if len(input) == 0 {
|
|
panic("empty input.txt file")
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
var part int
|
|
flag.IntVar(&part, "part", 1, "part 1 or 2")
|
|
flag.Parse()
|
|
fmt.Println("Running part", part)
|
|
|
|
if part == 1 {
|
|
ans := part1(input)
|
|
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
|
fmt.Println("Output:", ans)
|
|
} else {
|
|
ans := part2(input)
|
|
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
|
fmt.Println("Output:", ans)
|
|
}
|
|
}
|
|
|
|
func part1(input string) int {
|
|
races := parseInputPart1(input)
|
|
ans := 1
|
|
|
|
for _, r := range races {
|
|
ans *= wayToWinRace(r)
|
|
}
|
|
|
|
return ans
|
|
}
|
|
|
|
func wayToWinRace(r race) int {
|
|
// probably fast enough to do this naively for part 1...
|
|
// Time: 40 92 97 90
|
|
// total: 319
|
|
// but could binary search this
|
|
|
|
var ans int
|
|
for chargeTime := 1; chargeTime < r.time; chargeTime++ {
|
|
// velocity (chargeTime) * remaining time
|
|
dist := chargeTime * (r.time - chargeTime)
|
|
if dist > r.distance {
|
|
ans++
|
|
}
|
|
}
|
|
|
|
return ans
|
|
}
|
|
|
|
func part2(input string) int {
|
|
combinedRace := parseInputPart2(input)
|
|
|
|
// binary search this?
|
|
// but the left and right and middle could all fail if the distribution is
|
|
// F F P P P P F F F F F F F F F F F F
|
|
// F = Fail, P = Pass
|
|
// and it is some kind of (UN-CENTERED) bell curve distribution
|
|
// could test 1 by 1 from left and right of time bound...
|
|
|
|
// unfortunately you can just brute force this.
|
|
return wayToWinRace(combinedRace)
|
|
|
|
// all the optimizations might fall apart given unkind inputs, the example
|
|
// has a distribution with many passes in the middle:
|
|
// F P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P P F
|
|
// but an unkind one could have F P F F F F F F F F F F F F F F F which could
|
|
// create a linear time complexity anyways...
|
|
|
|
// maybe there's some kind of search where you divide the input times until
|
|
// you find a passing time, so in half, then quarters, then eights, etc
|
|
// once you find a passing time you have a reduced window to search
|
|
// but if the entire entry is 0 or 1 pass and the rest fails, then it
|
|
// degrades to linear in the worst case scenario anyways, so sanitized or
|
|
// expected inputs do go a long way
|
|
}
|
|
|
|
type race struct {
|
|
time, distance int
|
|
}
|
|
|
|
func parseInputPart1(input string) (ans []race) {
|
|
parts := strings.Split(input, "\n")
|
|
timeParts := strings.Split(parts[0], " ")
|
|
distParts := strings.Split(parts[1], " ")
|
|
|
|
numRegexp := regexp.MustCompile("[0-9]+")
|
|
|
|
var t, d int
|
|
for t < len(timeParts) && d < len(distParts) {
|
|
for !numRegexp.MatchString(timeParts[t]) {
|
|
t++
|
|
}
|
|
for !numRegexp.MatchString(distParts[d]) {
|
|
d++
|
|
}
|
|
ans = append(ans, race{
|
|
time: cast.ToInt(timeParts[t]),
|
|
distance: cast.ToInt(distParts[d]),
|
|
})
|
|
t++
|
|
d++
|
|
}
|
|
|
|
return ans
|
|
}
|
|
|
|
func parseInputPart2(input string) race {
|
|
parts := strings.Split(input, "\n")
|
|
timeLine := parts[0]
|
|
distLine := parts[1]
|
|
|
|
nonNums := regexp.MustCompile("[^0-9]+")
|
|
timeLine = nonNums.ReplaceAllString(timeLine, "")
|
|
distLine = nonNums.ReplaceAllString(distLine, "")
|
|
|
|
return race{
|
|
time: cast.ToInt(timeLine),
|
|
distance: cast.ToInt(distLine),
|
|
}
|
|
}
|