Reset for AoC 2021
Signed-off-by: AKP <tom@tdpain.net>
This commit is contained in:
parent
53e5edf207
commit
8f678ef382
257 changed files with 2 additions and 11206 deletions
144
.github/README.md
vendored
144
.github/README.md
vendored
|
@ -1,147 +1,7 @@
|
|||
# Advent of Code 2020
|
||||
# Advent of Code 2021
|
||||
|
||||
Solutions to the [2020 Advent of Code](https://adventofcode.com/2020), in at least Python and Go. I make no claims that they're the best way to do things, but if they produce a result within 15 seconds, I'm happy.
|
||||
|
||||
Go solutions are near direct copies of the Python solutions and may be added a few days afterwards.
|
||||
Solutions to the [2021 Advent of Code](https://adventofcode.com/2021).
|
||||
|
||||
Puzzle inputs and descriptions are not included in this repository. You'll have to get these yourself from the AoC website. [Here's why](https://www.reddit.com/r/adventofcode/comments/k99rod/sharing_input_data_were_we_requested_not_to/gf2ukkf/?context=3).
|
||||
|
||||
[AoC website](https://adventofcode.com) - [AoC subreddit](https://www.reddit.com/r/adventofcode) - [AoC awesome list](https://github.com/Bogdanp/awesome-advent-of-code)
|
||||
|
||||
---
|
||||
|
||||
**Key:** ![Completed][check] is completed, ![Incomplete][cross] is incomplete, ![Partially complete][partial] is partially complete (does not have both languages or does not have solutions to both parts) and ![Not yet attempted][pending] is released but not yet attempted. \* means that this day has a visualisation
|
||||
|
||||
<!-- PARSE START -->
|
||||
|
||||
| Day | | Python | Go | Notes |
|
||||
| ----------------------------------- | ------------------------------ | ----------------------------------------- | ------------------------------------- | -------------------- |
|
||||
| [1](/01-reportRepair) | ![Completed][check] | [Link](/01-reportRepair/python) | [Link](/01-reportRepair/go) | |
|
||||
| [2](/02-passwordPhilosophy) | ![Completed][check] | [Link](/02-passwordPhilosophy/python) | [Link](/02-passwordPhilosophy/go) | |
|
||||
| [3](/03-tobogganTrajectory) | ![Completed][check] | [Link](/03-tobogganTrajectory/python) | [Link](/03-tobogganTrajectory/go) | |
|
||||
| [4](/04-passportProcessing) | ![Completed][check] | [Link](/04-passportProcessing/python) | [Link](/04-passportProcessing/go) | |
|
||||
| [5](/05-binaryBoarding) | ![Completed][check] | [Link](/05-binaryBoarding/python) | [Link](/05-binaryBoarding/go) | |
|
||||
| [6](/06-customCustoms) | ![Completed][check] | [Link](/06-customCustoms/python) | [Link](/06-customCustoms/go) | |
|
||||
| [7](/07-handyHaversacks) | ![Completed][check] | [Link](/07-handyHaversacks/python) | [Link](/07-handyHaversacks/go) | |
|
||||
| [8](/08-handheldHalting) | ![Completed][check] | [Link](/08-handheldHalting/python) | [Link](/08-handheldHalting/go) | |
|
||||
| [9](/09-encodingError) | ![Completed][check] | [Link](/09-encodingError/python) | [Link](/09-encodingError/go) | |
|
||||
| [10](/10-adapterArray) | ![Completed][check] | [Link](/10-adapterArray/python) | [Link](/10-adapterArray/go) | |
|
||||
| [11](/11-seatingSystem) \* | ![Completed][check] | [Link](/11-seatingSystem/python) | [Link](/11-seatingSystem/python) | |
|
||||
| [12](/12-rainRisk) \* | ![Completed][check] | [Link](/12-rainRisk/python) | [Link](/12-rainRisk/go) | |
|
||||
| [13](/13-shuttleSearch) | ![Partially complete][partial] | [Link](/13-shuttleSearch/python) | | |
|
||||
| [14](/14-dockingData) | ![Completed][check] | [Link](/14-dockingData/python) | [Link](/14-dockingData/go) | |
|
||||
| [15](/15-rambunctiousRecitation) \* | ![Completed][check] | [Link](/15-rambunctiousRecitation/python) | [Link](/15-rambunctiousRecitation/go) | |
|
||||
| [16](/16-ticketTranslation) | ![Completed][check] | [Link](/16-ticketTranslation/python) | [Link](/16-ticketTranslation/go) | |
|
||||
| [17](/17-conwayCubes) | ![Partially complete][partial] | [Link](/17-conwayCubes/python) | | |
|
||||
| [18](/18-operationOrder) | ![Partially complete][partial] | [Link](/18-operationOrder/python) | | |
|
||||
| [19](/19-monsterMessages) | ![Completed][check] | [Link](/19-monsterMessages/python) | [Link](/19-monsterMessages/go) | |
|
||||
| [20](/20-jurassicJigsaw) | ![Partially complete][partial] | [Link](/20-jurassicJigsaw/python) | | Only part one solved |
|
||||
| [21](/21-allergenAmusement) | ![Partially complete][partial] | [Link](/21-allergenAmusement/python) | | |
|
||||
| [22](/22-crabCombat) | ![Partially complete][partial] | [Link](/22-crabCombat/python) | | |
|
||||
| [23](/23-crabCups) | ![Incomplete][cross] | | | |
|
||||
| [24](/24-lobbyLayout) | ![Partially complete][partial] | [Link](/24-lobbyLayout/python) | | |
|
||||
| [25](/25-comboBreaker) | ![Completed][check] | [Link](/25-comboBreaker/python) | [Link](/25-comboBreaker/go) | |
|
||||
|
||||
<!-- PARSE END -->
|
||||
|
||||
---
|
||||
|
||||
### Challenge summaries
|
||||
|
||||
**2020.01** - Find two/three numbers that sum to 2020
|
||||
|
||||
**2020.02** - Data validation
|
||||
|
||||
**2020.03** - Finding obstacles on a given path through a 2D array
|
||||
|
||||
**2020.04** - More data validation!
|
||||
|
||||
**2020.05** - Binary data/searching but also not
|
||||
|
||||
**2020.06** - Dealing with combinations of characters
|
||||
|
||||
**2020.07** - Dealing with large amounts of nested objects
|
||||
|
||||
**2020.08** - Write a simple interpreter with infinite loop detection, trial and error to find a value to modify
|
||||
|
||||
**2020.09** - Finding numbers that match different conditions within a sequence
|
||||
|
||||
**2020.10** - Finding permutations of a list that satisfy various constraints
|
||||
|
||||
**2020.11** - Build a custom cellular automata with weird neighborhoods
|
||||
|
||||
**2020.12** - Moving a virtual boat around a virtual sea with weird virtual instructions
|
||||
|
||||
**2020.13** - Chinese remainder theorem + buses = ???
|
||||
|
||||
**2020.14** - Funky bitmasks
|
||||
|
||||
**2020.15** - The Van Eck sequence
|
||||
|
||||
**2020.16** - Working out what values in a list means what by means of deduction and a set of rules
|
||||
|
||||
**2020.17** - ~~3D~~ **4**D cellular automata!
|
||||
|
||||
**2020.18** - Maths, but if BIDMAS didn't exist (the shunting yard algorithm)
|
||||
|
||||
**2020.19** - Generating regexes ~~as long as~~ longer than your arm and then making them recursive
|
||||
|
||||
**2020.20** - Solving a jigsaw programmatically and then looking for specific sequences of characters within the solved jigsaw
|
||||
|
||||
**2020.21** - Making deductions about values that are contained within multiple lists
|
||||
|
||||
**2020.22** - Simulating card games, and then making that card game recursive
|
||||
|
||||
**2020.23** - Crabs playing with cups
|
||||
|
||||
**2020.24** - Cellular automata, but this time with hexagons
|
||||
|
||||
**2020.25** - Reversing handshakes between a door and a RFID card (_"Unfortunately for the door, you know a thing or two about cryptographic handshakes"_)
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
_(Boilerplate code and code that generates visualisations is not included)_
|
||||
|
||||
---
|
||||
|
||||
<!-- RANK START -->
|
||||
|
||||
### Personal day-by-day stats
|
||||
|
||||
```
|
||||
--------Part 1-------- --------Part 2--------
|
||||
Day Time Rank Score Time Rank Score
|
||||
25 07:19:29 7966 0 - - -
|
||||
24 06:03:39 7277 0 06:50:17 6382 0
|
||||
22 06:37:56 9949 0 07:46:00 7333 0
|
||||
21 07:07:02 7031 0 07:34:43 6897 0
|
||||
20 12:40:05 9000 0 - - -
|
||||
19 07:14:52 7163 0 10:08:56 6083 0
|
||||
18 13:02:40 15057 0 17:01:47 15031 0
|
||||
17 19:16:58 17492 0 19:48:07 16771 0
|
||||
16 08:37:03 16270 0 11:24:49 13401 0
|
||||
15 11:38:11 20197 0 11:50:13 18272 0
|
||||
14 13:01:47 20661 0 14:41:22 17482 0
|
||||
13 05:43:41 16692 0 07:55:25 9661 0
|
||||
12 07:49:19 18545 0 09:08:54 16885 0
|
||||
11 12:07:36 24295 0 12:38:25 19932 0
|
||||
10 13:38:25 35155 0 15:10:40 24968 0
|
||||
9 12:26:06 34364 0 12:55:00 32791 0
|
||||
8 06:56:11 26688 0 07:13:46 22653 0
|
||||
7 12:03:04 29181 0 13:02:47 25777 0
|
||||
6 05:03:12 23094 0 05:22:54 21650 0
|
||||
5 06:42:59 25458 0 07:27:47 25658 0
|
||||
4 04:59:28 26236 0 11:53:07 34630 0
|
||||
3 07:18:34 33113 0 13:03:50 45815 0
|
||||
2 11:33:00 47471 0 11:38:14 44961 0
|
||||
1 18:09:35 65566 0 18:12:26 60825 0
|
||||
```
|
||||
|
||||
<!-- RANK END -->
|
||||
|
||||
[check]: https://github.com/codemicro/adventOfCode/blob/master/.github/check.png?raw=true
|
||||
[cross]: https://github.com/codemicro/adventOfCode/blob/master/.github/cross.png?raw=true
|
||||
[partial]: https://github.com/codemicro/adventOfCode/blob/master/.github/partial.png?raw=true
|
||||
[pending]: https://github.com/codemicro/adventOfCode/blob/master/.github/asterisk.png?raw=true
|
||||
|
|
BIN
.github/asterisk.png
vendored
BIN
.github/asterisk.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 3 KiB |
BIN
.github/check.png
vendored
BIN
.github/check.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 2.9 KiB |
1
.github/cloc.json
vendored
1
.github/cloc.json
vendored
File diff suppressed because one or more lines are too long
94
.github/clocgen.py
vendored
94
.github/clocgen.py
vendored
|
@ -1,94 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
from matplotlib.lines import Line2D
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def best_fit(X, Y):
|
||||
|
||||
xbar = sum(X) / len(X)
|
||||
ybar = sum(Y) / len(Y)
|
||||
n = len(X) # or len(Y)
|
||||
|
||||
numer = sum([xi * yi for xi, yi in zip(X, Y)]) - n * xbar * ybar
|
||||
denum = sum([xi ** 2 for xi in X]) - n * xbar ** 2
|
||||
|
||||
b = numer / denum
|
||||
a = ybar - b * xbar
|
||||
|
||||
return a, b
|
||||
|
||||
|
||||
with open(sys.argv[1], errors="ignore") as f:
|
||||
cloc_results = json.loads(f.read())
|
||||
|
||||
python_by_day = {}
|
||||
go_by_day = {}
|
||||
|
||||
for file in cloc_results["files"]:
|
||||
if file["language"].lower() == "python":
|
||||
target_dict = python_by_day
|
||||
elif file["language"].lower() == "go":
|
||||
target_dict = go_by_day
|
||||
else:
|
||||
continue
|
||||
|
||||
split_name = file["name"].split(os.path.sep)
|
||||
if split_name[-1].lower() not in [
|
||||
"__main__.py",
|
||||
"main.go",
|
||||
"visualise.py",
|
||||
"visualise.go",
|
||||
]:
|
||||
try:
|
||||
day_num = int(split_name[0].split("-")[0])
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if day_num not in target_dict:
|
||||
target_dict[day_num] = file["code"]
|
||||
else:
|
||||
target_dict[day_num] += file["code"]
|
||||
|
||||
print(python_by_day)
|
||||
print(go_by_day)
|
||||
|
||||
days_array = [i + 1 for i in range(max(len(python_by_day), len(go_by_day)))]
|
||||
|
||||
# Add Python
|
||||
python_colour = "#3572a5"
|
||||
|
||||
keys = list(sorted(python_by_day))
|
||||
plt.plot(keys, [python_by_day[key] for key in keys], color=python_colour)
|
||||
plt.scatter(keys, [python_by_day[key] for key in keys], color=python_colour, s=15)
|
||||
|
||||
a, b = best_fit(keys, [python_by_day[key] for key in keys])
|
||||
yfit = [a + b * xi for xi in days_array]
|
||||
plt.plot(days_array, yfit, color=python_colour, linestyle=":")
|
||||
|
||||
# Add Go
|
||||
golang_colour = "#00add8"
|
||||
|
||||
keys = list(sorted(go_by_day))
|
||||
plt.plot(keys, [go_by_day[key] for key in keys], color=golang_colour)
|
||||
plt.scatter(keys, [go_by_day[key] for key in keys], color=golang_colour, s=15)
|
||||
|
||||
a, b = best_fit(keys, [go_by_day[key] for key in keys])
|
||||
yfit = [a + b * xi for xi in days_array]
|
||||
plt.plot(days_array, yfit, color=golang_colour, linestyle=":")
|
||||
|
||||
custom_lines = [
|
||||
Line2D([0], [0], color=python_colour, lw=2),
|
||||
Line2D([0], [0], color=golang_colour, lw=2),
|
||||
]
|
||||
|
||||
plt.legend(custom_lines, ["Python", "Golang"])
|
||||
|
||||
plt.title("Lines of code by day")
|
||||
|
||||
plt.xticks(days_array)
|
||||
plt.xlabel("Day")
|
||||
plt.ylabel("Lines of code")
|
||||
|
||||
plt.savefig(sys.argv[2])
|
BIN
.github/clocgraph.png
vendored
BIN
.github/clocgraph.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 57 KiB |
BIN
.github/cross.png
vendored
BIN
.github/cross.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 3 KiB |
BIN
.github/image-20201205170258549.png
vendored
BIN
.github/image-20201205170258549.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 32 KiB |
BIN
.github/partial.png
vendored
BIN
.github/partial.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 2.5 KiB |
77
.github/tablegen.py
vendored
77
.github/tablegen.py
vendored
|
@ -1,77 +0,0 @@
|
|||
from datetime import datetime
|
||||
import re
|
||||
import sys
|
||||
from bs4 import BeautifulSoup
|
||||
import requests
|
||||
|
||||
today_day = datetime.now().day
|
||||
|
||||
readme_text = open(sys.argv[1]).read().strip().split("\n")
|
||||
|
||||
table_start_flag = "<!-- PARSE START -->"
|
||||
table_end_flag = "<!-- PARSE END -->"
|
||||
|
||||
rank_start_flag = "<!-- RANK START -->"
|
||||
rank_end_flag = "<!-- RANK END -->"
|
||||
|
||||
table_lines = []
|
||||
in_table = False
|
||||
for line in readme_text:
|
||||
line = line.strip()
|
||||
|
||||
if line == table_end_flag:
|
||||
in_table = False
|
||||
if in_table:
|
||||
table_lines.append(line)
|
||||
elif line == table_start_flag:
|
||||
in_table = True
|
||||
|
||||
for i, l in enumerate(table_lines):
|
||||
rs = re.match(r"\|\s*(\d{1,})\s*\|\s*\|.+\|.+\|", l)
|
||||
if rs is not None and int(rs.group(1)) == today_day:
|
||||
|
||||
table_lines[i] = f"| {today_day} | ![Not yet attempted][pending] | | | |"
|
||||
|
||||
rank_lines = "### Personal day-by-day stats\n\n```".split("\n")
|
||||
r = requests.get(
|
||||
"https://adventofcode.com/2020/leaderboard/self", cookies={"session": sys.argv[2]}
|
||||
)
|
||||
soup = BeautifulSoup(r.text, features="html.parser")
|
||||
rank_lines += (
|
||||
soup.find("article")
|
||||
.get_text()
|
||||
.split("and 0 otherwise.")[-1]
|
||||
.strip("\n")
|
||||
.split("\n")
|
||||
)
|
||||
rank_lines += ["```"]
|
||||
|
||||
in_rank = False
|
||||
table_appended = False
|
||||
rank_appended = False
|
||||
output = []
|
||||
for i, line in enumerate(readme_text):
|
||||
line = line.strip()
|
||||
|
||||
if line == table_start_flag:
|
||||
in_table = True
|
||||
output.append(table_start_flag)
|
||||
for ln in table_lines:
|
||||
output.append(ln)
|
||||
if line == table_end_flag:
|
||||
in_table = False
|
||||
|
||||
if line == rank_start_flag:
|
||||
in_rank = True
|
||||
output.append(rank_start_flag)
|
||||
for ln in rank_lines:
|
||||
output.append(ln)
|
||||
if line == rank_end_flag:
|
||||
in_rank = False
|
||||
|
||||
if in_rank or in_table:
|
||||
continue
|
||||
|
||||
output.append(line)
|
||||
|
||||
open(sys.argv[1], "w").write("\n".join(output))
|
41
.github/workflows/updategraph.yml
vendored
41
.github/workflows/updategraph.yml
vendored
|
@ -1,41 +0,0 @@
|
|||
name: Update CLOC graph
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
#schedule:
|
||||
# - cron: "58 4 * 12 *"
|
||||
|
||||
jobs:
|
||||
graph:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Login GitHub Registry
|
||||
run: docker login docker.pkg.github.com -u owner -p ${{ secrets.GH_TKN }}
|
||||
|
||||
- name: Run gocloc
|
||||
run: docker run --rm -v "${PWD}":/workdir docker.pkg.github.com/hhatto/gocloc/gocloc:latest --by-file --output-type=json --not-match-d="(template|.github|.vscode)" --exclude-ext=txt . > .github/cloc.json
|
||||
|
||||
- name: Install Matplotlib
|
||||
run: pip install matplotlib
|
||||
|
||||
- name: Run script
|
||||
run: python .github/clocgen.py .github/cloc.json .github/clocgraph.png
|
||||
|
||||
- name: Git commit and push
|
||||
run: |
|
||||
git config user.email 'actions@github.com'
|
||||
git config user.name 'github-actions'
|
||||
git add .github/clocgraph.png .github/cloc.json
|
||||
git commit -m 'Update CLOC graph'
|
||||
git push origin HEAD:${{ github.ref }}
|
34
.github/workflows/updatetable.yml
vendored
34
.github/workflows/updatetable.yml
vendored
|
@ -1,34 +0,0 @@
|
|||
name: Update README
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
#schedule:
|
||||
# - cron: "1 5 * 12 *"
|
||||
|
||||
jobs:
|
||||
table:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: pip install beautifulsoup4 requests
|
||||
|
||||
- name: Run script
|
||||
run: python .github/tablegen.py .github/README.md ${{ secrets.AOC_SESSION }}
|
||||
|
||||
- name: Prettify markdown
|
||||
uses: creyD/prettier_action@v3.1
|
||||
with:
|
||||
prettier_options: --write .github/README.md
|
||||
# dry: True
|
||||
commit_message: "Update README"
|
|
@ -1,31 +0,0 @@
|
|||
# [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,18 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
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,54 +0,0 @@
|
|||
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))
|
|
@ -1,5 +0,0 @@
|
|||
from typing import List
|
||||
|
||||
|
||||
def parse(instr: str) -> List:
|
||||
return [int(x) for x in instr.strip().split("\n")]
|
|
@ -1,12 +0,0 @@
|
|||
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
|
|
@ -1,13 +0,0 @@
|
|||
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
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"one": [
|
||||
{
|
||||
"input": "1721\n979\n366\n299\n675\n1456",
|
||||
"expected": 514579
|
||||
}
|
||||
],
|
||||
"two": [
|
||||
{
|
||||
"input": "1721\n979\n366\n299\n675\n1456",
|
||||
"expected": 241861950
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
# [Day 2: Password Philosophy](https://adventofcode.com/2020/day/2)
|
||||
|
||||
<details><summary>Script output</summary>
|
||||
|
||||
```
|
||||
❯ python .\python\
|
||||
AoC 2020: day 2 - Password Philosophy
|
||||
Python 3.8.5
|
||||
|
||||
Test cases
|
||||
1.1 pass
|
||||
2.1 pass
|
||||
|
||||
Answers
|
||||
Part 1: 500
|
||||
Part 2: 313
|
||||
|
||||
❯ go run .\go\
|
||||
AoC 2020: day 2 - Password Philosophy
|
||||
Go go1.15.2
|
||||
|
||||
Test cases
|
||||
1.1 pass
|
||||
2.1 pass
|
||||
|
||||
Answers
|
||||
Part 1: 500
|
||||
Part 2: 313
|
||||
```
|
||||
|
||||
</details>
|
|
@ -1,12 +0,0 @@
|
|||
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]): (.+)`)
|
|
@ -1,46 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
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,56 +0,0 @@
|
|||
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))
|
|
@ -1,5 +0,0 @@
|
|||
from typing import List
|
||||
|
||||
|
||||
def parse(instr: str) -> List:
|
||||
return instr.strip().split("\n")
|
|
@ -1,44 +0,0 @@
|
|||
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
|
|
@ -1,50 +0,0 @@
|
|||
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
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"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,27 +0,0 @@
|
|||
# [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,33 +0,0 @@
|
|||
package challenge
|
||||
|
||||
import "strings"
|
||||
|
||||
func parse(instr string) [][]rune {
|
||||
inputSlice := strings.Split(strings.TrimSpace(instr), "\n")
|
||||
|
||||
var forest [][]rune
|
||||
for _, line := range inputSlice {
|
||||
forest = append(forest, []rune(line))
|
||||
}
|
||||
return forest
|
||||
}
|
||||
|
||||
var tree_char = []rune("#")[0] // No idea why I can't just do rune("#")
|
||||
|
||||
func findCollisions(forest [][]rune, xOffset, yOffset int) int {
|
||||
var encounteredTrees int
|
||||
var xPointer int
|
||||
var yPointer int
|
||||
|
||||
for yPointer < len(forest) {
|
||||
row := forest[yPointer]
|
||||
targetIndex := xPointer % len(row)
|
||||
if row[targetIndex] == tree_char {
|
||||
encounteredTrees += 1
|
||||
}
|
||||
xPointer += xOffset
|
||||
yPointer += yOffset
|
||||
}
|
||||
|
||||
return encounteredTrees
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package challenge
|
||||
|
||||
func PartOne(instr string) int {
|
||||
return findCollisions(parse(instr), 3, 1)
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
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,56 +0,0 @@
|
|||
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))
|
|
@ -1,20 +0,0 @@
|
|||
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
|
|
@ -1,5 +0,0 @@
|
|||
from common import *
|
||||
|
||||
|
||||
def partOne(instr: str) -> int:
|
||||
return find_collisions(parse(instr), 3, 1)
|
|
@ -1,15 +0,0 @@
|
|||
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
|
|
@ -1,33 +0,0 @@
|
|||
# [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>
|
|
@ -1,11 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
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]
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
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()
|
||||
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
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))
|
|
@ -1,5 +0,0 @@
|
|||
from typing import List
|
||||
|
||||
|
||||
def parse(instr: str) -> List:
|
||||
return [x.replace("\n", " ") for x in instr.strip().split("\n\n")]
|
|
@ -1,37 +0,0 @@
|
|||
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
|
|
@ -1,103 +0,0 @@
|
|||
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
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
# [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>
|
|
@ -1,57 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
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()
|
||||
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"year": "2020",
|
||||
"day": "5",
|
||||
"title": "Binary Boarding",
|
||||
"testCases": {
|
||||
"one": [
|
||||
{
|
||||
"input": "BFFFBBFRRR",
|
||||
"expected": 567
|
||||
},
|
||||
{
|
||||
"input": "FFFBBBFRRR",
|
||||
"expected": 119
|
||||
},
|
||||
{
|
||||
"input": "BBFFBBFRLL",
|
||||
"expected": 820
|
||||
}
|
||||
],
|
||||
"two": []
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
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))
|
|
@ -1,47 +0,0 @@
|
|||
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
|
|
@ -1,14 +0,0 @@
|
|||
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
|
|
@ -1,41 +0,0 @@
|
|||
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
|
|
@ -1,31 +0,0 @@
|
|||
# [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>
|
|
@ -1,21 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
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()
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
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))
|
|
@ -1,5 +0,0 @@
|
|||
from typing import List
|
||||
|
||||
|
||||
def parse(instr: str) -> List[str]:
|
||||
return instr.strip().split("\n\n")
|
|
@ -1,28 +0,0 @@
|
|||
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
|
|
@ -1,44 +0,0 @@
|
|||
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
|
|
@ -1,33 +0,0 @@
|
|||
# [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>
|
|
@ -1,42 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
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()
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
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))
|
|
@ -1,34 +0,0 @@
|
|||
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
|
|
@ -1,28 +0,0 @@
|
|||
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
|
|
@ -1,29 +0,0 @@
|
|||
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)
|
|
@ -1,31 +0,0 @@
|
|||
# [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>
|
|
@ -1,30 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
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()
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
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))
|
|
@ -1,14 +0,0 @@
|
|||
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")]
|
|
@ -1,28 +0,0 @@
|
|||
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
|
|
@ -1,63 +0,0 @@
|
|||
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
|
|
@ -1,31 +0,0 @@
|
|||
# [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>
|
|
@ -1,17 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
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()
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
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))
|
|
@ -1,5 +0,0 @@
|
|||
from typing import List
|
||||
|
||||
|
||||
def parse(instr: str) -> List[int]:
|
||||
return [int(x) for x in instr.strip().split("\n")]
|
|
@ -1,41 +0,0 @@
|
|||
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
|
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