Add 2020 solutions

Signed-off-by: AKU <tom@tdpain.net>
This commit is contained in:
akp 2021-11-27 20:33:25 +00:00
parent 1ad039bb13
commit 25f4d9d658
No known key found for this signature in database
GPG key ID: AA5726202C8879B7
240 changed files with 10455 additions and 465 deletions

View file

@ -1,2 +1,31 @@
# [Day 1: Report Repair](https://adventofcode.com/2020/day/1)
<details><summary>Script output</summary>
```
python .\python\
AoC 2020: day 1 - Report Repair
Test cases
1.1 pass
2.1 pass
Answers
Part 1: 1010884
Part 2: 253928438
go run .\go\
AoC 2020: day 1 - Report Repair
Go go1.15.2
Test cases
1.1 pass
2.1 pass
Answers
Part 1: 1010884
Part 2: 253928438
```
</details>

View file

@ -1,23 +0,0 @@
Day 1 (Report Repair) benchmark
Dir: challenges/2020/01-reportRepair
Runs per part: 100
--------------------------------------------------------------------------------
Golang
benchmark.part.1.avg: 0.000018 seconds
benchmark.part.1.min: 0.000009 seconds
benchmark.part.1.max: 0.000060 seconds
benchmark.part.2.avg: 0.001030 seconds
benchmark.part.2.min: 0.000859 seconds
benchmark.part.2.max: 0.001785 seconds
--------------------------------------------------------------------------------
Python
benchmark.part.1.avg: 0.000608 seconds
benchmark.part.1.min: 0.000425 seconds
benchmark.part.1.max: 0.001051 seconds
benchmark.part.2.avg: 0.161098 seconds
benchmark.part.2.min: 0.113367 seconds
benchmark.part.2.max: 0.303925 seconds
--------------------------------------------------------------------------------

View file

@ -1,55 +0,0 @@
package challenge
import (
"errors"
"strconv"
"strings"
"github.com/codemicro/adventOfCode/lib/aocgo"
)
type Challenge struct {
aocgo.BaseChallenge
}
func (c Challenge) One(instr string) (interface{}, error) {
values := parse(instr)
for _, i := range values {
for _, v := range values {
if v+i == 2020 {
return v * i, nil
}
}
}
return nil, errors.New("no combinations found")
}
func (c Challenge) Two(instr string) (interface{}, error) {
values := parse(instr)
for _, i := range values {
for _, v := range values {
for _, x := range values {
if v+i+x == 2020 {
return v * i * x, nil
}
}
}
}
return nil, errors.New("no combinations found")
}
func parse(instr string) []int {
inputSlice := strings.Split(strings.TrimSpace(instr), "\n")
var values []int
for _, v := range inputSlice {
str, _ := strconv.Atoi(v)
values = append(values, str)
}
return values
}

View file

@ -0,0 +1,18 @@
package challenge
import (
"strconv"
"strings"
)
func parse(instr string) []int {
inputSlice := strings.Split(strings.TrimSpace(instr), "\n")
var values []int
for _, v := range inputSlice {
str, _ := strconv.Atoi(v)
values = append(values, str)
}
return values
}

View file

@ -0,0 +1,15 @@
package challenge
func PartOne(instr string) int {
values := parse(instr)
for _, i := range values {
for _, v := range values {
if v+i == 2020 {
return v * i
}
}
}
return 0
}

View file

@ -0,0 +1,17 @@
package challenge
func PartTwo(instr string) int {
values := parse(instr)
for _, i := range values {
for _, v := range values {
for _, x := range values {
if v+i+x == 2020 {
return v * i * x
}
}
}
}
return 0
}

View file

@ -0,0 +1,88 @@
package main
import (
"adventOfCode/01-reportRepair/go/challenge"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"runtime"
"strconv"
"github.com/fatih/color"
)
const (
year = "2020"
day = "1"
title = "Report Repair"
)
func main() {
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()
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"`
}
func runTests() {
testCases := struct {
One []tc `json:"one"`
Two []tc `json:"two"`
}{}
{
inb, err := ioutil.ReadFile("testCases.json")
if err != nil {
fmt.Println("Error: could not open testCases.json. Skipping tests")
return
}
err = json.Unmarshal(inb, &testCases)
if err != nil {
fmt.Println("Error: could not parse testCases.json. Skipping tests")
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(testCases.One, challenge.PartOne, "1")
rt(testCases.Two, challenge.PartTwo, "2")
fmt.Println()
}

View file

@ -1,17 +0,0 @@
{
"inputFile": "input.txt",
"testCases": {
"one": [
{
"input": "1721\n979\n366\n299\n675\n1456",
"expected": "514579"
}
],
"two": [
{
"input": "1721\n979\n366\n299\n675\n1456",
"expected": "241861950"
}
]
}
}

View file

@ -1,32 +0,0 @@
from typing import Any, List
from aocpy import BaseChallenge
class Challenge(BaseChallenge):
@staticmethod
def one(instr: str) -> int:
values = parse(instr)
for i in values:
for v in values:
if v + i == 2020:
return v * i
raise ValueError("no macthing combinations")
@staticmethod
def two(instr: str) -> Any:
values = parse(instr)
for i in values:
for v in values:
for x in values:
if v + i + x == 2020:
return v * i * x
raise ValueError("no matching combinations")
def parse(instr: str) -> List:
return [int(x) for x in instr.strip().split("\n")]

View file

@ -0,0 +1,54 @@
import json
import sys
from rich import print
from partOne import partOne
from partTwo import partTwo
year = "2020"
day = "1"
title = "Report Repair"
def run_tests():
try:
test_cases = open("testCases.json").read()
except FileNotFoundError:
print("Info: could not open testCases.json. Skipping tests")
return
print("Test cases")
test_cases = json.loads(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__":
print(f"[yellow]AoC {year}[/yellow]: day {day} - {title}\n")
try:
challenge_input = open("input.txt").read()
except FileNotFoundError:
print("Error: could not open input.txt")
sys.exit(-1)
run_tests()
print("Answers")
print("Part 1:", partOne(challenge_input))
print("Part 2:", partTwo(challenge_input))

View file

@ -0,0 +1,5 @@
from typing import List
def parse(instr: str) -> List:
return [int(x) for x in instr.strip().split("\n")]

View file

@ -0,0 +1,12 @@
from common import *
def partOne(instr: str) -> int:
values = parse(instr)
for i in values:
for v in values:
if v + i == 2020:
return v * i
return 0

View file

@ -0,0 +1,13 @@
from common import *
def partTwo(instr: str) -> int:
values = parse(instr)
for i in values:
for v in values:
for x in values:
if v + i + x == 2020:
return v * i * x
return 0

View file

@ -0,0 +1,14 @@
{
"one": [
{
"input": "1721\n979\n366\n299\n675\n1456",
"expected": 514579
}
],
"two": [
{
"input": "1721\n979\n366\n299\n675\n1456",
"expected": 241861950
}
]
}

View file

@ -1,23 +0,0 @@
Day 2 (Password Philosophy) benchmark
Dir: challenges/2020/02-passwordPhilosophy
Runs per part: 50
--------------------------------------------------------------------------------
Golang
benchmark.part.1.avg: 0.001280 seconds
benchmark.part.1.min: 0.000806 seconds
benchmark.part.1.max: 0.002234 seconds
benchmark.part.2.avg: 0.001383 seconds
benchmark.part.2.min: 0.000786 seconds
benchmark.part.2.max: 0.005438 seconds
--------------------------------------------------------------------------------
Python
benchmark.part.1.avg: 0.005367 seconds
benchmark.part.1.min: 0.002311 seconds
benchmark.part.1.max: 0.010500 seconds
benchmark.part.2.avg: 0.004618 seconds
benchmark.part.2.min: 0.001737 seconds
benchmark.part.2.max: 0.011591 seconds
--------------------------------------------------------------------------------

View file

@ -1,97 +0,0 @@
package challenge
import (
"regexp"
"strconv"
"strings"
"github.com/codemicro/adventOfCode/lib/aocgo"
)
type Challenge struct {
aocgo.BaseChallenge
}
func (c Challenge) One(instr string) (interface{}, error) {
inputSlice := parse(instr)
type Password struct {
plaintext string
targetLetter rune
minRepeats int
maxRepeats int
}
var passwords []Password
for _, line := range inputSlice {
matches := parserRegex.FindAllStringSubmatch(line, -1)
miR, _ := strconv.Atoi(matches[0][1])
maR, _ := strconv.Atoi(matches[0][2])
passwords = append(passwords, Password{
plaintext: matches[0][4],
targetLetter: rune(matches[0][3][0]),
minRepeats: miR,
maxRepeats: maR,
})
}
var num_valid_passwords int
for _, password := range passwords {
var target_letter_count int
for _, char := range password.plaintext {
if char == password.targetLetter {
target_letter_count += 1
}
}
if (target_letter_count >= password.minRepeats) && (target_letter_count <= password.maxRepeats) {
num_valid_passwords += 1
}
}
return num_valid_passwords, nil
}
func (c Challenge) Two(instr string) (interface{}, error) {
inputSlice := parse(instr)
type Password struct {
plaintext string
targetLetter rune
positionOne int
positionTwo int
}
var passwords []Password
for _, line := range inputSlice {
matches := parserRegex.FindAllStringSubmatch(line, -1)
miR, _ := strconv.Atoi(matches[0][1])
maR, _ := strconv.Atoi(matches[0][2])
passwords = append(passwords, Password{
plaintext: matches[0][4],
targetLetter: rune(matches[0][3][0]),
positionOne: miR - 1,
positionTwo: maR - 1,
})
}
var num_valid_passwords int
for _, password := range passwords {
positionOneMatches := rune(password.plaintext[password.positionOne]) == password.targetLetter
positionTwoMatches := rune(password.plaintext[password.positionTwo]) == password.targetLetter
if positionOneMatches != positionTwoMatches {
num_valid_passwords += 1
}
}
return num_valid_passwords, nil
}
func parse(instr string) []string {
return strings.Split(strings.TrimSpace(string(instr)), "\n")
}
var parserRegex = regexp.MustCompile(`(?m)(\d+)-(\d+) ([a-z]): (.+)`)

View file

@ -0,0 +1,12 @@
package challenge
import (
"regexp"
"strings"
)
func parse(instr string) []string {
return strings.Split(strings.TrimSpace(string(instr)), "\n")
}
var parserRegex = regexp.MustCompile(`(?m)(\d+)-(\d+) ([a-z]): (.+)`)

View file

@ -0,0 +1,46 @@
package challenge
import (
"strconv"
)
func PartOne(instr string) int {
inputSlice := parse(instr)
type Password struct {
plaintext string
targetLetter rune
minRepeats int
maxRepeats int
}
var passwords []Password
for _, line := range inputSlice {
matches := parserRegex.FindAllStringSubmatch(line, -1)
miR, _ := strconv.Atoi(matches[0][1])
maR, _ := strconv.Atoi(matches[0][2])
passwords = append(passwords, Password{
plaintext: matches[0][4],
targetLetter: rune(matches[0][3][0]),
minRepeats: miR,
maxRepeats: maR,
})
}
var num_valid_passwords int
for _, password := range passwords {
var target_letter_count int
for _, char := range password.plaintext {
if char == password.targetLetter {
target_letter_count += 1
}
}
if (target_letter_count >= password.minRepeats) && (target_letter_count <= password.maxRepeats) {
num_valid_passwords += 1
}
}
return num_valid_passwords
}

View file

@ -0,0 +1,42 @@
package challenge
import (
"strconv"
)
func PartTwo(instr string) int {
inputSlice := parse(instr)
type Password struct {
plaintext string
targetLetter rune
positionOne int
positionTwo int
}
var passwords []Password
for _, line := range inputSlice {
matches := parserRegex.FindAllStringSubmatch(line, -1)
miR, _ := strconv.Atoi(matches[0][1])
maR, _ := strconv.Atoi(matches[0][2])
passwords = append(passwords, Password{
plaintext: matches[0][4],
targetLetter: rune(matches[0][3][0]),
positionOne: miR - 1,
positionTwo: maR - 1,
})
}
var num_valid_passwords int
for _, password := range passwords {
positionOneMatches := rune(password.plaintext[password.positionOne]) == password.targetLetter
positionTwoMatches := rune(password.plaintext[password.positionTwo]) == password.targetLetter
if positionOneMatches != positionTwoMatches {
num_valid_passwords += 1
}
}
return num_valid_passwords
}

View file

@ -0,0 +1,88 @@
package main
import (
"adventOfCode/02-passwordPhilosophy/go/challenge"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"runtime"
"strconv"
"github.com/fatih/color"
)
const (
year = "2020"
day = "2"
title = "Password Philosophy"
)
func main() {
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()
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"`
}
func runTests() {
testCases := struct {
One []tc `json:"one"`
Two []tc `json:"two"`
}{}
{
inb, err := ioutil.ReadFile("testCases.json")
if err != nil {
fmt.Println("Error: could not open testCases.json. Skipping tests")
return
}
err = json.Unmarshal(inb, &testCases)
if err != nil {
fmt.Println("Error: could not parse testCases.json. Skipping tests")
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(testCases.One, challenge.PartOne, "1")
rt(testCases.Two, challenge.PartTwo, "2")
fmt.Println()
}

View file

@ -1,17 +0,0 @@
{
"inputFile": "input.txt",
"testCases": {
"one": [
{
"input": "1-3 a: abcde\n1-3 b: cdefg\n2-9 c: ccccccccc",
"expected": "2"
}
],
"two": [
{
"input": "1-3 a: abcde\n1-3 b: cdefg\n2-9 c: ccccccccc",
"expected": "1"
}
]
}
}

View file

@ -1,98 +0,0 @@
import re
from typing import List
from aocpy import BaseChallenge
class Challenge(BaseChallenge):
@staticmethod
def one(instr: str) -> int:
input_string = parse(instr)
class Password:
plaintext: str
target_letter: str
min_repeats: int
max_repeats: int
def __init__(
self, plaintext: str, target_letter: str, min_repeats: int, max_repeats: int
) -> None:
self.plaintext = plaintext
self.target_letter = target_letter
self.min_repeats = min_repeats
self.max_repeats = max_repeats
parser_regex = r"(\d+)-(\d+) ([a-z]): (.+)"
passwords = []
for line in input_string:
m = re.match(parser_regex, line)
passwords.append(
Password(m.group(4), m.group(3), int(m.group(1)), int(m.group(2)))
)
num_valid_passwords = 0
for password in passwords:
target_letter_count = 0
for char in password.plaintext:
if char == password.target_letter:
target_letter_count += 1
if password.min_repeats <= target_letter_count <= password.max_repeats:
num_valid_passwords += 1
return num_valid_passwords
@staticmethod
def two(instr: str) -> int:
input_string = parse(instr)
class Password:
plaintext: str
target_letter: str
position_one: int
position_two: int
def __init__(
self,
plaintext: str,
target_letter: str,
position_one: int,
position_two: int,
) -> None:
self.plaintext = plaintext
self.target_letter = target_letter
self.position_one = position_one - 1 # No concept of index zero... eurgh
self.position_two = position_two - 1
parser_regex = r"(\d+)-(\d+) ([a-z]): (.+)"
passwords = []
for line in input_string:
m = re.match(parser_regex, line)
passwords.append(
Password(m.group(4), m.group(3), int(m.group(1)), int(m.group(2)))
)
num_valid_passwords = 0
for password in passwords:
position_one_matches = (
password.plaintext[password.position_one] == password.target_letter
)
position_two_matches = (
password.plaintext[password.position_two] == password.target_letter
)
if position_one_matches ^ position_two_matches:
num_valid_passwords += 1
return num_valid_passwords
def parse(instr: str) -> List:
return instr.strip().split("\n")

View file

@ -0,0 +1,56 @@
import json
import platform
import sys
from rich import print
from partOne import partOne
from partTwo import partTwo
year = "2020"
day = "2"
title = "Password Philosophy"
def run_tests():
try:
test_cases = open("testCases.json").read()
except FileNotFoundError:
print("Info: could not open testCases.json. Skipping tests")
return
print("Test cases")
test_cases = json.loads(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__":
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()
print("Answers")
print("Part 1:", partOne(challenge_input))
print("Part 2:", partTwo(challenge_input))

View file

@ -0,0 +1,5 @@
from typing import List
def parse(instr: str) -> List:
return instr.strip().split("\n")

View file

@ -0,0 +1,44 @@
import re
from common import *
def partOne(instr: str) -> int:
input_string = parse(instr)
class Password:
plaintext: str
target_letter: str
min_repeats: int
max_repeats: int
def __init__(
self, plaintext: str, target_letter: str, min_repeats: int, max_repeats: int
) -> None:
self.plaintext = plaintext
self.target_letter = target_letter
self.min_repeats = min_repeats
self.max_repeats = max_repeats
parser_regex = r"(\d+)-(\d+) ([a-z]): (.+)"
passwords = []
for line in input_string:
m = re.match(parser_regex, line)
passwords.append(
Password(m.group(4), m.group(3), int(m.group(1)), int(m.group(2)))
)
num_valid_passwords = 0
for password in passwords:
target_letter_count = 0
for char in password.plaintext:
if char == password.target_letter:
target_letter_count += 1
if password.min_repeats <= target_letter_count <= password.max_repeats:
num_valid_passwords += 1
return num_valid_passwords

View file

@ -0,0 +1,50 @@
import re
from common import *
def partTwo(instr: str) -> int:
input_string = parse(instr)
class Password:
plaintext: str
target_letter: str
position_one: int
position_two: int
def __init__(
self,
plaintext: str,
target_letter: str,
position_one: int,
position_two: int,
) -> None:
self.plaintext = plaintext
self.target_letter = target_letter
self.position_one = position_one - 1 # No concept of index zero... eurgh
self.position_two = position_two - 1
parser_regex = r"(\d+)-(\d+) ([a-z]): (.+)"
passwords = []
for line in input_string:
m = re.match(parser_regex, line)
passwords.append(
Password(m.group(4), m.group(3), int(m.group(1)), int(m.group(2)))
)
num_valid_passwords = 0
for password in passwords:
position_one_matches = (
password.plaintext[password.position_one] == password.target_letter
)
position_two_matches = (
password.plaintext[password.position_two] == password.target_letter
)
if position_one_matches ^ position_two_matches:
num_valid_passwords += 1
return num_valid_passwords

View file

@ -0,0 +1,14 @@
{
"one": [
{
"input": "1-3 a: abcde\n1-3 b: cdefg\n2-9 c: ccccccccc",
"expected": 2
}
],
"two": [
{
"input": "1-3 a: abcde\n1-3 b: cdefg\n2-9 c: ccccccccc",
"expected": 1
}
]
}

View file

@ -1,2 +1,27 @@
# [Day 3: Toboggan Trajactory](https://adventofcode.com/2021/day/3)
# [Day 3: Toboggan Trajectory](https://adventofcode.com/2020/day/3)
<details><summary>Script output</summary>
```
python .\python\
AoC 2020: day 3 - Toboggan Trajectory
Python 3.8.5
Info: could not open testCases.json. Skipping tests
Answers
Part 1: 292
Part 2: 9354744432
go run .\go\
AoC 2020: day 3 - Toboggan Trajectory
Go go1.15.2
Info: could not open testCases.json. Skipping tests
Answers
Part 1: 292
Part 2: 9354744432
```
</details>

View file

@ -1,23 +0,0 @@
Day 3 (Toboggan Trajectory) benchmark
Dir: challenges/2020/03-tobogganTrajectory
Runs per part: 50
--------------------------------------------------------------------------------
Golang
benchmark.part.1.avg: 0.000101 seconds
benchmark.part.1.min: 0.000039 seconds
benchmark.part.1.max: 0.000569 seconds
benchmark.part.2.avg: 0.000089 seconds
benchmark.part.2.min: 0.000047 seconds
benchmark.part.2.max: 0.000229 seconds
--------------------------------------------------------------------------------
Python
benchmark.part.1.avg: 0.000542 seconds
benchmark.part.1.min: 0.000341 seconds
benchmark.part.1.max: 0.001299 seconds
benchmark.part.2.avg: 0.000756 seconds
benchmark.part.2.min: 0.000504 seconds
benchmark.part.2.max: 0.001462 seconds
--------------------------------------------------------------------------------

View file

@ -1,10 +1,6 @@
package challenge
import (
"strings"
"github.com/codemicro/adventOfCode/lib/aocgo"
)
import "strings"
func parse(instr string) [][]rune {
inputSlice := strings.Split(strings.TrimSpace(instr), "\n")
@ -35,32 +31,3 @@ func findCollisions(forest [][]rune, xOffset, yOffset int) int {
return encounteredTrees
}
type Challenge struct {
aocgo.BaseChallenge
}
func (c Challenge) One(instr string) (interface{}, error) {
return findCollisions(parse(instr), 3, 1), nil
}
func (c Challenge) Two(instr string) (interface{}, error) {
forest := parse(instr)
offsetPairs := [][]int{
{3, 1},
{1, 1},
{5, 1},
{7, 1},
{1, 2},
}
treeProduct := 1
for _, pair := range offsetPairs {
encounteredTrees := findCollisions(forest, pair[0], pair[1])
treeProduct *= encounteredTrees
}
return treeProduct, nil
}

View file

@ -0,0 +1,5 @@
package challenge
func PartOne(instr string) int {
return findCollisions(parse(instr), 3, 1)
}

View file

@ -0,0 +1,22 @@
package challenge
func PartTwo(instr string) int {
forest := parse(instr)
offsetPairs := [][]int{
{3, 1},
{1, 1},
{5, 1},
{7, 1},
{1, 2},
}
treeProduct := 1
for _, pair := range offsetPairs {
encounteredTrees := findCollisions(forest, pair[0], pair[1])
treeProduct *= encounteredTrees
}
return treeProduct
}

View file

@ -0,0 +1,90 @@
package main
import (
"adventOfCode/03-tobogganTrajectory/go/challenge"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"runtime"
"strconv"
"github.com/fatih/color"
)
const (
year = "2020"
day = "3"
title = "Toboggan Trajectory"
)
func main() {
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()
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"`
}
func runTests() {
testCases := struct {
One []tc `json:"one"`
Two []tc `json:"two"`
}{}
{
inb, err := ioutil.ReadFile("testCases.json")
if err != nil {
fmt.Println("Info: could not open testCases.json. Skipping tests")
fmt.Println()
return
}
err = json.Unmarshal(inb, &testCases)
if err != nil {
fmt.Println("Error: could not parse testCases.json. 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(testCases.One, challenge.PartOne, "1")
rt(testCases.Two, challenge.PartTwo, "2")
fmt.Println()
}

View file

@ -1,4 +0,0 @@
{
"inputFile": "input.txt",
"testCases": {}
}

View file

@ -1,41 +0,0 @@
from typing import List
from aocpy import BaseChallenge
tree_char = "#"
def parse(instr: str) -> List:
return [[char for char in line] for line in instr.strip().split("\n")]
def find_collisions(forest: list, x_offset: int, y_offset: int) -> int:
encountered_trees = 0
x_pointer = 0
for row in forest[::y_offset]:
target_index = x_pointer % len(row)
if row[target_index] == tree_char:
encountered_trees += 1
x_pointer += x_offset
return encountered_trees
class Challenge(BaseChallenge):
@staticmethod
def one(instr: str) -> int:
return find_collisions(parse(instr), 3, 1)
@staticmethod
def two(instr: str) -> int:
forest = parse(instr)
tree_product = 1
offset_pairs = [(3, 1), (1, 1), (5, 1), (7, 1), (1, 2)]
for i, pair in enumerate(offset_pairs):
encountered_trees = find_collisions(forest, *pair)
tree_product *= encountered_trees
return tree_product

View file

@ -0,0 +1,56 @@
import json
import platform
import sys
from rich import print
from partOne import partOne
from partTwo import partTwo
year = "2020"
day = "3"
title = "Toboggan Trajectory"
def run_tests():
try:
test_cases = open("testCases.json").read()
except FileNotFoundError:
print("Info: could not open testCases.json. Skipping tests\n")
return
print("Test cases")
test_cases = json.loads(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__":
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()
print("Answers")
print("Part 1:", partOne(challenge_input))
print("Part 2:", partTwo(challenge_input))

View file

@ -0,0 +1,20 @@
from typing import List
tree_char = "#"
def parse(instr: str) -> List:
return [[char for char in line] for line in instr.strip().split("\n")]
def find_collisions(forest: list, x_offset: int, y_offset: int) -> int:
encountered_trees = 0
x_pointer = 0
for row in forest[::y_offset]:
target_index = x_pointer % len(row)
if row[target_index] == tree_char:
encountered_trees += 1
x_pointer += x_offset
return encountered_trees

View file

@ -0,0 +1,5 @@
from common import *
def partOne(instr: str) -> int:
return find_collisions(parse(instr), 3, 1)

View file

@ -0,0 +1,15 @@
from common import *
def partTwo(instr: str) -> int:
forest = parse(instr)
tree_product = 1
offset_pairs = [(3, 1), (1, 1), (5, 1), (7, 1), (1, 2)]
for i, pair in enumerate(offset_pairs):
encountered_trees = find_collisions(forest, *pair)
tree_product *= encountered_trees
return tree_product

View file

@ -0,0 +1,33 @@
# [Day 4: Passport Processing](https://adventofcode.com/2020/day/4)
<details><summary>Script output</summary>
```
python .\python\
AoC 2020: day 4 - Passport Processing
Python 3.8.5
Test cases
1.1 pass
2.1 pass
2.2 pass
Answers
Part 1: 213
Part 2: 147
go run .\go\
AoC 2020: day 4 - Passport Processing
Go go1.15.2
Test cases
1.1 pass
2.1 pass
2.2 pass
Answers
Part 1: 213
Part 2: 147
```
</details>

View file

@ -0,0 +1,11 @@
package challenge
import "strings"
func parse(instr string) []string {
inputSlice := strings.Split(strings.TrimSpace(instr), "\n\n")
for i, x := range inputSlice {
inputSlice[i] = strings.ReplaceAll(x, "\n", " ")
}
return inputSlice
}

View file

@ -0,0 +1,36 @@
package challenge
import "regexp"
func PartOne(instr string) int {
inputSlice := parse(instr)
var valid_passports int
testCases := []*regexp.Regexp{
regexp.MustCompile(`byr:([^ ]+)`),
regexp.MustCompile(`iyr:([^ ]+)`),
regexp.MustCompile(`eyr:([^ ]+)`),
regexp.MustCompile(`hgt:([^ ]+)`),
regexp.MustCompile(`hcl:([^ ]+)`),
regexp.MustCompile(`ecl:([^ ]+)`),
regexp.MustCompile(`pid:([^ ]+)`),
}
for _, passport := range inputSlice {
valid := true
for _, tc := range testCases {
if !tc.MatchString(passport) {
valid = false
break
}
}
if valid {
valid_passports += 1
}
}
return valid_passports
}

View file

@ -0,0 +1,154 @@
package challenge
import (
"regexp"
"strconv"
"strings"
)
func PartTwo(instr string) int {
inputSlice := parse(instr)
var passports []Passport
for _, x := range inputSlice {
passports = append(passports, NewPassport(x))
}
var validPassports int
for _, passport := range passports {
if passport.Validate() {
validPassports += 1
}
}
return validPassports
}
type Passport struct {
byr string // Birth year
iyr string // Issue year
eyr string // Expiration year
hgt string // Height
hcl string // Hair colour
ecl string // Eye colour
pid string // Passport ID
cid string // Country ID
}
func (p Passport) Validate() bool {
// byr (Birth Year) - four digits; at least 1920 and at most 2002.
if p.byr == "" {
return false
}
{
i, _ := strconv.Atoi(p.byr)
if !(1920 <= i && i <= 2002) {
return false
}
}
// iyr (Issue Year) - four digits; at least 2010 and at most 2020.
if p.iyr == "" {
return false
}
{
i, _ := strconv.Atoi(p.iyr)
if !(2010 <= i && i <= 2020) {
return false
}
}
// eyr (Expiration Year) - four digits; at least 2020 and at most 2030.
if p.eyr == "" {
return false
}
{
i, _ := strconv.Atoi(p.eyr)
if !(2020 <= i && i <= 2030) {
return false
}
}
// hgt (Height) - a number followed by either cm or in:
// If cm, the number must be at least 150 and at most 193.
// If in, the number must be at least 59 and at most 76.
if p.hgt == "" {
return false
}
if strings.Contains(p.hgt, "cm") {
i, _ := strconv.Atoi(strings.Trim(p.hgt, "cm"))
if !(150 <= i && i <= 193) {
return false
}
} else if strings.Contains(p.hgt, "in") {
i, _ := strconv.Atoi(strings.Trim(p.hgt, "in"))
if !(59 <= i && i <= 76) {
return false
}
} else {
return false
}
// hcl (Hair Color) - a // followed by exactly six characters 0-9 or a-f.
if p.hcl == "" {
return false
}
if p.hcl[0] == []byte("#")[0] {
if m, _ := regexp.MatchString(`#[0-9a-f]{6}`, p.hcl); !m {
return false
}
} else {
return false
}
// ecl (Eye Color) - exactly one of: amb blu brn gry grn hzl oth.
if p.ecl == "" {
return false
}
{
var contained bool
for _, v := range []string{"amb", "blu", "brn", "gry", "grn", "hzl", "oth"} {
if p.ecl == v {
contained = true
break
}
}
if !contained {
return false
}
}
// pid (Passport ID) - a nine-digit number, including leading zeroes.
if len(p.pid) == 9 {
_, err := strconv.Atoi(p.pid)
if err != nil {
return false
}
} else {
return false
}
return true
}
func NewPassport(instr string) Passport {
np := Passport{}
np.byr = extractField("byr", instr)
np.iyr = extractField("iyr", instr)
np.eyr = extractField("eyr", instr)
np.hgt = extractField("hgt", instr)
np.hcl = extractField("hcl", instr)
np.ecl = extractField("ecl", instr)
np.pid = extractField("pid", instr)
np.cid = extractField("cid", instr)
return np
}
func extractField(field, instr string) string {
fieldRegex := regexp.MustCompile(field + `:([^ ]+)`)
matches := fieldRegex.FindAllStringSubmatch(instr, -1)
if len(matches) == 0 {
return ""
}
return matches[0][1]
}

View file

@ -0,0 +1,90 @@
package main
import (
"adventOfCode/04-passportProcessing/go/challenge"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"runtime"
"strconv"
"github.com/fatih/color"
)
const (
year = "2020"
day = "4"
title = "Passport Processing"
)
func main() {
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()
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"`
}
func runTests() {
testCases := struct {
One []tc `json:"one"`
Two []tc `json:"two"`
}{}
{
inb, err := ioutil.ReadFile("testCases.json")
if err != nil {
fmt.Println("Info: could not open testCases.json. Skipping tests")
fmt.Println()
return
}
err = json.Unmarshal(inb, &testCases)
if err != nil {
fmt.Println("Error: could not parse testCases.json. 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(testCases.One, challenge.PartOne, "1")
rt(testCases.Two, challenge.PartTwo, "2")
fmt.Println()
}

View file

@ -0,0 +1,56 @@
import json
import platform
import sys
from rich import print
from partOne import partOne
from partTwo import partTwo
year = "2020"
day = "4"
title = "Passport Processing"
def run_tests():
try:
test_cases = open("testCases.json").read()
except FileNotFoundError:
print("Info: could not open testCases.json. Skipping tests\n")
return
print("Test cases")
test_cases = json.loads(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__":
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()
print("Answers")
print("Part 1:", partOne(challenge_input))
print("Part 2:", partTwo(challenge_input))

View file

@ -0,0 +1,5 @@
from typing import List
def parse(instr: str) -> List:
return [x.replace("\n", " ") for x in instr.strip().split("\n\n")]

View file

@ -0,0 +1,37 @@
import re
from common import *
def partOne(instr: str) -> int:
input_list = parse(instr)
test_cases = [
# Regex to match expression, bool if can be skipped
[r"byr:([^ ]+)", False],
[r"iyr:([^ ]+)", False],
[r"eyr:([^ ]+)", False],
[r"hgt:([^ ]+)", False],
[r"hcl:([^ ]+)", False],
[r"ecl:([^ ]+)", False],
[r"pid:([^ ]+)", False],
[r"cid:([^ ]+)", True],
]
valid_passports = 0
for passport in input_list:
results = []
for tc in test_cases:
results.append([re.search(tc[0], passport) is not None, tc[1]])
valid = True
for r in results:
if not (r[0] or r[1]):
valid = False
break
valid_passports += 1 if valid else 0
return valid_passports

View file

@ -0,0 +1,103 @@
import re
from common import *
def partTwo(instr: str) -> int:
input_list = parse(instr)
passports = [Passport(x) for x in input_list]
valid_passports = 0
for passport in passports:
if passport.validate():
valid_passports += 1
return valid_passports
class Passport:
byr: str # Birth year
iyr: str # Issue year
eyr: str # Expiration year
hgt: str # Height
hcl: str # Hair colour
ecl: str # Eye colour
pid: str # Passport ID
cid: str # Country ID
def __init__(self, instr: str) -> None:
self.byr = self._extract_field("byr", instr)
self.iyr = self._extract_field("iyr", instr)
self.eyr = self._extract_field("eyr", instr)
self.hgt = self._extract_field("hgt", instr)
self.hcl = self._extract_field("hcl", instr)
self.ecl = self._extract_field("ecl", instr)
self.pid = self._extract_field("pid", instr)
self.cid = self._extract_field("cid", instr)
def _extract_field(self, field: str, instr: str) -> str:
matches = re.search(field + r":([^ ]+)", instr)
if matches is None:
return ""
return matches.group(1)
def validate(self) -> bool:
# byr (Birth Year) - four digits; at least 1920 and at most 2002.
if self.byr == "":
return False
if not (1920 <= int(self.byr) <= 2002):
return False
# iyr (Issue Year) - four digits; at least 2010 and at most 2020.
if self.iyr == "":
return False
if not (2010 <= int(self.iyr) <= 2020):
return False
# eyr (Expiration Year) - four digits; at least 2020 and at most 2030.
if self.eyr == "":
return False
if not (2020 <= int(self.eyr) <= 2030):
return False
# hgt (Height) - a number followed by either cm or in:
# If cm, the number must be at least 150 and at most 193.
# If in, the number must be at least 59 and at most 76.
if self.hgt == "":
return False
if "cm" in self.hgt:
if not (150 <= int(self.hgt.strip("cm")) <= 193):
return False
elif "in" in self.hgt:
if not (59 <= int(self.hgt.strip("in")) <= 76):
return False
else:
return False
# hcl (Hair Color) - a # followed by exactly six characters 0-9 or a-f.
if self.hcl == "":
return False
if self.hcl[0] == "#":
if re.match(r"#[0-9a-f]{6}", self.hcl) is None:
return False
else:
return False
# ecl (Eye Color) - exactly one of: amb blu brn gry grn hzl oth.
if self.ecl == "":
return False
if self.ecl not in ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]:
return False
# pid (Passport ID) - a nine-digit number, including leading zeroes.
if len(self.pid) == 9:
try:
int(self.pid)
except ValueError:
return False
else:
return False
return True

View file

@ -0,0 +1,18 @@
{
"one": [
{
"input": "ecl:gry pid:860033327 eyr:2020 hcl:#fffffd\nbyr:1937 iyr:2017 cid:147 hgt:183cm\n\niyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884\nhcl:#cfa07d byr:1929\n\nhcl:#ae17e1 iyr:2013\neyr:2024\necl:brn pid:760753108 byr:1931\nhgt:179cm\n\nhcl:#cfa07d eyr:2025 pid:166559648\niyr:2011 ecl:brn hgt:59in",
"expected": 2
}
],
"two": [
{
"input": "eyr:1972 cid:100\nhcl:#18171d ecl:amb hgt:170 pid:186cm iyr:2018 byr:1926\n\niyr:2019\nhcl:#602927 eyr:1967 hgt:170cm\necl:grn pid:012533040 byr:1946\n\nhcl:dab227 iyr:2012\necl:brn hgt:182cm pid:021572410 eyr:2020 byr:1992 cid:277\n\nhgt:59cm ecl:zzz\neyr:2038 hcl:74454a iyr:2023\npid:3556412378 byr:2007\n",
"expected": 0
},
{
"input": "pid:087499704 hgt:74in ecl:grn iyr:2012 eyr:2030 byr:1980\nhcl:#623a2f\n\neyr:2029 ecl:blu cid:129 byr:1989\niyr:2014 pid:896056539 hcl:#a97842 hgt:165cm\n\nhcl:#888785\nhgt:164cm byr:2001 iyr:2015 cid:88\npid:545766238 ecl:hzl\neyr:2022\n\niyr:2010 hgt:158cm hcl:#b6652a ecl:blu byr:1944 eyr:2021 pid:093154719\n",
"expected": 4
}
]
}

View file

@ -0,0 +1,33 @@
# [Day 5: Binary Boarding](https://adventofcode.com/2020/day/5)
<details><summary>Script output</summary>
```
python .\python\
AoC 2020: day 5 - Binary Boarding
Python 3.8.5
Test cases
1.1 pass
1.2 pass
1.3 pass
Answers
Part 1: 989
Part 2: 548
go run .\go\
AoC 2020: day 5 - Binary Boarding
Go go1.15.2
Test cases
1.1 pass
1.2 pass
1.3 pass
Answers
Part 1: 989
Part 2: 548
```
</details>

View file

@ -0,0 +1,57 @@
package challenge
import "strings"
var (
front = []rune("F")[0]
back = []rune("B")[0]
left = []rune("L")[0]
right = []rune("R")[0]
)
const (
numRows = 128
numCols = 8
)
func parse(instr string) []string {
return strings.Split(strings.TrimSpace(instr), "\n")
}
func decodePosition(rowString string, decChar, incChar rune, maxVal int) int {
minVal := 0
maxVal -= 1
currentRange := (maxVal + 1) - minVal
for _, char := range strings.ToUpper(rowString) {
rangeModifier := int(currentRange / 2)
if char == decChar {
maxVal -= rangeModifier
} else if char == incChar {
minVal += rangeModifier
}
currentRange /= 2
}
if rune(rowString[len(rowString)-1]) == decChar {
return minVal
}
return maxVal
}
func parseSeat(seatString string) (int, int) {
row := decodePosition(seatString[:7], front, back, numRows)
col := decodePosition(seatString[7:], left, right, numCols)
return row, col
}
func getSeatId(row, col int) int {
return (row * 8) + col
}

View file

@ -0,0 +1,16 @@
package challenge
func PartOne(instr string) int {
inputSlice := parse(instr)
var highestSeatId int
for _, seat := range inputSlice {
psr, psc := parseSeat(seat)
seatId := getSeatId(psr, psc)
if seatId > highestSeatId {
highestSeatId = seatId
}
}
return highestSeatId
}

View file

@ -0,0 +1,36 @@
package challenge
func PartTwo(instr string) int {
inputSlice := parse(instr)
var seatMatrix [numRows][numCols]bool
for _, seat := range inputSlice {
row, col := parseSeat(seat)
seatMatrix[row][col] = true
}
var (
lastOne bool
lastTwo bool
)
for row := 0; row < len(seatMatrix); row += 1 {
for col := 0; col < len(seatMatrix[row]); col += 1 {
this := seatMatrix[row][col]
if lastTwo && !lastOne && this {
// We need to get the previous item because at this point, we've already moved on one
prevRow := row
prevCol := col - 1
if prevCol < 0 {
prevRow -= 1
prevCol += numCols
}
return getSeatId(prevRow, prevCol)
}
lastTwo = lastOne
lastOne = this
}
}
return 0
}

View file

@ -0,0 +1,106 @@
package main
import (
"adventOfCode/05-binaryBoarding/go/challenge"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"runtime"
"strconv"
"github.com/fatih/color"
)
const (
year = "2020"
day = "1"
title = "Report Repair"
)
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()
}

View file

@ -0,0 +1,22 @@
{
"year": "2020",
"day": "5",
"title": "Binary Boarding",
"testCases": {
"one": [
{
"input": "BFFFBBFRRR",
"expected": 567
},
{
"input": "FFFBBBFRRR",
"expected": 119
},
{
"input": "BBFFBBFRLL",
"expected": 820
}
],
"two": []
}
}

View file

@ -0,0 +1,66 @@
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"])
print("Answers")
print("Part 1:", partOne(challenge_input))
print("Part 2:", partTwo(challenge_input))

View file

@ -0,0 +1,47 @@
from typing import List, Tuple
front = "F"
back = "B"
left = "L"
right = "R"
num_rows = 128
num_cols = 8
def parse(instr: str) -> List:
return instr.strip().split("\n")
def decode_position(row_string: str, dec_char: str, inc_char: str, max_val: int) -> int:
min_val = 0
max_val -= 1
current_range = (max_val + 1) - min_val
for char in row_string.upper():
range_modifier = current_range / 2
if char == dec_char:
max_val -= range_modifier
elif char == inc_char:
min_val += range_modifier
current_range /= 2
if row_string[-1] == dec_char:
return min_val
else:
return max_val
def parse_seat(seat_string: str) -> Tuple[int, int]:
row = decode_position(seat_string[:7], front, back, num_rows)
col = decode_position(seat_string[7:], left, right, num_cols)
return int(row), int(col)
def get_seat_id(row: int, col: int) -> int:
return (row * 8) + col

View file

@ -0,0 +1,14 @@
from common import *
def partOne(instr: str) -> int:
input_list = parse(instr)
highest_seat_id = 0
for seat in input_list:
parsed_seat = parse_seat(seat)
seat_id = get_seat_id(*parsed_seat)
if seat_id > highest_seat_id:
highest_seat_id = seat_id
return highest_seat_id

View file

@ -0,0 +1,41 @@
from common import *
def partTwo(instr: str) -> int:
input_list = parse(instr)
# Build a matrix to represent the entire plane
# False represents an empty seat
seat_matrix = []
for _ in range(num_rows):
x = []
for _ in range(num_cols):
x.append(False)
seat_matrix.append(x)
# Populate that matrix
for seat in input_list:
row, col = parse_seat(seat)
seat_matrix[row][col] = True
lastOne = None
lastTwo = None
for row in range(len(seat_matrix)):
for col in range(len(seat_matrix[row])):
this = seat_matrix[row][col]
if [lastTwo, lastOne, this] == [True, False, True]:
# We need to get the previous item because at this point, we've already moved on one
prev_row = row
prev_col = col - 1
if prev_col < 0:
prev_row -= 1
return get_seat_id(prev_row, prev_col)
lastTwo = lastOne
lastOne = this
return 0

View file

@ -0,0 +1,31 @@
# [Day 6: Custom Customs](https://adventofcode.com/2020/day/6)
<details><summary>Script output</summary>
```
python .\python\
AoC 2020: day 6 - Custom Customs
Python 3.8.5
Test cases
1.1 pass
2.1 pass
Answers
Part 1: 6310
Part 2: 3193
go run .\go\
AoC 2020: day 6 - Custom Customs
Go go1.15.2
Test cases
1.1 pass
2.1 pass
Answers
Part 1: 6310
Part 2: 3193
```
</details>

View file

@ -0,0 +1,21 @@
package challenge
import "strings"
func parse(instr string) []string {
return strings.Split(strings.TrimSpace(instr), "\n\n")
}
type Group struct {
Questions []rune
NumPax int
}
func IsRuneInSlice(r rune, s []rune) bool {
for _, v := range s {
if v == r {
return true
}
}
return false
}

View file

@ -0,0 +1,30 @@
package challenge
import "strings"
func PartOne(instr string) int {
newGroup := func(instr string) Group {
g := Group{}
individualPax := strings.Split(instr, "\n")
g.NumPax = len(individualPax)
for _, pax := range individualPax {
for _, char := range pax {
if !IsRuneInSlice(char, g.Questions) {
g.Questions = append(g.Questions, char)
}
}
}
return g
}
var questionTotal int
for _, x := range parse(instr) {
questionTotal += len(newGroup(x).Questions)
}
return questionTotal
}

View file

@ -0,0 +1,51 @@
package challenge
import "strings"
func PartTwo(instr string) int {
checkChar := func(aq map[int][]rune, char rune) (isInAll bool) {
isInAll = true
for _, val := range aq {
if !IsRuneInSlice(char, val) {
isInAll = false
break
}
}
return
}
newGroup := func(instr string) Group {
g := Group{}
individualPax := strings.Split(instr, "\n")
g.NumPax = len(individualPax)
paxQuestions := make(map[int][]rune)
for i, pax := range individualPax {
paxQuestions[i] = []rune(pax)
}
for _, val := range paxQuestions {
for _, char := range val {
if checkChar(paxQuestions, char) {
if !IsRuneInSlice(char, g.Questions) {
g.Questions = append(g.Questions, char)
}
}
}
}
return g
}
var questionTotal int
for _, x := range parse(instr) {
questionTotal += len(newGroup(x).Questions)
}
return questionTotal
}

View file

@ -0,0 +1,100 @@
package main
import (
"adventOfCode/06-customCustoms/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()
}

View file

@ -0,0 +1,19 @@
{
"year": "2020",
"day": "6",
"title": "Custom Customs",
"testCases": {
"one": [
{
"input": "abc\n\na\nb\nc\n\nab\nac\n\na\na\na\na\n\nb\n",
"expected": 11
}
],
"two": [
{
"input": "abc\n\na\nb\nc\n\nab\nac\n\na\na\na\na\n\nb\n",
"expected": 6
}
]
}
}

View file

@ -0,0 +1,66 @@
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"])
print("Answers")
print("Part 1:", partOne(challenge_input))
print("Part 2:", partTwo(challenge_input))

View file

@ -0,0 +1,5 @@
from typing import List
def parse(instr: str) -> List[str]:
return instr.strip().split("\n\n")

View file

@ -0,0 +1,28 @@
from common import *
class Group:
questions: List[str]
num_pax: int
def __init__(self, instr: str) -> None:
individual_pax = instr.split("\n")
self.num_pax = len(individual_pax)
self.questions = []
for pax in individual_pax:
for char in pax:
if char not in self.questions:
self.questions.append(char)
def partOne(instr: str) -> int:
groups = [Group(x) for x in parse(instr)]
question_total = 0
for group in groups:
question_total += len(group.questions)
return question_total

View file

@ -0,0 +1,44 @@
from common import *
from typing import Dict
def check_char(aq: Dict[int, List[str]], char: str) -> bool:
is_in_all = True
for key in aq:
val = aq[key]
if char not in val:
is_in_all = False
break
return is_in_all
class Group:
questions: List[str]
num_pax: int
def __init__(self, instr: str) -> None:
individual_pax = instr.split("\n")
self.num_pax = len(individual_pax)
self.questions = []
pax_questions = {}
for i, pax in enumerate(individual_pax):
pax_questions[i] = [char for char in pax]
for key in pax_questions:
val = pax_questions[key]
for char in val:
if check_char(pax_questions, char):
if char not in self.questions:
self.questions.append(char)
def partTwo(instr: str) -> int:
groups = [Group(x) for x in parse(instr)]
question_total = 0
for group in groups:
question_total += len(group.questions)
return question_total

View file

@ -0,0 +1,33 @@
# [Day 7: Handy Haversacks](https://adventofcode.com/2020/day/7)
<details><summary>Script output</summary>
```
python .\python\
AoC 2020: day 7 - Handy Haversacks
Python 3.8.5
Test cases
1.1 pass
2.1 pass
2.2 pass
Answers
Part 1: 300
Part 2: 8030
go run .\go\
AoC 2020: day 7 - Handy Haversacks
Go go1.15.2
Test cases
1.1 pass
2.1 pass
2.2 pass
Answers
Part 1: 300
Part 2: 8030
```
</details>

View file

@ -0,0 +1,42 @@
package challenge
import (
"regexp"
"strconv"
"strings"
)
var (
ruleRegex = regexp.MustCompile(`(.+) bags contain (.+).`)
definitionRegex = regexp.MustCompile(`(\d+) (.+) bags?`)
)
const (
targetColour = "shiny gold"
)
func parse(instr string) map[string]map[string]int {
inp := strings.Split(strings.TrimSpace(instr), "\n")
rules := make(map[string]map[string]int)
for _, rule := range inp {
rr := ruleRegex.FindAllStringSubmatch(rule, -1)
containerBag := rr[0][1]
ruleSet := strings.Split(rr[0][2], ", ")
bagRules := make(map[string]int)
for _, definition := range ruleSet {
rsr := definitionRegex.FindAllStringSubmatch(definition, -1)
if len(rsr) != 0 {
i, _ := strconv.Atoi(rsr[0][1])
bagRules[rsr[0][2]] = i
}
}
rules[containerBag] = bagRules
}
return rules
}

View file

@ -0,0 +1,41 @@
package challenge
func checkBag(ruleset map[string]map[string]int, testCl, targetCl string) bool {
{
var isPresent bool
for v := range ruleset[testCl] {
if v == targetCl {
isPresent = true
break
}
}
if isPresent {
return true
}
}
for childColour := range ruleset[testCl] {
if checkBag(ruleset, childColour, targetCl) {
return true
}
}
return false
}
func PartOne(instr string) int {
rules := parse(instr)
canContainTarget := 0
for bagColour, _ := range rules {
if bagColour != targetColour {
if checkBag(rules, bagColour, targetColour) {
canContainTarget += 1
}
}
}
return canContainTarget
}

View file

@ -0,0 +1,28 @@
package challenge
func countForBag(ruleset map[string]map[string]int, testCl string) int {
if len(ruleset[testCl]) == 0 {
return -1
}
count := 0
for childColour := range ruleset[testCl] {
childBags := countForBag(ruleset, childColour)
var v int
if childBags == -1 {
v = ruleset[testCl][childColour]
} else {
v = ruleset[testCl][childColour] * childBags
v += ruleset[testCl][childColour]
}
count += v
}
return count
}
func PartTwo(instr string) int {
return countForBag(parse(instr), targetColour)
}

View file

@ -0,0 +1,100 @@
package main
import (
"adventOfCode/07-handyHaversacks/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()
}

View file

@ -0,0 +1,23 @@
{
"year": "2020",
"day": "7",
"title": "Handy Haversacks",
"testCases": {
"one": [
{
"input": "light red bags contain 1 bright white bag, 2 muted yellow bags.\ndark orange bags contain 3 bright white bags, 4 muted yellow bags.\nbright white bags contain 1 shiny gold bag.\nmuted yellow bags contain 2 shiny gold bags, 9 faded blue bags.\nshiny gold bags contain 1 dark olive bag, 2 vibrant plum bags.\ndark olive bags contain 3 faded blue bags, 4 dotted black bags.\nvibrant plum bags contain 5 faded blue bags, 6 dotted black bags.\nfaded blue bags contain no other bags.\ndotted black bags contain no other bags.\n",
"expected": 4
}
],
"two": [
{
"input": "light red bags contain 1 bright white bag, 2 muted yellow bags.\ndark orange bags contain 3 bright white bags, 4 muted yellow bags.\nbright white bags contain 1 shiny gold bag.\nmuted yellow bags contain 2 shiny gold bags, 9 faded blue bags.\nshiny gold bags contain 1 dark olive bag, 2 vibrant plum bags.\ndark olive bags contain 3 faded blue bags, 4 dotted black bags.\nvibrant plum bags contain 5 faded blue bags, 6 dotted black bags.\nfaded blue bags contain no other bags.\ndotted black bags contain no other bags.\n",
"expected": 32
},
{
"input": "shiny gold bags contain 2 dark red bags.\ndark red bags contain 2 dark orange bags.\ndark orange bags contain 2 dark yellow bags.\ndark yellow bags contain 2 dark green bags.\ndark green bags contain 2 dark blue bags.\ndark blue bags contain 2 dark violet bags.\ndark violet bags contain no other bags.\n",
"expected": 126
}
]
}
}

View file

@ -0,0 +1,67 @@
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" not in sys.argv:
print("Answers")
print("Part 1:", partOne(challenge_input))
print("Part 2:", partTwo(challenge_input))

View file

@ -0,0 +1,34 @@
import re
from typing import Dict
rule_regex = re.compile(r"(.+) bags contain (.+).")
definition_regex = re.compile(
r"(\d+) (.+) bags?"
) # A definiton is the section of a rule that contains the conditions
target_colour = "shiny gold"
def parse(instr: str) -> Dict[str, Dict[str, int]]:
inp = instr.strip().split("\n")
rules = {}
for rule in inp:
rr = rule_regex.match(rule)
container_bag = rr.group(1)
rule_set = rr.group(2).split(", ")
bag_rules = {}
for definition in rule_set:
rsr = definition_regex.match(definition)
# if this is false, it probably means we've encountered something saying "no other bags"
if rsr is not None:
bag_rules[rsr.group(2)] = int(rsr.group(1))
rules[container_bag] = bag_rules
return rules

View file

@ -0,0 +1,28 @@
from common import *
from typing import Dict
def check_bag(ruleset: Dict[str, Dict[str, int]], test_cl: str, target_cl: str) -> bool:
if (
target_cl in ruleset[test_cl]
): # bag colour can directly contain the target colour
return True
for child_colours in ruleset[test_cl]:
if check_bag(ruleset, child_colours, target_cl):
return True
return False
def partOne(instr: str) -> int:
rules = parse(instr)
can_contain_target = 0
for bag_colour in rules:
if bag_colour != target_colour:
if check_bag(rules, bag_colour, target_colour):
can_contain_target += 1
return can_contain_target

View file

@ -0,0 +1,29 @@
from common import *
from typing import Dict
def count_for_bag(ruleset: Dict[str, Dict[str, int]], test_cl: str) -> int:
if len(ruleset[test_cl]) == 0:
return -1
count = 0
for child_colour in ruleset[test_cl]:
child_bags = count_for_bag(ruleset, child_colour)
if child_bags == -1:
v = ruleset[test_cl][child_colour]
else:
v = ruleset[test_cl][child_colour] * child_bags
# The below line includes the number of bags that contain all the children - for
# example, if you had a bag type that had 6 child bags, and there were 3 of these
# master bags, the line above would add all the child bags (6*3 of them), and the line
# below would add the 3 container bags.
v += ruleset[test_cl][child_colour]
count += v
return count
def partTwo(instr: str) -> int:
rules = parse(instr)
return count_for_bag(rules, target_colour)

View file

@ -0,0 +1,31 @@
# [Day 8: Handheld Halting](https://adventofcode.com/2020/day/8)
<details><summary>Script output</summary>
```
python .\python\
AoC 2020: day 8 - Handheld Halting
Python 3.8.5
Test cases
1.1 pass
2.1 pass
Answers
Part 1: 1816
Part 2: 1149
go run .\go\
AoC 2020: day 8 - Handheld Halting
Go go1.15.2
Test cases
1.1 pass
2.1 pass
Answers
Part 1: 1816
Part 2: 1149
```
</details>

View file

@ -0,0 +1,30 @@
package challenge
import (
"strconv"
"strings"
)
type Instruction struct {
Opcode string
Operand int
}
func NewInstruction(instr string) Instruction {
icp := strings.Split(instr, " ")
opc, err := strconv.Atoi(icp[1])
if err != nil {
panic(err)
}
return Instruction{
Opcode: icp[0],
Operand: opc,
}
}
func parse(instr string) (o []Instruction) {
for _, v := range strings.Split(strings.TrimSpace(instr), "\n") {
o = append(o, NewInstruction(v))
}
return
}

View file

@ -0,0 +1,44 @@
package challenge
func PartOne(instr string) int {
instructions := parse(instr)
var (
acc int
pc int
visited []int
)
for {
{
var found bool
for _, v := range visited {
if v == pc {
found = true
break
}
}
if found {
return acc
} else {
visited = append(visited, pc)
}
}
cir := instructions[pc]
if cir.Opcode == "jmp" {
pc += cir.Operand
} else {
switch cir.Opcode {
case "acc":
acc += cir.Operand
case "nop":
}
pc += 1
}
}
}

View file

@ -0,0 +1,82 @@
package challenge
func execute(instructions []Instruction) (cleanExit bool, acc int) {
var (
pc int
visited []int
)
for {
if pc >= len(instructions) {
// Clean exit
return true, acc
}
{
var found bool
for _, v := range visited {
if v == pc {
found = true
break
}
}
if found {
return false, acc
} else {
visited = append(visited, pc)
}
}
cir := instructions[pc]
if cir.Opcode == "jmp" {
pc += cir.Operand
} else {
switch cir.Opcode {
case "acc":
acc += cir.Operand
case "nop":
}
pc += 1
}
}
}
func PartTwo(instr string) int {
masterInstructions := parse(instr)
var switchSetLocations []int
for idx, instruction := range masterInstructions {
if instruction.Opcode == "jmp" || instruction.Opcode == "nop" {
switchSetLocations = append(switchSetLocations, idx)
}
}
for _, slc := range switchSetLocations {
// Copy instruction set
instructions := make([]Instruction, len(masterInstructions))
copy(instructions, masterInstructions)
// Swap instruction
oldval := instructions[slc].Opcode
var newval string
if oldval == "jmp" {
newval = "nop"
} else if oldval == "nop" {
newval = "jmp"
}
instructions[slc].Opcode = newval
// Execute
cleanExit, acc := execute(instructions)
if cleanExit {
return acc
}
}
return 0
}

View file

@ -0,0 +1,100 @@
package main
import (
"adventOfCode/08-handheldHalting/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()
}

View file

@ -0,0 +1,19 @@
{
"year": "2020",
"day": "8",
"title": "Handheld Halting",
"testCases": {
"one": [
{
"input": "nop +0\nacc +1\njmp +4\nacc +3\njmp -3\nacc -99\nacc +1\njmp -4\nacc +6",
"expected": 5
}
],
"two": [
{
"input": "nop +0\nacc +1\njmp +4\nacc +3\njmp -3\nacc -99\nacc +1\njmp -4\nacc +6",
"expected": 8
}
]
}
}

View file

@ -0,0 +1,66 @@
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"])
print("Answers")
print("Part 1:", partOne(challenge_input))
print("Part 2:", partTwo(challenge_input))

View file

@ -0,0 +1,14 @@
from typing import List
class Instruction:
opcode: str
operand: int
def __init__(self, instr: str) -> None:
self.opcode, t = instr.split(" ")
self.operand = int(t)
def parse(instr: str) -> List[Instruction]:
return [Instruction(x) for x in instr.strip().split("\n")]

View file

@ -0,0 +1,28 @@
from common import *
def partOne(instr: str) -> int:
instructions = parse(instr)
acc = 0
pc = 0
visited = [] # indexes of visited instructions
while True:
if pc in visited:
return acc
else:
visited.append(pc)
cir = instructions[pc]
if cir.opcode == "jmp":
pc += cir.operand
else:
if cir.opcode == "acc":
acc += cir.operand
elif cir.opcode == "nop":
pass
pc += 1

View file

@ -0,0 +1,63 @@
import copy
from typing import List, Tuple
from common import *
def execute(instructions: List[Instruction]) -> Tuple[bool, int]:
# Returning false means a loop occured. Returning true means no loop occured.
# The value of the accumulator is always returned
acc = 0
pc = 0
visited = [] # indexes of visited instructions
while True:
if pc >= len(instructions):
# Clean exit
return True, acc
if pc in visited:
# Loop is going to occur at some point
return False, acc
else:
visited.append(pc)
cir = instructions[pc]
if cir.opcode == "jmp":
pc += cir.operand
else:
if cir.opcode == "acc":
acc += cir.operand
elif cir.opcode == "nop":
pass
pc += 1
def partTwo(instr: str) -> int:
master_instructions = parse(instr)
# I imagine there's probably a smart way to do this... but I'm going to bruteforce it haha
# Build list of locations of instructions to be switched
switch_set_locations = []
for idx, instruction in enumerate(master_instructions):
if instruction.opcode in ["jmp", "nop"]:
switch_set_locations.append(idx)
for slc in switch_set_locations:
instructions = copy.deepcopy(master_instructions)
# Switch instruction
oldval = instructions[slc].opcode
instructions[slc].opcode = "jmp" if oldval == "nop" else "nop"
# Execute
clean_exit, acc = execute(instructions)
if clean_exit:
break
return acc

View file

@ -0,0 +1,31 @@
# [Day 9: Encoding Error](https://adventofcode.com/2020/day/9)
<details><summary>Script output</summary>
```
python .\python\
AoC 2020: day 9 - Encoding Error
Python 3.8.5
Test cases
1.1 pass
2.1 pass
Answers
Part 1: 530627549
Part 2: 77730285
go run .\go\
AoC 2020: day 9 - Encoding Error
Go go1.15.2
Test cases
1.1 pass
2.1 pass
Answers
Part 1: 530627549
Part 2: 77730285
```
</details>

View file

@ -0,0 +1,17 @@
package challenge
import (
"strconv"
"strings"
)
func parse(instr string) (o []int) {
for _, x := range strings.Split(strings.TrimSpace(instr), "\n") {
i, err := strconv.Atoi(x)
if err != nil {
panic(err)
}
o = append(o, i)
}
return
}

View file

@ -0,0 +1,60 @@
package challenge
import (
"sort"
)
func PartOne(instr string) int {
inputSlice := parse(instr)
preambleLen := 25
if len(inputSlice) < 30 { // This is for test inputs
preambleLen = 5
}
pointer := preambleLen
for pointer < len(inputSlice) {
if !hasCombinations(inputSlice[pointer-preambleLen:pointer], inputSlice[pointer]) {
return inputSlice[pointer]
}
pointer += 1
}
return 0
}
func hasCombinations(options []int, target int) bool {
// Remove duplicate options
{
keys := make(map[int]bool)
var new []int
for _, entry := range options {
if _, value := keys[entry]; !value {
keys[entry] = true
new = append(new, entry)
}
}
options = new
}
sort.Ints(options) // sorts in place
var lPtr int
rPtr := len(options) - 1
for lPtr < rPtr {
v := options[lPtr] + options[rPtr]
if v == target {
return true
} else if v < target {
lPtr += 1
} else {
rPtr -= 1
}
}
return false
}

View file

@ -0,0 +1,49 @@
package challenge
func PartTwo(instr string) int {
inputSlice := parse(instr)
targetValue := PartOne(instr)
if targetValue == 0 {
return 0
}
var pointer int
for pointer < len(inputSlice) {
startPoint := pointer
iptr := pointer
var count int
for iptr < len(inputSlice) {
count += inputSlice[iptr]
if count == targetValue {
if iptr-startPoint < 2 {
break
}
allValues := inputSlice[startPoint : iptr+1]
min := allValues[0]
max := allValues[0]
for _, v := range allValues {
if v < min {
min = v
}
if v > max {
max = v
}
}
return min + max
}
if count > targetValue {
break
}
iptr += 1
}
pointer += 1
}
return 0
}

View file

@ -0,0 +1,100 @@
package main
import (
"adventOfCode/09-encodingError/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()
}

View file

@ -0,0 +1,19 @@
{
"year": "2020",
"day": "9",
"title": "Encoding Error",
"testCases": {
"one": [
{
"input": "35\n20\n15\n25\n47\n40\n62\n55\n65\n95\n102\n117\n150\n182\n127\n219\n299\n277\n309\n576",
"expected": 127
}
],
"two": [
{
"input": "35\n20\n15\n25\n47\n40\n62\n55\n65\n95\n102\n117\n150\n182\n127\n219\n299\n277\n309\n576",
"expected": 62
}
]
}
}

View file

@ -0,0 +1,66 @@
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"])
print("Answers")
print("Part 1:", partOne(challenge_input))
print("Part 2:", partTwo(challenge_input))

View file

@ -0,0 +1,5 @@
from typing import List
def parse(instr: str) -> List[int]:
return [int(x) for x in instr.strip().split("\n")]

View file

@ -0,0 +1,41 @@
from typing import Set
from common import *
def has_combinations(options: Set[int], target: int) -> bool:
# Returns true of the options set has two values that sum to be the target, else returns false
options = sorted(options)
l_ptr = 0
r_ptr = len(options) - 1
while l_ptr < r_ptr:
v = options[l_ptr] + options[r_ptr]
if v == target:
return True
elif v < target:
l_ptr += 1
else:
r_ptr -= 1
return False
def partOne(instr: str) -> int:
input_list = parse(instr)
preamble_len = 25
if len(input_list) < 30: # This is for tests
preamble_len = 5
pointer = preamble_len
while pointer < len(input_list):
if not has_combinations(
set(input_list[pointer - preamble_len : pointer]), input_list[pointer]
):
return input_list[pointer]
pointer += 1
return 0

View file

@ -0,0 +1,36 @@
from common import *
from partOne import partOne
def partTwo(instr: str) -> int:
input_list = parse(instr)
target_value = partOne(instr)
if target_value == 0:
return 0
pointer = 0
while pointer < len(input_list):
# iterate consecutive values from here
start_point = pointer
iptr = pointer # Internal PoinTeR
count = 0
while iptr < len(input_list):
count += input_list[iptr]
if count == target_value:
# must be at least two values
if iptr - start_point < 2:
break
all_values = input_list[start_point : iptr + 1]
return min(all_values) + max(all_values)
if count > target_value:
break
iptr += 1
pointer += 1
return 0

View file

@ -0,0 +1,68 @@
# [Day 10: Adapter Array](https://adventofcode.com/2020/day/10)
### Part two explaination
If you take away all the extra details, all we're being asked to do in part two is to find the number of valid possible orderings of the list of joltages where a given ordering is valid if:
* it starts at zero and ends at 3 more than the highest input value
* each value in the input is followed by a value that is between 1 and 3 more than itself
* for example, if your value was `5`, it could be followed by `6`, `7`, or `8` only.
* this also means that the value `5` can only be preceded by `2`, `3` or `4`.
We do not need to use all values from the input like we did in part one.
The number of valid combinations to reach value `n` is the sum of the number of valid combinations of `n-1`, `n-2` and `n-3`, assuming values are sorted in ascending order. If one of these numbers is not present in the input, it can be treated as zero.
Where `n` is `0`, there is only one way to combine a single number, hence it has a combinations value of 1.
For example, if we have the following data:
| n | Combinations |
|---|---------------|
| 0 | 1 |
| 1 | 1 |
| 2 | 2 |
We can find the number of valid combinations to get to the value `n=3` by doing `1 + 1 + 2 = 4`.
Using this method, we can find the answer to part two with a very small algorithm that's only 7 lines long, excluding data parsing.
### Related
* [Computerphile - Tail recursion](https://www.youtube.com/watch?v=_JtPhF8MshA)
---
<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>

Some files were not shown because too many files have changed in this diff Show more