generated from devries/aoc_template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 6ae7847
Showing
20 changed files
with
1,116 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
name: Test | ||
|
||
on: | ||
push: | ||
pull_request: | ||
branches: [ main ] | ||
workflow_dispatch: | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-go@v4 | ||
with: | ||
go-version: '^1.21' | ||
|
||
- name: Test utilities | ||
run: go test -cover ./utils | ||
|
||
- name: Test solutions | ||
if: ${{ github.repository != 'devries/aoc_template' }} | ||
run: go test -cover ./day* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
*~ | ||
.*.swp | ||
.DS_Store | ||
*.json | ||
run.go | ||
aoc_run |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2021 Christopher De Vries | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
GOFILES := $(filter-out run.go, $(wildcard **/*.go)) | ||
TEMPLATES := $(wildcard templates/*.tmpl) | ||
GO ?= go | ||
.PHONY: run runall test build clean start help | ||
|
||
run.go: $(GOFILES) $(TEMPLATES) | ||
$(GO) generate | ||
|
||
run: main.go run.go ## run the most recently edited day | ||
$(GO) run . | ||
|
||
runall: main.go run.go ## Run all days | ||
$(GO) run . -a | ||
|
||
test: ## Run all tests | ||
$(GO) test -cover ./utils | ||
$(GO) test -cover ./day* | ||
|
||
aoc_run: main.go run.go | ||
$(GO) build -o aoc_run . | ||
|
||
build: aoc_run ## Build binary executable aoc_run | ||
|
||
clean: ## Clean run.go and aoc_run | ||
- rm run.go | ||
- rm aoc_run | ||
|
||
help: ## Show this help | ||
@echo "These are the make commands for the solutions to this Advent of Code repository.\n" | ||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' | ||
|
||
day%p1: | ||
$(GO) run ./start -d $(shell echo $* | sed 's/^0*//') | ||
|
||
day%p2: day%p1 | ||
mkdir $@ | ||
- sed -E 's/^package day(.*)p1$$/package day\1p2/' day$(*)p1/solution.go > day$(*)p2/solution.go | ||
- sed -E 's/^package day(.*)p1$$/package day\1p2/' day$(*)p1/solution_test.go > day$(*)p2/solution_test.go | ||
|
||
start: day$(shell date +%d)p1 ## Start today |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# Advent of Code Template in Go | ||
|
||
This is a template to put advent of code problems into a single executable. It | ||
allows you to measure the execution time of each part of each problem, and to | ||
ignore some of the boilerplate. Not sure if anyone else will be interested, but | ||
I thought I would give it a try because I have seen a lot of templates like | ||
this. | ||
|
||
# Instructions | ||
|
||
To start a new day, download your input from the advent of code website and put | ||
it in a file named `dayXX.txt` where `XX` is replaced by the two digit day of | ||
the month within a subdirectory called `inputs`. Then, to generate code from a | ||
template using the command: | ||
|
||
``` | ||
make start | ||
``` | ||
|
||
It will default to adding code for the current day. This will create a | ||
directory called `dayXXp1` where `XX` is replaced by the day number. Inside | ||
will be a file called `solution.go` with a `Solve` function in which to put | ||
your solution, and a `solution_test.go` file to write your tests. The `Solve` | ||
function takes an `io.Reader` argument which is the input and returns a | ||
solution which can be any type. | ||
|
||
If you wish to start a problem for a specific day, say the 21st, you can create | ||
the desired directory from the template by using the make command to create the | ||
part 1 directory for that day using the command below. | ||
|
||
``` | ||
make day21p1 | ||
``` | ||
|
||
To run the last code you worked on use the command: | ||
|
||
``` | ||
make run | ||
``` | ||
|
||
This will generate a `run.go` file and run the most recently modified code. Once | ||
the first part is finished you can start the second part by using the command: | ||
|
||
``` | ||
make day21p2 | ||
``` | ||
|
||
This will copy your current part 1 code from `day21p1` and update the package | ||
names. You can then edit that code to complete part 2. | ||
|
||
You can run all the days with the command: | ||
|
||
``` | ||
make runall | ||
``` | ||
|
||
You can build a binary called `aoc_run` by using the | ||
|
||
``` | ||
make build | ||
``` | ||
|
||
command. | ||
|
||
Finally, you can run your tests with the command: | ||
|
||
``` | ||
make test | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"sort" | ||
"strings" | ||
"text/template" | ||
"time" | ||
|
||
"aoc/utils" | ||
) | ||
|
||
type DayDirectory struct { | ||
Name string | ||
Day int | ||
Part string | ||
} | ||
|
||
func newDayDirectory(name string) DayDirectory { | ||
res := DayDirectory{Name: name} | ||
|
||
fmt.Sscanf(name, "day%dp%s", &(res.Day), &(res.Part)) | ||
|
||
return res | ||
} | ||
|
||
type TemplateData struct { | ||
DayDirectories []DayDirectory | ||
CurrentDirectory DayDirectory | ||
} | ||
|
||
type ByName []DayDirectory | ||
|
||
func (a ByName) Len() int { return len(a) } | ||
func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } | ||
func (a ByName) Less(i, j int) bool { | ||
return a[i].Name < a[j].Name | ||
} | ||
|
||
func main() { | ||
files, err := os.ReadDir(".") | ||
utils.Check(err, "unable to read directory") | ||
|
||
td := TemplateData{} | ||
|
||
latest := time.Time{} | ||
|
||
for _, f := range files { | ||
if strings.HasPrefix(f.Name(), "day") && f.IsDir() { | ||
found := false | ||
sub, err := os.ReadDir(f.Name()) | ||
utils.Check(err, "unable to read subdirectory %s", f.Name()) | ||
|
||
for _, sf := range sub { | ||
switch sf.Name() { | ||
case "solution.go": | ||
t, err := getModTime(sf) | ||
utils.Check(err, "unable to get mod time for %s/%s", f.Name(), sf.Name()) | ||
if t.After(latest) { | ||
latest = t | ||
td.CurrentDirectory = newDayDirectory(f.Name()) | ||
} | ||
found = true | ||
} | ||
} | ||
|
||
if found { | ||
td.DayDirectories = append(td.DayDirectories, newDayDirectory(f.Name())) | ||
} | ||
} | ||
} | ||
|
||
sort.Sort(ByName(td.DayDirectories)) | ||
|
||
tpls := template.Must(template.ParseFS(os.DirFS("templates"), "*.tmpl")) | ||
|
||
fout, err := os.Create("run.go") | ||
utils.Check(err, "unable to create run.go file") | ||
defer fout.Close() | ||
|
||
err = tpls.ExecuteTemplate(fout, "run.tmpl", td) | ||
utils.Check(err, "unable to execute template") | ||
} | ||
|
||
func getModTime(de os.DirEntry) (time.Time, error) { | ||
fi, err := de.Info() | ||
if err != nil { | ||
return time.Time{}, fmt.Errorf("unable to get file information: %w", err) | ||
} | ||
|
||
return fi.ModTime(), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module aoc | ||
|
||
go 1.21 | ||
|
||
require github.com/spf13/pflag v1.0.5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package main | ||
|
||
//go:generate go run ./gen | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"os" | ||
"strconv" | ||
"time" | ||
|
||
"aoc/utils" | ||
"github.com/spf13/pflag" | ||
) | ||
|
||
var RunAll bool | ||
var DaySelected int | ||
var PartSelected string | ||
|
||
func init() { | ||
pflag.BoolVarP(&RunAll, "all", "a", false, "run all days") | ||
pflag.IntVarP(&DaySelected, "day", "d", 0, "run specific day") | ||
pflag.StringVarP(&PartSelected, "part", "p", "2", "run specific part (default 2)") | ||
} | ||
|
||
func main() { | ||
pflag.Parse() | ||
if RunAll { | ||
runAll() | ||
return | ||
} | ||
|
||
switch DaySelected { | ||
case 0: | ||
runCurrent() | ||
default: | ||
runDay(DaySelected, PartSelected) | ||
} | ||
} | ||
|
||
type aocFunc func(io.Reader) any | ||
|
||
type aocResult struct { | ||
Result string | ||
TimeElapsed time.Duration | ||
} | ||
|
||
type aocRunnerInput struct { | ||
Name string | ||
Func aocFunc | ||
Filename string | ||
Day int | ||
Part string | ||
} | ||
|
||
func runAocPart(partFunc aocFunc, filename string) aocResult { | ||
f, err := os.Open(filename) | ||
utils.Check(err, "unable to open file %s", filename) | ||
defer f.Close() | ||
|
||
start := time.Now() | ||
r := partFunc(f) | ||
duration := time.Since(start) | ||
|
||
res := aocResult{TimeElapsed: duration} | ||
|
||
switch v := r.(type) { | ||
case int: | ||
res.Result = strconv.Itoa(v) | ||
case int64: | ||
res.Result = strconv.FormatInt(v, 10) | ||
case uint64: | ||
res.Result = strconv.FormatUint(v, 10) | ||
case string: | ||
res.Result = v | ||
case fmt.Stringer: | ||
res.Result = v.String() | ||
default: | ||
res.Result = "unknown return value" | ||
} | ||
|
||
return res | ||
} | ||
|
||
func runAll() { | ||
var r aocResult | ||
var total time.Duration | ||
|
||
for _, v := range days { | ||
r = runAocPart(v.Func, v.Filename) | ||
total += r.TimeElapsed | ||
|
||
fmt.Printf("%s: %s time elapsed: %s\n", v.Name, r.Result, r.TimeElapsed) | ||
} | ||
|
||
fmt.Printf("Overall time elapsed: %s\n", total) | ||
} | ||
|
||
func runDay(day int, part string) { | ||
found := false | ||
|
||
for _, v := range days { | ||
if v.Day == day && v.Part == part { | ||
fmt.Printf("Day %d part %s\n", day, part) | ||
r := runAocPart(v.Func, v.Filename) | ||
fmt.Println(r.Result) | ||
fmt.Printf("Time elapsed: %s\n", r.TimeElapsed) | ||
found = true | ||
break | ||
} | ||
} | ||
|
||
if !found { | ||
fmt.Printf("Did not find a solution for day %d part %s\n", day, part) | ||
} | ||
} |
Oops, something went wrong.