Intitial project scaffolding & day 1 solution

This commit is contained in:
Stanislav Nikolov 2022-12-01 18:27:32 +02:00
commit fccc444b53
9 changed files with 2491 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
aoc2022
.idea

60
day1/day1.go Normal file
View File

@ -0,0 +1,60 @@
package day1_calorie_counting
import (
"sort"
"strconv"
)
type Solver struct {
elves [][]int
}
func (s *Solver) ParseInput(input []string) {
elves := make([][]int, 0)
elfIndex := 0
elves = append(elves, make([]int, 0))
for _, line := range input {
if line == "" {
elfIndex += 1
elves = append(elves, make([]int, 0))
} else {
n, _ := strconv.Atoi(line)
elves[elfIndex] = append(elves[elfIndex], n)
}
}
s.elves = elves
}
func (s *Solver) SolvePart1() int {
mostCalories := 0
for _, calories := range s.elves {
sum := 0
for _, c := range calories {
sum += c
}
if sum > mostCalories {
mostCalories = sum
}
}
return mostCalories
}
func (s *Solver) SolvePart2() int {
caloriesPerElf := make([]int, len(s.elves))
for i, calories := range s.elves {
sum := 0
for _, c := range calories {
sum += c
}
caloriesPerElf[i] = sum
}
sort.Ints(caloriesPerElf)
top3sum := 0
for i := len(caloriesPerElf) - 3; i < len(caloriesPerElf); i++ {
top3sum += caloriesPerElf[i]
}
return top3sum
}

12
day1/day1_test.go Normal file
View File

@ -0,0 +1,12 @@
package day1_calorie_counting
import (
"snikolov.me/aoc2022/testutils"
"testing"
)
func TestCorrectSolutions(t *testing.T) {
solver := &Solver{}
testutils.TestHelper(t, solver, 1, true, 24000, 45000)
testutils.TestHelper(t, solver, 1, false, 66306, 195292)
}

2244
day1/prod.in.txt Normal file

File diff suppressed because it is too large Load Diff

14
day1/test.in.txt Normal file
View File

@ -0,0 +1,14 @@
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module snikolov.me/aoc2022
go 1.19

69
main.go Normal file
View File

@ -0,0 +1,69 @@
package main
import (
"flag"
"fmt"
"log"
day1_calorie_counting "snikolov.me/aoc2022/day1"
"snikolov.me/aoc2022/utils"
"time"
)
type Args struct {
DayNumber int
PartNumber int
TestMode bool
DownloadInput bool
}
func main() {
var args Args
flag.IntVar(&args.DayNumber, "day", 0, "day number")
flag.IntVar(&args.PartNumber, "part", 0, "day part number")
flag.BoolVar(&args.TestMode, "test", false, "use test input")
flag.BoolVar(&args.DownloadInput, "download-input", false, "download production input")
flag.Parse()
inputFileName := utils.GetInputFileName(args.DayNumber, args.TestMode)
if args.DownloadInput {
const sessionCookie = "53616c7465645f5f63d2a07212d6266c5e8de3664d5684dac31a6031e5ddf4c1ec004e5475bf45a16c9f9919fae43b7344135650408c9aec716880afd94bbc0b"
if err := utils.DownloadInput(sessionCookie, args.DayNumber, inputFileName); err != nil {
log.Fatalf("Could not download puzzle input: %s", err)
}
} else {
solve(args, inputFileName)
}
}
func solve(args Args, inputFileName string) {
solvers := map[int]utils.Solver{
1: &day1_calorie_counting.Solver{},
}
solver, ok := solvers[args.DayNumber]
if !ok {
log.Fatalf("Could not find a solver for day %d", args.DayNumber)
}
startTime := time.Now()
if input, err := utils.ReadInput(inputFileName); err == nil {
solver.ParseInput(input)
} else {
log.Fatalf("Could not read input for day %d: %s", args.DayNumber, err)
}
var solution int
switch args.PartNumber {
case 1:
solution = solver.SolvePart1()
case 2:
solution = solver.SolvePart2()
default:
log.Fatal("unknown part number, please use 1 or 2")
return
}
fmt.Printf("%s: %d\n", time.Now().Sub(startTime), solution)
}

20
testutils/testutils.go Normal file
View File

@ -0,0 +1,20 @@
package testutils
import (
"snikolov.me/aoc2022/utils"
"testing"
)
func TestHelper(t *testing.T, solver utils.Solver, dayNumber int, testMode bool, solutionPart1, solutionPart2 int) {
testInput, err := utils.ReadInput("../" + utils.GetInputFileName(dayNumber, testMode))
if err != nil {
t.Fatal(err)
}
solver.ParseInput(testInput)
if result := solver.SolvePart1(); result != solutionPart1 {
t.Errorf("wrong output for day %d part 1, test = %v: %d != %d", dayNumber, testMode, result, solutionPart1)
}
if result := solver.SolvePart2(); result != solutionPart2 {
t.Errorf("wrong output for day %d part 2, test = %v: %d != %d", dayNumber, testMode, result, solutionPart1)
}
}

67
utils/utils.go Normal file
View File

@ -0,0 +1,67 @@
package utils
import (
"bufio"
"fmt"
"io"
"log"
"net/http"
"os"
)
type Solver interface {
ParseInput(input []string)
SolvePart1() int
SolvePart2() int
}
func ReadInput(inputFileName string) ([]string, error) {
var input = make([]string, 0)
reader, err := os.Open(inputFileName)
if err != nil {
return input, fmt.Errorf("failed to open input file: %w", err)
}
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
input = append(input, scanner.Text())
}
return input, nil
}
func GetInputFileName(dayNumber int, testMode bool) string {
var testString string
if testMode {
testString = "test"
} else {
testString = "prod"
}
return fmt.Sprintf("day%d/%s.in.txt", dayNumber, testString)
}
func DownloadInput(sessionCookie string, dayNumber int, inputFileName string) error {
req, err := http.NewRequest("GET", fmt.Sprintf("https://adventofcode.com/2022/day/%d/input", dayNumber), nil)
if err != nil {
log.Fatalf("couldn't create reques: %s", err)
}
req.AddCookie(&http.Cookie{Name: "session", Value: sessionCookie})
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
writer, err := os.Create(inputFileName)
if err != nil {
return err
}
if _, err := io.Copy(writer, resp.Body); err != nil {
return err
}
return nil
}