Add 2020 solutions
Signed-off-by: AKU <tom@tdpain.net>
This commit is contained in:
parent
1ad039bb13
commit
25f4d9d658
240 changed files with 10455 additions and 465 deletions
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
--------------------------------------------------------------------------------
|
|
@ -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
|
||||
}
|
18
challenges/2020/01-reportRepair/go/challenge/common.go
Normal file
18
challenges/2020/01-reportRepair/go/challenge/common.go
Normal 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
|
||||
}
|
15
challenges/2020/01-reportRepair/go/challenge/partOne.go
Normal file
15
challenges/2020/01-reportRepair/go/challenge/partOne.go
Normal 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
|
||||
}
|
17
challenges/2020/01-reportRepair/go/challenge/partTwo.go
Normal file
17
challenges/2020/01-reportRepair/go/challenge/partTwo.go
Normal 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
|
||||
}
|
88
challenges/2020/01-reportRepair/go/main.go
Normal file
88
challenges/2020/01-reportRepair/go/main.go
Normal 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()
|
||||
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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")]
|
54
challenges/2020/01-reportRepair/python/__main__.py
Normal file
54
challenges/2020/01-reportRepair/python/__main__.py
Normal 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))
|
5
challenges/2020/01-reportRepair/python/common.py
Normal file
5
challenges/2020/01-reportRepair/python/common.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from typing import List
|
||||
|
||||
|
||||
def parse(instr: str) -> List:
|
||||
return [int(x) for x in instr.strip().split("\n")]
|
12
challenges/2020/01-reportRepair/python/partOne.py
Normal file
12
challenges/2020/01-reportRepair/python/partOne.py
Normal 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
|
13
challenges/2020/01-reportRepair/python/partTwo.py
Normal file
13
challenges/2020/01-reportRepair/python/partTwo.py
Normal 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
|
14
challenges/2020/01-reportRepair/testCases.json
Normal file
14
challenges/2020/01-reportRepair/testCases.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
||||
--------------------------------------------------------------------------------
|
|
@ -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]): (.+)`)
|
12
challenges/2020/02-passwordPhilosophy/go/challenge/common.go
Normal file
12
challenges/2020/02-passwordPhilosophy/go/challenge/common.go
Normal 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]): (.+)`)
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
88
challenges/2020/02-passwordPhilosophy/go/main.go
Normal file
88
challenges/2020/02-passwordPhilosophy/go/main.go
Normal 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()
|
||||
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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")
|
56
challenges/2020/02-passwordPhilosophy/python/__main__.py
Normal file
56
challenges/2020/02-passwordPhilosophy/python/__main__.py
Normal 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))
|
5
challenges/2020/02-passwordPhilosophy/python/common.py
Normal file
5
challenges/2020/02-passwordPhilosophy/python/common.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from typing import List
|
||||
|
||||
|
||||
def parse(instr: str) -> List:
|
||||
return instr.strip().split("\n")
|
44
challenges/2020/02-passwordPhilosophy/python/partOne.py
Normal file
44
challenges/2020/02-passwordPhilosophy/python/partOne.py
Normal 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
|
50
challenges/2020/02-passwordPhilosophy/python/partTwo.py
Normal file
50
challenges/2020/02-passwordPhilosophy/python/partTwo.py
Normal 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
|
14
challenges/2020/02-passwordPhilosophy/testCases.json
Normal file
14
challenges/2020/02-passwordPhilosophy/testCases.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
--------------------------------------------------------------------------------
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package challenge
|
||||
|
||||
func PartOne(instr string) int {
|
||||
return findCollisions(parse(instr), 3, 1)
|
||||
}
|
|
@ -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
|
||||
}
|
90
challenges/2020/03-tobogganTrajectory/go/main.go
Normal file
90
challenges/2020/03-tobogganTrajectory/go/main.go
Normal 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()
|
||||
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"inputFile": "input.txt",
|
||||
"testCases": {}
|
||||
}
|
|
@ -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
|
56
challenges/2020/03-tobogganTrajectory/python/__main__.py
Normal file
56
challenges/2020/03-tobogganTrajectory/python/__main__.py
Normal 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))
|
20
challenges/2020/03-tobogganTrajectory/python/common.py
Normal file
20
challenges/2020/03-tobogganTrajectory/python/common.py
Normal 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
|
5
challenges/2020/03-tobogganTrajectory/python/partOne.py
Normal file
5
challenges/2020/03-tobogganTrajectory/python/partOne.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from common import *
|
||||
|
||||
|
||||
def partOne(instr: str) -> int:
|
||||
return find_collisions(parse(instr), 3, 1)
|
15
challenges/2020/03-tobogganTrajectory/python/partTwo.py
Normal file
15
challenges/2020/03-tobogganTrajectory/python/partTwo.py
Normal 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
|
33
challenges/2020/04-passportProcessing/README.md
Normal file
33
challenges/2020/04-passportProcessing/README.md
Normal 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>
|
11
challenges/2020/04-passportProcessing/go/challenge/common.go
Normal file
11
challenges/2020/04-passportProcessing/go/challenge/common.go
Normal 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
|
||||
}
|
|
@ -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
|
||||
}
|
154
challenges/2020/04-passportProcessing/go/challenge/partTwo.go
Normal file
154
challenges/2020/04-passportProcessing/go/challenge/partTwo.go
Normal 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]
|
||||
}
|
90
challenges/2020/04-passportProcessing/go/main.go
Normal file
90
challenges/2020/04-passportProcessing/go/main.go
Normal 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()
|
||||
|
||||
}
|
56
challenges/2020/04-passportProcessing/python/__main__.py
Normal file
56
challenges/2020/04-passportProcessing/python/__main__.py
Normal 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))
|
5
challenges/2020/04-passportProcessing/python/common.py
Normal file
5
challenges/2020/04-passportProcessing/python/common.py
Normal 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")]
|
37
challenges/2020/04-passportProcessing/python/partOne.py
Normal file
37
challenges/2020/04-passportProcessing/python/partOne.py
Normal 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
|
103
challenges/2020/04-passportProcessing/python/partTwo.py
Normal file
103
challenges/2020/04-passportProcessing/python/partTwo.py
Normal 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
|
18
challenges/2020/04-passportProcessing/testCases.json
Normal file
18
challenges/2020/04-passportProcessing/testCases.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
33
challenges/2020/05-binaryBoarding/README.md
Normal file
33
challenges/2020/05-binaryBoarding/README.md
Normal 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>
|
57
challenges/2020/05-binaryBoarding/go/challenge/common.go
Normal file
57
challenges/2020/05-binaryBoarding/go/challenge/common.go
Normal 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
|
||||
}
|
16
challenges/2020/05-binaryBoarding/go/challenge/partOne.go
Normal file
16
challenges/2020/05-binaryBoarding/go/challenge/partOne.go
Normal 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
|
||||
}
|
36
challenges/2020/05-binaryBoarding/go/challenge/partTwo.go
Normal file
36
challenges/2020/05-binaryBoarding/go/challenge/partTwo.go
Normal 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
|
||||
}
|
106
challenges/2020/05-binaryBoarding/go/main.go
Normal file
106
challenges/2020/05-binaryBoarding/go/main.go
Normal 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()
|
||||
|
||||
}
|
22
challenges/2020/05-binaryBoarding/info.json
Normal file
22
challenges/2020/05-binaryBoarding/info.json
Normal 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": []
|
||||
}
|
||||
}
|
66
challenges/2020/05-binaryBoarding/python/__main__.py
Normal file
66
challenges/2020/05-binaryBoarding/python/__main__.py
Normal 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))
|
47
challenges/2020/05-binaryBoarding/python/common.py
Normal file
47
challenges/2020/05-binaryBoarding/python/common.py
Normal 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
|
14
challenges/2020/05-binaryBoarding/python/partOne.py
Normal file
14
challenges/2020/05-binaryBoarding/python/partOne.py
Normal 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
|
41
challenges/2020/05-binaryBoarding/python/partTwo.py
Normal file
41
challenges/2020/05-binaryBoarding/python/partTwo.py
Normal 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
|
31
challenges/2020/06-customCustoms/README.md
Normal file
31
challenges/2020/06-customCustoms/README.md
Normal 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>
|
21
challenges/2020/06-customCustoms/go/challenge/common.go
Normal file
21
challenges/2020/06-customCustoms/go/challenge/common.go
Normal 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
|
||||
}
|
30
challenges/2020/06-customCustoms/go/challenge/partOne.go
Normal file
30
challenges/2020/06-customCustoms/go/challenge/partOne.go
Normal 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
|
||||
}
|
51
challenges/2020/06-customCustoms/go/challenge/partTwo.go
Normal file
51
challenges/2020/06-customCustoms/go/challenge/partTwo.go
Normal 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
|
||||
}
|
100
challenges/2020/06-customCustoms/go/main.go
Normal file
100
challenges/2020/06-customCustoms/go/main.go
Normal 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()
|
||||
|
||||
}
|
19
challenges/2020/06-customCustoms/info.json
Normal file
19
challenges/2020/06-customCustoms/info.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
66
challenges/2020/06-customCustoms/python/__main__.py
Normal file
66
challenges/2020/06-customCustoms/python/__main__.py
Normal 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))
|
5
challenges/2020/06-customCustoms/python/common.py
Normal file
5
challenges/2020/06-customCustoms/python/common.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from typing import List
|
||||
|
||||
|
||||
def parse(instr: str) -> List[str]:
|
||||
return instr.strip().split("\n\n")
|
28
challenges/2020/06-customCustoms/python/partOne.py
Normal file
28
challenges/2020/06-customCustoms/python/partOne.py
Normal 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
|
44
challenges/2020/06-customCustoms/python/partTwo.py
Normal file
44
challenges/2020/06-customCustoms/python/partTwo.py
Normal 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
|
33
challenges/2020/07-handyHaversacks/README.md
Normal file
33
challenges/2020/07-handyHaversacks/README.md
Normal 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>
|
42
challenges/2020/07-handyHaversacks/go/challenge/common.go
Normal file
42
challenges/2020/07-handyHaversacks/go/challenge/common.go
Normal 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
|
||||
}
|
41
challenges/2020/07-handyHaversacks/go/challenge/partOne.go
Normal file
41
challenges/2020/07-handyHaversacks/go/challenge/partOne.go
Normal 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
|
||||
}
|
28
challenges/2020/07-handyHaversacks/go/challenge/partTwo.go
Normal file
28
challenges/2020/07-handyHaversacks/go/challenge/partTwo.go
Normal 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)
|
||||
}
|
100
challenges/2020/07-handyHaversacks/go/main.go
Normal file
100
challenges/2020/07-handyHaversacks/go/main.go
Normal 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()
|
||||
|
||||
}
|
23
challenges/2020/07-handyHaversacks/info.json
Normal file
23
challenges/2020/07-handyHaversacks/info.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
67
challenges/2020/07-handyHaversacks/python/__main__.py
Normal file
67
challenges/2020/07-handyHaversacks/python/__main__.py
Normal 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))
|
34
challenges/2020/07-handyHaversacks/python/common.py
Normal file
34
challenges/2020/07-handyHaversacks/python/common.py
Normal 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
|
28
challenges/2020/07-handyHaversacks/python/partOne.py
Normal file
28
challenges/2020/07-handyHaversacks/python/partOne.py
Normal 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
|
29
challenges/2020/07-handyHaversacks/python/partTwo.py
Normal file
29
challenges/2020/07-handyHaversacks/python/partTwo.py
Normal 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)
|
31
challenges/2020/08-handheldHalting/README.md
Normal file
31
challenges/2020/08-handheldHalting/README.md
Normal 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>
|
30
challenges/2020/08-handheldHalting/go/challenge/common.go
Normal file
30
challenges/2020/08-handheldHalting/go/challenge/common.go
Normal 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
|
||||
}
|
44
challenges/2020/08-handheldHalting/go/challenge/partOne.go
Normal file
44
challenges/2020/08-handheldHalting/go/challenge/partOne.go
Normal 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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
82
challenges/2020/08-handheldHalting/go/challenge/partTwo.go
Normal file
82
challenges/2020/08-handheldHalting/go/challenge/partTwo.go
Normal 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
|
||||
}
|
100
challenges/2020/08-handheldHalting/go/main.go
Normal file
100
challenges/2020/08-handheldHalting/go/main.go
Normal 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()
|
||||
|
||||
}
|
19
challenges/2020/08-handheldHalting/info.json
Normal file
19
challenges/2020/08-handheldHalting/info.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
66
challenges/2020/08-handheldHalting/python/__main__.py
Normal file
66
challenges/2020/08-handheldHalting/python/__main__.py
Normal 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))
|
14
challenges/2020/08-handheldHalting/python/common.py
Normal file
14
challenges/2020/08-handheldHalting/python/common.py
Normal 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")]
|
28
challenges/2020/08-handheldHalting/python/partOne.py
Normal file
28
challenges/2020/08-handheldHalting/python/partOne.py
Normal 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
|
63
challenges/2020/08-handheldHalting/python/partTwo.py
Normal file
63
challenges/2020/08-handheldHalting/python/partTwo.py
Normal 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
|
31
challenges/2020/09-encodingError/README.md
Normal file
31
challenges/2020/09-encodingError/README.md
Normal 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>
|
17
challenges/2020/09-encodingError/go/challenge/common.go
Normal file
17
challenges/2020/09-encodingError/go/challenge/common.go
Normal 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
|
||||
}
|
60
challenges/2020/09-encodingError/go/challenge/partOne.go
Normal file
60
challenges/2020/09-encodingError/go/challenge/partOne.go
Normal 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
|
||||
}
|
49
challenges/2020/09-encodingError/go/challenge/partTwo.go
Normal file
49
challenges/2020/09-encodingError/go/challenge/partTwo.go
Normal 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
|
||||
}
|
100
challenges/2020/09-encodingError/go/main.go
Normal file
100
challenges/2020/09-encodingError/go/main.go
Normal 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()
|
||||
|
||||
}
|
19
challenges/2020/09-encodingError/info.json
Normal file
19
challenges/2020/09-encodingError/info.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
66
challenges/2020/09-encodingError/python/__main__.py
Normal file
66
challenges/2020/09-encodingError/python/__main__.py
Normal 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))
|
5
challenges/2020/09-encodingError/python/common.py
Normal file
5
challenges/2020/09-encodingError/python/common.py
Normal 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")]
|
41
challenges/2020/09-encodingError/python/partOne.py
Normal file
41
challenges/2020/09-encodingError/python/partOne.py
Normal 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
|
36
challenges/2020/09-encodingError/python/partTwo.py
Normal file
36
challenges/2020/09-encodingError/python/partTwo.py
Normal 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
|
68
challenges/2020/10-adapterArray/README.md
Normal file
68
challenges/2020/10-adapterArray/README.md
Normal 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
Loading…
Add table
Add a link
Reference in a new issue