Day 10
This commit is contained in:
parent
9747f89779
commit
56df7dcf85
9 changed files with 469 additions and 1 deletions
2
.github/README.md
vendored
2
.github/README.md
vendored
|
@ -25,7 +25,7 @@ Puzzle inputs and descriptions are not included in this repository. You'll have
|
|||
| [7](/07-handyHaversacks) | ![Completed][check] | [Link](/07-handyHaversacks/python) | [Link](/07-handyHaversacks/go) |
|
||||
| [8](/08-handheldHalting) | ![Completed][check] | [Link](/08-handheldHalting/python) | [Link](/08-handheldHalting/go) |
|
||||
| [9](/09-encodingError) | ![Completed][check] | [Link](/09-encodingError/python) | [Link](/09-encodingError/go) |
|
||||
| 10 | ![Not yet attempted][pending] | | |
|
||||
| [10](/10-adapterArray) | ![Completed][check] | [Link](/10-adapterArray/python) | [Link](/10-adapterArray/go) |
|
||||
| 11 | | | |
|
||||
| 12 | | | |
|
||||
| 13 | | | |
|
||||
|
|
35
10-adapterArray/README.md
Normal file
35
10-adapterArray/README.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
# [Day 10: Adapter Array](https://adventofcode.com/2020/day/10)
|
||||
|
||||
<details><summary>Script output</summary>
|
||||
|
||||
```
|
||||
❯ python .\python\
|
||||
AoC 2020: day 10 - Adapter Array
|
||||
Python 3.8.5
|
||||
|
||||
Test cases
|
||||
1.1 pass
|
||||
1.2 pass
|
||||
2.1 pass
|
||||
2.2 pass
|
||||
|
||||
Answers
|
||||
Part 1: 3000
|
||||
Part 2: 193434623148032
|
||||
|
||||
❯ go run .\go\
|
||||
AoC 2020: day 10 - Adapter Array
|
||||
Go go1.15.2
|
||||
|
||||
Test cases
|
||||
1.1 pass
|
||||
1.2 pass
|
||||
2.1 pass
|
||||
2.2 pass
|
||||
|
||||
Answers
|
||||
Part 1: 3000
|
||||
Part 2: 193434623148032
|
||||
```
|
||||
|
||||
</details>
|
114
10-adapterArray/go/challenge/partOne.go
Normal file
114
10-adapterArray/go/challenge/partOne.go
Normal file
|
@ -0,0 +1,114 @@
|
|||
package challenge
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Adaptor struct {
|
||||
Output int
|
||||
MinInput int
|
||||
MaxInput int
|
||||
}
|
||||
|
||||
func (a Adaptor) IsCompatible(inputJoltage int) bool {
|
||||
return (a.MinInput <= inputJoltage) && (a.MaxInput >= inputJoltage)
|
||||
}
|
||||
|
||||
func NewAdaptor(outputJoltage int) Adaptor {
|
||||
return Adaptor{
|
||||
Output: outputJoltage,
|
||||
MinInput: outputJoltage - 3,
|
||||
MaxInput: outputJoltage - 1,
|
||||
}
|
||||
}
|
||||
|
||||
func parse(instr string) []Adaptor {
|
||||
var aslc []Adaptor
|
||||
for _, v := range strings.Split(strings.TrimSpace(instr), "\n") {
|
||||
i, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
aslc = append(aslc, NewAdaptor(i))
|
||||
}
|
||||
|
||||
// If I don't sort it, the recusrion in part one takes impossibly long to run
|
||||
sort.Slice(aslc, func(p, q int) bool {
|
||||
return aslc[p].Output < aslc[q].Output
|
||||
})
|
||||
|
||||
return aslc
|
||||
}
|
||||
|
||||
type ac struct {
|
||||
idx int
|
||||
adp Adaptor
|
||||
}
|
||||
|
||||
func findChain(adaptors []Adaptor, currentJoltage int) (bool, int, int) {
|
||||
if len(adaptors) == 0 {
|
||||
return true, 0, 0
|
||||
}
|
||||
|
||||
var candidates []ac
|
||||
for i, a := range adaptors {
|
||||
if a.IsCompatible(currentJoltage) {
|
||||
candidates = append(candidates, ac{i, a})
|
||||
}
|
||||
}
|
||||
|
||||
if len(candidates) == 0 {
|
||||
return false, 0, 0
|
||||
}
|
||||
|
||||
for _, possibleAdaptor := range candidates {
|
||||
lc := make([]Adaptor, len(adaptors))
|
||||
copy(lc, adaptors)
|
||||
lc = append(lc[:possibleAdaptor.idx], lc[possibleAdaptor.idx+1:]...)
|
||||
|
||||
foundChain, numOneDiff, numThreeDiff := findChain(lc, possibleAdaptor.adp.Output)
|
||||
|
||||
if foundChain {
|
||||
od := 0
|
||||
td := 0
|
||||
|
||||
diff := possibleAdaptor.adp.Output - currentJoltage
|
||||
if diff == 1 {
|
||||
od += 1
|
||||
} else if diff == 3 {
|
||||
td += 1
|
||||
}
|
||||
|
||||
return true, od + numOneDiff, td + numThreeDiff
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false, 0, 0
|
||||
|
||||
}
|
||||
|
||||
func PartOne(instr string) int {
|
||||
adaptors := parse(instr)
|
||||
|
||||
// Add integreated device adaptor
|
||||
var maxJ int
|
||||
{
|
||||
for _, adaptor := range adaptors {
|
||||
if maxJ < adaptor.Output {
|
||||
maxJ = adaptor.Output
|
||||
}
|
||||
}
|
||||
}
|
||||
adaptors = append(adaptors, NewAdaptor(maxJ + 3))
|
||||
|
||||
s, oj, tj := findChain(adaptors, 0)
|
||||
|
||||
if s {
|
||||
return oj * tj
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
35
10-adapterArray/go/challenge/partTwo.go
Normal file
35
10-adapterArray/go/challenge/partTwo.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package challenge
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func PartTwo(instr string) int {
|
||||
var joltages []int
|
||||
for _, x := range strings.Split(strings.TrimSpace(instr), "\n") {
|
||||
i, err := strconv.Atoi(x)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
joltages = append(joltages, i)
|
||||
}
|
||||
sort.Ints(joltages)
|
||||
|
||||
routeLengths := map[int]int{0: 1}
|
||||
|
||||
for _, joltage := range joltages {
|
||||
var totalRoutes int
|
||||
for _, n := range []int{1, 2, 3} {
|
||||
v, exists := routeLengths[joltage-n]
|
||||
if !exists {
|
||||
v = 0
|
||||
}
|
||||
totalRoutes += v
|
||||
}
|
||||
routeLengths[joltage] = totalRoutes
|
||||
}
|
||||
|
||||
return routeLengths[joltages[len(joltages)-1]]
|
||||
}
|
100
10-adapterArray/go/main.go
Normal file
100
10-adapterArray/go/main.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"adventOfCode/10-adapterArray/go/challenge"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var infoStruct info
|
||||
{
|
||||
inb, err := ioutil.ReadFile("info.json")
|
||||
if err != nil {
|
||||
fmt.Println("Error: could not open info.json")
|
||||
os.Exit(-1)
|
||||
}
|
||||
err = json.Unmarshal(inb, &infoStruct)
|
||||
if err != nil {
|
||||
fmt.Println("Error: could not parse info.json")
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
year = infoStruct.Year
|
||||
day = infoStruct.Day
|
||||
title = infoStruct.Title
|
||||
)
|
||||
|
||||
fmt.Fprintf(color.Output, "%s: day %s - %s\n", color.YellowString("AoC "+year), color.BlueString(day), title)
|
||||
fmt.Fprintf(color.Output, "Go %s\n\n", color.BlueString(runtime.Version()))
|
||||
|
||||
var challengeInput string
|
||||
{
|
||||
inb, err := ioutil.ReadFile("input.txt")
|
||||
if err != nil {
|
||||
fmt.Println("Error: could not open input.txt")
|
||||
os.Exit(-1)
|
||||
}
|
||||
challengeInput = string(inb)
|
||||
}
|
||||
|
||||
runTests(infoStruct)
|
||||
|
||||
fmt.Println("Answers")
|
||||
fmt.Fprintf(color.Output, "Part %s: %s\n", color.BlueString("1"), color.BlueString(strconv.Itoa(challenge.PartOne(challengeInput))))
|
||||
fmt.Fprintf(color.Output, "Part %s: %s\n", color.BlueString("2"), color.BlueString(strconv.Itoa(challenge.PartTwo(challengeInput))))
|
||||
|
||||
}
|
||||
|
||||
type tc struct {
|
||||
Input string `json:"input"`
|
||||
Expected int `json:"expected"`
|
||||
}
|
||||
|
||||
type info struct {
|
||||
Year string `json:"year"`
|
||||
Day string `json:"day"`
|
||||
Title string `json:"title"`
|
||||
TestCases struct {
|
||||
One []tc `json:"one"`
|
||||
Two []tc `json:"two"`
|
||||
} `json:"testCases"`
|
||||
}
|
||||
|
||||
func runTests(infoStruct info) {
|
||||
|
||||
if len(infoStruct.TestCases.One) == 0 && len(infoStruct.TestCases.Two) == 0 {
|
||||
fmt.Println("Info: no test cases specified. Skipping tests")
|
||||
fmt.Println()
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Test cases")
|
||||
|
||||
rt := func(tcs []tc, f func(string) int, n string) {
|
||||
for i, tc := range tcs {
|
||||
fmt.Fprintf(color.Output, "%s ", color.BlueString(n+"."+strconv.Itoa(i+1)))
|
||||
result := f(tc.Input)
|
||||
if result == tc.Expected {
|
||||
fmt.Fprintf(color.Output, "%s", color.GreenString("pass"))
|
||||
} else {
|
||||
fmt.Fprintf(color.Output, "%s (got %s, expected %s)", color.RedString("fail"), color.BlueString(strconv.Itoa(result)), color.BlueString(strconv.Itoa(tc.Expected)))
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
rt(infoStruct.TestCases.One, challenge.PartOne, "1")
|
||||
rt(infoStruct.TestCases.Two, challenge.PartTwo, "2")
|
||||
|
||||
fmt.Println()
|
||||
|
||||
}
|
27
10-adapterArray/info.json
Normal file
27
10-adapterArray/info.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"year": "2020",
|
||||
"day": "10",
|
||||
"title": "Adapter Array",
|
||||
"testCases": {
|
||||
"one": [
|
||||
{
|
||||
"input": "16\n10\n15\n5\n1\n11\n7\n19\n6\n12\n4",
|
||||
"expected": 35
|
||||
},
|
||||
{
|
||||
"input": "28\n33\n18\n42\n31\n14\n46\n20\n48\n47\n24\n23\n49\n45\n19\n38\n39\n11\n1\n32\n25\n35\n8\n17\n7\n9\n4\n2\n34\n10\n3",
|
||||
"expected": 220
|
||||
}
|
||||
],
|
||||
"two": [
|
||||
{
|
||||
"input": "16\n10\n15\n5\n1\n11\n7\n19\n6\n12\n4",
|
||||
"expected": 8
|
||||
},
|
||||
{
|
||||
"input": "28\n33\n18\n42\n31\n14\n46\n20\n48\n47\n24\n23\n49\n45\n19\n38\n39\n11\n1\n32\n25\n35\n8\n17\n7\n9\n4\n2\n34\n10\n3",
|
||||
"expected": 19208
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
69
10-adapterArray/python/__main__.py
Normal file
69
10-adapterArray/python/__main__.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
import json
|
||||
import platform
|
||||
import sys
|
||||
|
||||
from rich import print
|
||||
|
||||
from partOne import partOne
|
||||
from partTwo import partTwo
|
||||
|
||||
|
||||
def run_tests(test_cases):
|
||||
do_tests = True
|
||||
if len(test_cases) == 0:
|
||||
do_tests = False
|
||||
elif len(test_cases["one"]) == 0 and len(test_cases["two"]) == 0:
|
||||
do_tests = False
|
||||
|
||||
if not do_tests:
|
||||
print("Info: no test cases specified. Skipping tests\n")
|
||||
return
|
||||
|
||||
print("Test cases")
|
||||
|
||||
def rt(tcs, f, n):
|
||||
for i, tc in enumerate(tcs):
|
||||
print(f"{n}.{i+1} ", end="")
|
||||
expectedInt = tc["expected"]
|
||||
result = f(str(tc["input"]))
|
||||
if result == expectedInt:
|
||||
print("[green]pass[/green]")
|
||||
else:
|
||||
print(f"[red]fail[/red] (got {result}, expected {expectedInt})")
|
||||
|
||||
rt(test_cases["one"], partOne, 1)
|
||||
rt(test_cases["two"], partTwo, 2)
|
||||
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
info = open("info.json").read()
|
||||
except FileNotFoundError:
|
||||
print("Error: could not open info.json")
|
||||
sys.exit(-1)
|
||||
|
||||
info = json.loads(info)
|
||||
|
||||
year = info["year"]
|
||||
day = info["day"]
|
||||
title = info["title"]
|
||||
|
||||
print(f"[yellow]AoC {year}[/yellow]: day {day} - {title}")
|
||||
print(f"Python {platform.python_version()}\n")
|
||||
|
||||
try:
|
||||
challenge_input = open("input.txt").read()
|
||||
except FileNotFoundError:
|
||||
print("Error: could not open input.txt")
|
||||
sys.exit(-1)
|
||||
|
||||
run_tests(info["testCases"])
|
||||
|
||||
if "debug" in sys.argv:
|
||||
sys.exit()
|
||||
|
||||
print("Answers")
|
||||
print("Part 1:", partOne(challenge_input))
|
||||
print("Part 2:", partTwo(challenge_input))
|
75
10-adapterArray/python/partOne.py
Normal file
75
10-adapterArray/python/partOne.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
import copy
|
||||
import sys
|
||||
from typing import List, Tuple
|
||||
|
||||
|
||||
class Adaptor:
|
||||
output: int
|
||||
min_input: int
|
||||
max_input: int
|
||||
|
||||
def __init__(self, output_joltage: int) -> None:
|
||||
self.output = output_joltage
|
||||
self.min_input = output_joltage - 3
|
||||
self.max_input = output_joltage - 1
|
||||
|
||||
def is_compatible(self, input_joltage: int) -> bool:
|
||||
return (self.min_input <= input_joltage) and (self.max_input >= input_joltage)
|
||||
|
||||
|
||||
def parse(instr: str) -> List[Adaptor]:
|
||||
# If I don't sort it, the recusrion in part one takes impossibly long to run
|
||||
return sorted(
|
||||
[Adaptor(int(x)) for x in instr.strip().split("\n")], key=lambda x: x.output
|
||||
)
|
||||
|
||||
|
||||
def find_chain(adaptors: List[Adaptor], current_joltage: int) -> Tuple[bool, int, int]:
|
||||
# Returns if found a chain, and a delta for the one diff and three diff
|
||||
|
||||
if len(adaptors) == 0:
|
||||
return True, 0, 0
|
||||
|
||||
candidates = [
|
||||
(i, a) for i, a in enumerate(adaptors) if a.is_compatible(current_joltage)
|
||||
]
|
||||
|
||||
if len(candidates) == 0:
|
||||
return False, 0, 0
|
||||
|
||||
for possible_adaptor in candidates:
|
||||
lc = copy.deepcopy(adaptors)
|
||||
lc.pop(possible_adaptor[0])
|
||||
|
||||
found_chain, num_one_diff, num_three_diff = find_chain(
|
||||
lc, possible_adaptor[1].output
|
||||
)
|
||||
|
||||
if found_chain:
|
||||
od = 0
|
||||
td = 0
|
||||
|
||||
diff = possible_adaptor[1].output - current_joltage
|
||||
if diff == 1:
|
||||
od += 1
|
||||
elif diff == 3:
|
||||
td += 1
|
||||
|
||||
return True, od + num_one_diff, td + num_three_diff
|
||||
|
||||
return False, 0, 0
|
||||
|
||||
|
||||
def partOne(instr: str) -> int:
|
||||
adaptors = parse(instr)
|
||||
|
||||
# Add device adaptor
|
||||
max_j_adaptor = max(adaptors, key=lambda x: x.output)
|
||||
adaptors.append(Adaptor(max_j_adaptor.output + 3))
|
||||
|
||||
s, oj, tj = find_chain(adaptors, 0)
|
||||
|
||||
if s:
|
||||
return oj * tj
|
||||
else:
|
||||
return 0
|
13
10-adapterArray/python/partTwo.py
Normal file
13
10-adapterArray/python/partTwo.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
def partTwo(instr: str) -> int:
|
||||
joltages = sorted([int(x) for x in instr.strip().split("\n")])
|
||||
|
||||
route_lengths = {0: 1}
|
||||
for joltage in joltages:
|
||||
total_routes = 0
|
||||
# Get the route lengths for the three previous joltages
|
||||
for n in [1, 2, 3]:
|
||||
total_routes += route_lengths.get(joltage - n, 0)
|
||||
print(joltage, total_routes)
|
||||
route_lengths[joltage] = total_routes
|
||||
|
||||
return route_lengths[max(joltages)]
|
Loading…
Add table
Add a link
Reference in a new issue