refactor: massive refactor to have only one binary to call

This commit is contained in:
2025-11-26 14:03:32 +01:00
parent 314da54495
commit 3723f84d1a
41 changed files with 272 additions and 188 deletions

View File

@@ -0,0 +1,79 @@
package dayfive
import (
"advent-of-code/internal/registry"
"os"
"strings"
)
func init() {
registry.Register("2020D5", ParseInput, PartOne, PartTwo)
}
func calculateSeatID(pass string) int {
if len(pass) < 10 {
return -1
}
rowStr := pass[:7]
columnStr := pass[7:10]
row := 0
for idx, char := range rowStr {
if char == 'B' {
row |= 1 << (6 - idx)
}
}
column := 0
for idx, char := range columnStr {
if char == 'R' {
column |= 1 << (2 - idx)
}
}
return row*8 + column
}
func ParseInput(filepath string) []string {
content, _ := os.ReadFile(filepath)
return strings.Split(string(content), "\n")
}
func PartOne(input []string) int {
maxSeatID := 0
for _, pass := range input {
seatID := calculateSeatID(pass)
if seatID > maxSeatID {
maxSeatID = seatID
}
}
return maxSeatID
}
func PartTwo(input []string) int {
seatIDs := make(map[int]bool)
minSeatID := 1000
maxSeatID := 0
for _, pass := range input {
if len(pass) < 10 {
continue
}
seatID := calculateSeatID(pass)
seatIDs[seatID] = true
if seatID < minSeatID {
minSeatID = seatID
}
if seatID > maxSeatID {
maxSeatID = seatID
}
}
for seatID := minSeatID + 1; seatID < maxSeatID; seatID++ {
if !seatIDs[seatID] && seatIDs[seatID-1] && seatIDs[seatID+1] {
return seatID
}
}
return 0
}

View File

@@ -0,0 +1,35 @@
package dayfive
import "testing"
func TestPartOne(t *testing.T) {
input := []string{
"FBFBBFFRLR",
"BFFFBBFRRR",
"FFFBBBFRRR",
"BBFFBBFRLL",
}
expected := 820
got := PartOne(input)
if got != expected {
t.Errorf("PartOne() = %d, want %d", got, expected)
}
}
func TestPartTwo(t *testing.T) {
input := []string{
"FFFFFFFLLL",
"FFFFFFFLLR",
"FFFFFFFLRL",
"FFFFFFFLRR",
"FFFFFFFRLR",
"FFFFFFFRRL",
"FFFFFFFRRR",
}
expected := 4
got := PartTwo(input)
if got != expected {
t.Errorf("PartTwo() = %d, want %d", got, expected)
}
}

View File

@@ -0,0 +1,154 @@
package dayfour
import (
"advent-of-code/internal/registry"
"os"
"strconv"
"strings"
)
func init() {
registry.Register("2020D4", ParseInput, PartOne, PartTwo)
}
func ParseInput(filepath string) []string {
content, _ := os.ReadFile(filepath)
text := string(content)
passports := strings.Split(text, "\n\n")
result := make([]string, 0, len(passports))
for _, passport := range passports {
passport = strings.TrimSpace(passport)
if passport == "" {
continue
}
passport = strings.ReplaceAll(passport, "\n", " ")
passport = strings.Join(strings.Fields(passport), " ")
result = append(result, passport)
}
return result
}
func PartOne(data []string) int {
count := 0
required := []string{"byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"}
for _, passport := range data {
fields := strings.Fields(passport)
present := make(map[string]bool)
for _, field := range fields {
if idx := strings.Index(field, ":"); idx != -1 {
present[field[:idx]] = true
}
}
missing := false
for _, field := range required {
if !present[field] {
missing = true
break
}
}
if !missing {
count++
}
}
return count
}
func PartTwo(data []string) int {
isNumberInRange := func(value string, min, max int, digits int) bool {
if len(value) != digits {
return false
}
n, err := strconv.Atoi(value)
if err != nil {
return false
}
return n >= min && n <= max
}
isValidHeight := func(value string) bool {
if len(value) < 3 {
return false
}
unit := value[len(value)-2:]
number := value[:len(value)-2]
n, err := strconv.Atoi(number)
if err != nil {
return false
}
if unit == "cm" {
return n >= 150 && n <= 193
}
if unit == "in" {
return n >= 59 && n <= 76
}
return false
}
isValidHairColor := func(value string) bool {
if len(value) != 7 || value[0] != '#' {
return false
}
for _, c := range value[1:] {
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
return false
}
}
return true
}
isValidPID := func(value string) bool {
if len(value) != 9 {
return false
}
for _, c := range value {
if c < '0' || c > '9' {
return false
}
}
return true
}
validECL := map[string]bool{
"amb": true, "blu": true, "brn": true, "gry": true,
"grn": true, "hzl": true, "oth": true,
}
required := []string{"byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"}
count := 0
for _, passport := range data {
fields := map[string]string{}
for field := range strings.FieldsSeq(passport) {
parts := strings.SplitN(field, ":", 2)
if len(parts) == 2 {
fields[parts[0]] = parts[1]
}
}
passportValidators := map[string]func(string) bool{
"byr": func(v string) bool { return isNumberInRange(v, 1920, 2002, 4) },
"iyr": func(v string) bool { return isNumberInRange(v, 2010, 2020, 4) },
"eyr": func(v string) bool { return isNumberInRange(v, 2020, 2030, 4) },
"hgt": isValidHeight,
"hcl": isValidHairColor,
"ecl": func(v string) bool { return validECL[v] },
"pid": isValidPID,
}
valid := true
for _, key := range required {
value, ok := fields[key]
if !ok || !passportValidators[key](value) {
valid = false
break
}
}
if valid {
count++
}
}
return count
}

View File

@@ -0,0 +1,27 @@
package dayfour
import "testing"
var testInput = []string{
"ecl:gry pid:860033327 eyr:2020 hcl:#fffffd byr:1937 iyr:2017 cid:147 hgt:183cm",
"iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884 hcl:#cfa07d byr:1929",
"hcl:#ae17e1 iyr:2013 eyr:2024 ecl:brn pid:760753108 byr:1931 hgt:179cm",
"hcl:#cfa07d eyr:2025 pid:166559648 iyr:2011 ecl:brn hgt:59in",
}
func TestPartOne(t *testing.T) {
expected := 2
got := PartOne(testInput)
if got != expected {
t.Errorf("PartOne() = %d, want %d", got, expected)
}
}
func TestPartTwo(t *testing.T) {
expected := 2
got := PartTwo(testInput)
if got != expected {
t.Errorf("PartTwo() = %d, want %d", got, expected)
}
}

View File

@@ -0,0 +1,50 @@
package dayone
import (
"advent-of-code/internal/registry"
"os"
"strconv"
"strings"
)
func init() {
registry.Register("2020D1", ParseInput, PartOne, PartTwo)
}
func ParseInput(filepath string) []int {
content, _ := os.ReadFile(filepath)
lines := strings.Fields(string(content))
var data []int
for _, line := range lines {
num, _ := strconv.Atoi(line)
data = append(data, num)
}
return data
}
func PartOne(data []int) int {
seen := make(map[int]bool)
for _, number := range data {
complement := 2020 - number
if seen[complement] {
return number * complement
}
seen[number] = true
}
return 0
}
func PartTwo(data []int) int {
seen := make(map[int]bool)
for idx := range data {
for _, number := range data[idx+1:] {
complement := 2020 - data[idx] - number
if seen[complement] {
return complement * data[idx] * number
}
}
seen[data[idx]] = true
}
return 0
}

View File

@@ -0,0 +1,22 @@
package dayone
import "testing"
var testInput = []int{1721, 979, 366, 299, 675, 1456}
func TestPartOne(t *testing.T) {
expected := 514579
got := PartOne(testInput)
if got != expected {
t.Errorf("PartOne() = %d, want %d", got, expected)
}
}
func TestPartTwo(t *testing.T) {
expected := 241861950
got := PartTwo(testInput)
if got != expected {
t.Errorf("PartTwo() = %d, want %d", got, expected)
}
}

View File

@@ -0,0 +1,70 @@
package daysix
import (
"advent-of-code/internal/registry"
"os"
"strings"
)
func init() {
registry.Register("2020D6", ParseInput, PartOne, PartTwo)
}
func ParseInput(filepath string) []string {
content, _ := os.ReadFile(filepath)
return strings.Split(strings.TrimSpace(string(content)), "\n")
}
func PartOne(input []string) int {
total := 0
groupAnswers := make(map[rune]bool)
for idx, line := range input {
if line != "" {
for _, char := range line {
if char >= 'a' && char <= 'z' {
groupAnswers[char] = true
}
}
}
if line == "" || idx == len(input)-1 {
total += len(groupAnswers)
groupAnswers = make(map[rune]bool)
}
}
total += len(groupAnswers)
return total
}
func PartTwo(input []string) int {
total := 0
groupAnswers := make(map[rune]int)
groupSize := 0
for idx, line := range input {
if line != "" {
groupSize++
for _, char := range line {
if char >= 'a' && char <= 'z' {
groupAnswers[char]++
}
}
}
if line == "" || idx == len(input)-1 {
for _, count := range groupAnswers {
if count == groupSize {
total++
}
}
groupAnswers = make(map[rune]int)
groupSize = 0
}
}
return total
}

View File

@@ -0,0 +1,38 @@
package daysix
import "testing"
var testInput = []string{
"abc",
"",
"a",
"b",
"c",
"",
"ab",
"ac",
"",
"a",
"a",
"a",
"a",
"",
"b",
}
func TestPartOne(t *testing.T) {
expected := 11
got := PartOne(testInput)
if got != expected {
t.Errorf("PartOne() = %d, want %d", got, expected)
}
}
func TestPartTwo(t *testing.T) {
expected := 6
got := PartTwo(testInput)
if got != expected {
t.Errorf("PartTwo() = %d, want %d", got, expected)
}
}

View File

@@ -0,0 +1,52 @@
package daythree
import (
"advent-of-code/internal/registry"
"os"
"strings"
)
func init() {
registry.Register("2020D3", ParseInput, PartOne, PartTwo)
}
func ParseInput(filepath string) []string {
content, _ := os.ReadFile(filepath)
return strings.Split(string(content), "\n")
}
func PartOne(input []string) int {
trees := 0
column := 0
for row := range input {
if len(input[row]) == 0 {
continue
}
if input[row][column%len(input[row])] == '#' {
trees++
}
column += 3
}
return trees
}
func PartTwo(input []string) int {
result := 1
slopes := [][]int{{1, 1}, {3, 1}, {5, 1}, {7, 1}, {1, 2}}
for _, slope := range slopes {
trees := 0
column := 0
for row := 0; row < len(input); row += slope[1] {
if len(input[row]) == 0 {
continue
}
if input[row][column%len(input[row])] == '#' {
trees++
}
column += slope[0]
}
result *= trees
}
return result
}

View File

@@ -0,0 +1,34 @@
package daythree
import "testing"
var testInput = []string{
"..##.......",
"#...#...#..",
".#....#..#.",
"..#.#...#.#",
".#...##..#.",
"..#.##.....",
".#.#.#....#",
".#........#",
"#.##...#...",
"#...##....#",
".#..#...#.#",
}
func TestPartOne(t *testing.T) {
expected := 7
got := PartOne(testInput)
if got != expected {
t.Errorf("PartOne() = %d, want %d", got, expected)
}
}
func TestPartTwo(t *testing.T) {
expected := 336
got := PartTwo(testInput)
if got != expected {
t.Errorf("PartTwo() = %d, want %d", got, expected)
}
}

View File

@@ -0,0 +1,77 @@
package daytwo
import (
"advent-of-code/internal/registry"
"os"
"strconv"
"strings"
)
func init() {
registry.Register("2020D2", ParseInput, PartOne, PartTwo)
}
func ParseInput(filepath string) []string {
content, _ := os.ReadFile(filepath)
lines := strings.Split(string(content), "\n")
var data []string
for _, line := range lines {
data = append(data, line)
}
return data
}
func PartOne(data []string) int {
valid := 0
for _, line := range data {
if line == "" {
continue
}
parts := strings.Split(line, ": ")
policy := parts[0]
password := parts[1]
policyParts := strings.Split(policy, " ")
rangeStr := policyParts[0]
letter := policyParts[1]
rangeParts := strings.Split(rangeStr, "-")
min, _ := strconv.Atoi(rangeParts[0])
max, _ := strconv.Atoi(rangeParts[1])
count := strings.Count(password, letter)
if count >= min && count <= max {
valid++
}
}
return valid
}
func PartTwo(data []string) int {
valid := 0
for _, line := range data {
if line == "" {
continue
}
parts := strings.Split(line, ": ")
policy := parts[0]
password := parts[1]
policyParts := strings.Split(policy, " ")
rangeStr := policyParts[0]
letter := policyParts[1]
rangeParts := strings.Split(rangeStr, "-")
firstPosition, _ := strconv.Atoi(rangeParts[0])
secondPosition, _ := strconv.Atoi(rangeParts[1])
atFirstPosition := len(password) >= firstPosition && password[firstPosition-1] == letter[0]
atSecondPosition := len(password) >= secondPosition && password[secondPosition-1] == letter[0]
if atFirstPosition != atSecondPosition {
valid++
}
}
return valid
}

View File

@@ -0,0 +1,26 @@
package daytwo
import "testing"
var testInput = []string{
"1-3 a: abcde",
"1-3 b: cdefg",
"2-9 c: ccccccccc",
}
func TestPartOne(t *testing.T) {
expected := 2
got := PartOne(testInput)
if got != expected {
t.Errorf("PartOne() = %d, want %d", got, expected)
}
}
func TestPartTwo(t *testing.T) {
expected := 1
got := PartTwo(testInput)
if got != expected {
t.Errorf("PartTwo() = %d, want %d", got, expected)
}
}