2024 day1

This commit is contained in:
2024-12-01 08:02:28 +00:00
parent 2584df5496
commit 0034691f2a
12 changed files with 1666 additions and 0 deletions

16
2024/go/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"args": ["16"],
"program": "${fileDirname}"
}
]
}

54
2024/go/day01/day01.go Normal file
View File

@@ -0,0 +1,54 @@
package day01
import (
"adventofcode2024/utils"
"sort"
"strings"
)
func Part1(input string) int {
var list1, list2 []int
num := 0
lines := strings.Split(input, "\n")
for _, line := range lines {
fields := strings.Fields(line)
// Convert the fields to integers
val1 := utils.MustAtoi(fields[0])
val2 := utils.MustAtoi(fields[1])
// Append to the respective slices
list1 = append(list1, val1)
list2 = append(list2, val2)
}
sort.Ints(list1)
sort.Ints(list2)
for i := 0; i < len(list1); i++ {
num += utils.Abs(list1[i] - list2[i])
}
return num
}
func Part2(input string) int {
var list1 []int
list2 := make(map[int]int)
num := 0
lines := strings.Split(input, "\n")
for _, line := range lines {
fields := strings.Fields(line)
// Convert the fields to integers
val1 := utils.MustAtoi(fields[0])
val2 := utils.MustAtoi(fields[1])
// Append to the respective slices
list1 = append(list1, val1)
list2[val2]++
}
for i := 0; i < len(list1); i++ {
num += list1[i] * list2[list1[i]]
}
return num
}

View File

@@ -0,0 +1,17 @@
package day01
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestPart1(t *testing.T) {
r := Part1("")
require.Equal(t, 0, r)
}
func TestPart2(t *testing.T) {
r := Part2("")
require.Equal(t, 0, r)
}

1000
2024/go/day01/input.txt Normal file

File diff suppressed because it is too large Load Diff

5
2024/go/go.mod Normal file
View File

@@ -0,0 +1,5 @@
module adventofcode2024
go 1.19
require golang.org/x/exp v0.0.0-20221208152030-732eee02a75a

2
2024/go/go.sum Normal file
View File

@@ -0,0 +1,2 @@
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=

84
2024/go/main.go Normal file
View File

@@ -0,0 +1,84 @@
package main
import (
"fmt"
// "math/rand"
"os"
// "strings"
// "time"
"adventofcode2024/utils"
"adventofcode2024/day01"
)
// Usage: go run main.go <NN>
// assumes input is in day<NN>/input.txt
func main() {
d := day()
fmt.Printf("Running day %02d\n", d)
switch d {
case 1:
fmt.Printf("part 1: %d\n", day01.Part1(utils.Readfile(d)))
fmt.Printf("part 2: %d\n", day01.Part2(utils.Readfile(d)))
default:
panic(fmt.Errorf("no such day: %d", d))
}
}
// Reads day from os.Args.
func day() int {
latest := 1
if len(os.Args) == 1 {
return latest
}
if os.Args[1] == "next" {
genNext(latest + 1)
os.Exit(0)
}
day := utils.MustAtoi(os.Args[1])
return day
}
func genNext(n int) {
os.Mkdir(fmt.Sprintf("day%02d", n), 0755)
f, err := os.Create(fmt.Sprintf("day%02d/day%02d.go", n, n))
utils.PanicOnErr(err)
defer f.Close()
f.WriteString(fmt.Sprintf(`package day%02d
func Part1(input string) int {
return 0
}
func Part2(input string) int {
return 0
}
`, n))
fmt.Printf("wrote day%02d/day%02d.go\n", n, n)
f, err = os.Create(fmt.Sprintf("day%02d/day%02d_test.go", n, n))
utils.PanicOnErr(err)
defer f.Close()
f.WriteString(fmt.Sprintf(`package day%02d
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestPart1(t *testing.T) {
r := Part1("")
require.Equal(t, 0, r)
}
func TestPart2(t *testing.T) {
r := Part2("")
require.Equal(t, 0, r)
}
`, n))
fmt.Printf("wrote day%02d/day%02d_test.go\n", n, n)
utils.GenInputFile(n)
}

View File

@@ -0,0 +1,77 @@
package grid2d
import (
"strings"
"adventofcode2024/utils"
)
type Grid[T any] struct {
sizeX, sizeY int
matrix [][]T
empty T
}
func NewGrid[T any](sizeX, sizeY int, empty T) *Grid[T] {
matrix := make([][]T, sizeY)
rows := make([]T, sizeX*sizeY)
for i := 0; i < sizeX*sizeY; i++ {
rows[i] = empty
}
j := 0
for i := 0; i < sizeY; i++ {
matrix[i] = rows[j : j+sizeX : j+sizeX]
j += sizeX
}
return &Grid[T]{
sizeX: sizeX,
sizeY: sizeY,
matrix: matrix,
empty: empty,
}
}
func (g *Grid[T]) SizeX() int {
return g.sizeX
}
func (g *Grid[T]) SizeY() int {
return g.sizeY
}
func (g *Grid[T]) Matrix() [][]T {
return g.matrix
}
func (g *Grid[T]) Get(x, y int) T {
if x < 0 || x >= g.sizeX {
return g.empty
}
if y < 0 || y >= g.sizeY {
return g.empty
}
return g.matrix[y][x]
}
func (g *Grid[T]) Set(x, y int, v T) {
if x < 0 || x >= g.sizeX {
panic("invalid x")
}
if y < 0 || y >= g.sizeY {
panic("invalid y")
}
g.matrix[y][x] = v
}
func (g *Grid[T]) StringWithFormatter(formatter func(T, int, int) string) string {
var r strings.Builder
for j := 0; j < g.sizeY; j++ {
for i := 0; i < g.sizeX; i++ {
_, err := r.WriteString(formatter(g.matrix[j][i], i, j))
utils.PanicOnErr(err)
}
_, err := r.WriteRune('\n')
utils.PanicOnErr(err)
}
return r.String()
}

View File

@@ -0,0 +1,63 @@
package utils
import (
"fmt"
"io"
"net/http"
"os"
)
func GenInputFile(day int) string {
var d string
if day < 10 {
d = fmt.Sprintf("0%d", day)
} else {
d = fmt.Sprintf("%d", day)
}
pwd, _ := os.Getwd()
path := fmt.Sprintf("%s/day%s/input.txt", pwd, d)
fi, _ := os.Stat(path)
if fi != nil {
return path
}
fmt.Printf("Creating new input file %v...", path)
f, err := os.Create(path)
if err != nil {
fmt.Println(err)
} else {
defer f.Close()
data := readHttp(2024, day)
_, err := f.WriteString(data)
if err != nil {
fmt.Println(err)
}
}
return path
}
func readHttp(year, day int) string {
fmt.Println("Fetching data into file...")
url := fmt.Sprintf("https://adventofcode.com/%d/day/%d/input", year, day)
session := os.Getenv("sessionAoC")
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err)
}
req.AddCookie(&http.Cookie{Name: "session", Value: session})
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
return string(body)
}

View File

@@ -0,0 +1,45 @@
package inputs
import (
"strings"
"adventofcode2024/utils"
"adventofcode2024/utils/grid2d"
sparsegrid "adventofcode2024/utils/sparseGrid"
)
func ToInts(input string, sep string) []int {
var r []int
for _, line := range strings.Split(input, sep) {
if line != "" {
r = append(r, utils.MustAtoi(line))
}
}
return r
}
func ToGrid2D[T any](input, rowSep, colSep string, empty T, conv func(string) T) *grid2d.Grid[T] {
lines := strings.Split(input, rowSep)
grid := grid2d.NewGrid(len(lines[0]), len(lines), empty)
for y, line := range lines {
for x, v := range strings.Split(line, colSep) {
grid.Set(x, y, conv(v))
}
}
return grid
}
func ToSparseGrid[T comparable](input, rowSep, colSep string, empty T, conv func(string) T) *sparsegrid.SparseGrid[T] {
lines := strings.Split(input, rowSep)
grid := sparsegrid.NewGrid(empty)
for y, line := range lines {
for x, v := range strings.Split(line, colSep) {
grid.Set(x, y, conv(v))
}
}
return grid
}

View File

@@ -0,0 +1,81 @@
package sparsegrid
import (
"fmt"
"strings"
"adventofcode2024/utils"
)
type SparseGrid[T comparable] struct {
minX, maxX, minY, maxY int
data map[string]T
empty T
}
func NewGrid[T comparable](empty T) *SparseGrid[T] {
return &SparseGrid[T]{
minX: utils.MaxInt,
maxX: utils.MinInt,
minY: utils.MaxInt,
maxY: utils.MinInt,
data: map[string]T{},
empty: empty,
}
}
func (g *SparseGrid[T]) SizeX() (int, int) {
return g.minX, g.maxX
}
func (g *SparseGrid[T]) SizeY() (int, int) {
return g.minY, g.maxY
}
func (g *SparseGrid[T]) Visited() int {
return len(g.data)
}
func (g *SparseGrid[T]) Get(x, y int) T {
k := key(x, y)
v, ok := g.data[k]
if !ok {
return g.empty
}
return v
}
func (g *SparseGrid[T]) Set(x, y int, v T) {
k := key(x, y)
current, ok := g.data[k]
if ok && v == current {
return
} else if !ok && v == g.empty {
return
} else if v == g.empty {
delete(g.data, k)
} else {
g.data[k] = v
g.minX = utils.Min(g.minX, x)
g.maxX = utils.Max(g.maxX, x)
g.minY = utils.Min(g.minY, y)
g.maxY = utils.Max(g.maxY, y)
}
}
func (g *SparseGrid[T]) StringWithFormatter(formatter func(T, int, int) string) string {
var r strings.Builder
for j := g.minY; j <= g.maxY; j++ {
for i := g.minX; i <= g.maxX; i++ {
_, err := r.WriteString(formatter(g.Get(i, j), i, j))
utils.PanicOnErr(err)
}
_, err := r.WriteRune('\n')
utils.PanicOnErr(err)
}
return r.String()
}
func key(x, y int) string {
return fmt.Sprintf("%d:%d", x, y)
}

222
2024/go/utils/utils.go Normal file
View File

@@ -0,0 +1,222 @@
package utils
import (
"bufio"
"fmt"
"io"
"os"
"reflect"
"regexp"
"strconv"
"strings"
"golang.org/x/exp/constraints"
)
func PanicOnErr(err error) {
if err != nil {
panic(err)
}
}
const MaxInt = int(^uint(0) >> 1)
const MinInt = ^MaxInt
func Max[T constraints.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
func Min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
func SliceMinMax[T constraints.Ordered](slice []T) (*T, *T) {
if len(slice) == 0 {
return nil, nil
}
min := &slice[0]
max := &slice[0]
for i, v := range slice {
if v < *min {
min = &slice[i]
}
if v > *max {
max = &slice[i]
}
}
return min, max
}
func MustAtoi(s string) int {
v, err := strconv.Atoi(s)
PanicOnErr(err)
return v
}
// Returns key from map[T]int which has the max value
func MapFindMax(m interface{}) interface{} {
var maxK interface{} = nil
var maxV = MinInt
iter := reflect.ValueOf(m).MapRange()
for iter.Next() {
k := iter.Key()
v := int(iter.Value().Int())
if v > maxV {
maxV = v
maxK = k.Interface()
}
}
return maxK
}
// Returns key from map[T]int which has the min value
func MapFindMin(m interface{}) interface{} {
var minK interface{} = nil
var minV = MaxInt
iter := reflect.ValueOf(m).MapRange()
for iter.Next() {
k := iter.Key()
v := int(iter.Value().Int())
if v < minV {
minV = v
minK = k.Interface()
}
}
return minK
}
func Readfile(day int) string {
filename := fmt.Sprintf("day%02d/input.txt", day)
file, err := os.Open(filename)
PanicOnErr(err)
defer file.Close()
reader := bufio.NewReader(file)
contents, err := io.ReadAll(reader)
PanicOnErr(err)
return strings.TrimSuffix(string(contents), "\n")
}
func ReadfileAsSlice(day int) []string {
filename := fmt.Sprintf("day%02d/input.txt", day)
file, err := os.Open(filename)
PanicOnErr(err)
defer file.Close()
arr := make([]string, 0)
sc := bufio.NewScanner(file)
for sc.Scan() {
arr = append(arr, sc.Text())
}
return arr
}
func ParseToStruct(re *regexp.Regexp, input string, target interface{}) bool {
m := re.FindStringSubmatch(input)
if m == nil {
return false
}
var useOffset bool
for i, name := range re.SubexpNames() {
if i == 0 {
continue
}
var field reflect.Value
if name == "" {
// use offset
if i == 1 {
useOffset = true
} else if !useOffset {
panic("can't mix named and unnamed subexpressions")
}
field = reflect.ValueOf(target).Elem().Field(i - 1)
} else {
// use name
if i == 1 {
useOffset = false
} else if useOffset {
panic("can't mix named and unnamed subexpressions")
}
field = reflect.ValueOf(target).Elem().FieldByName(name)
}
if field.Kind() == reflect.String {
field.SetString(m[i])
} else if field.Kind() == reflect.Int {
v, err := strconv.Atoi(m[i])
PanicOnErr(err)
field.SetInt(int64(v))
} else if field.Kind() == reflect.Uint8 {
if len(m[i]) != 1 {
panic(fmt.Sprintf("expecting 1 char, got: %s", m[i]))
}
field.SetUint(uint64(m[i][0]))
} else {
panic(fmt.Sprintf("unknown kind: %s", field.Kind()))
}
}
return true
}
func MustParseToStruct(re *regexp.Regexp, input string, target interface{}) {
if !ParseToStruct(re, input, target) {
panic(fmt.Errorf("failed to parse: %s", input))
}
}
func CharToLower(c byte) byte {
return strings.ToLower(string(c))[0]
}
func CharToUpper(c byte) byte {
return strings.ToUpper(string(c))[0]
}
func Contains(haystack []string, needle string) bool {
for _, s := range haystack {
if s == needle {
return true
}
}
return false
}
func Abs[T constraints.Signed](x T) T {
if x < 0 {
return -x
}
return x
}
func Gcd(x, y int) int {
if x <= 0 || y <= 0 {
panic(fmt.Errorf("invalid input: %d, %d", x, y))
}
if x == y {
return x
}
if x > y {
return Gcd(x-y, y)
} else {
return Gcd(x, y-x)
}
}
func Sign[T constraints.Signed](x T) int {
if x < 0 {
return -1
} else if x > 0 {
return 1
} else {
return 0
}
}