update scripts

This commit is contained in:
alexchao26
2021-12-01 20:34:10 -05:00
parent 2888e20870
commit b8c031d229
21 changed files with 405 additions and 305 deletions
+3 -1
View File
@@ -1,3 +1,5 @@
.vscode/*
input*.txt
prompt.*
!scripts/skeleton/tmpls/input.txt
prompt.md
prompt.txt
-13
View File
@@ -1,13 +0,0 @@
# Advent of Code 2018
Language: GoLang.
[https://adventofcode.com/2018](https://adventofcode.com/2018)
---
## Summary
Day | Name | Type of Algo & Notes
--- | --- | ---
1 | Chronal Calibration | - Pretty simple, direct math problem
2 | Inventory Management System | - Pretty simple, brute force string comparison
3 | No Matter How You Slice It | - Still pretty simple, 2D grid overlaps, simple hashmap problem
4 | Repose Record | - Not very fun data manipulation, pretty straight forward "which time does X happen most often at"
+1 -1
View File
@@ -1,5 +1,5 @@
# Advent of Code 2019
Language: GoLang.
Language: Go
[https://adventofcode.com/2019](https://adventofcode.com/2019)
-17
View File
@@ -1,17 +0,0 @@
## 2020 Event
To run days for 2020, I'm planning on using `go test -run RegExpToMatchFunctionNames .` to make it cleaner to run any given examples.
`go run main.go -part <1 or 2>` will be usable to run the actual inputs for that day
Scripts have been added to the outer folder to setup files for a particular day more easily
```zsh
for ((i=1; i<26; i++)); do
go run ../scripts/template/template.go -day $i -year 2020
done
```
and to fetch input prompts
```go
// optional -day and -year flags with sensible defaults (today)
go run ../scripts/fetchers/cmd-inputs.go
```
+39
View File
@@ -0,0 +1,39 @@
# https://gist.github.com/prwhite/8168133
help: ## Show this help
@ echo 'Usage: make <target>'
@ echo
@ echo 'Available targets:'
@ grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
check-aoc-cookie: ## ensures $AOC_SESSION_COOKIE env var is set
@ test $${AOC_SESSION_COOKIE?env var not set}
skeleton: ## make skeleton main(_test).go files, optional: $DAY and $YEAR
# Check DAY and YEAR are both set
@ if [[ -n $$DAY && -n $$YEAR ]]; then \
go run scripts/cmd/skeleton/main.go -day $(DAY) -year $(YEAR); \
fi
# if DAY or YEAR are not set, allow the go binary to default to today
@ if [[ -z $$DAY || -z $$YEAR ]]; then \
go run scripts/cmd/skeleton/main.go; \
fi
input: check-aoc-cookie ## get input, requires $AOC_SESSION_COOKIE, optional: $DAY and $YEAR
# Check DAY and YEAR are both set
@ if [[ -n $$DAY && -n $$YEAR ]]; then \
go run scripts/cmd/input/main.go -day $(DAY) -year $(YEAR) -cookie $(AOC_SESSION_COOKIE); \
fi
# if DAY or YEAR are not yet, allow the go binary to default to today
@ if [[ -z $$DAY || -z $$YEAR ]]; then \
go run scripts/cmd/input/main.go -cookie $(AOC_SESSION_COOKIE); \
fi
prompt: check-aoc-cookie ## get prompt, requires $AOC_SESSION_COOKIE, optional: $DAY and $YEAR
# Check DAY and YEAR are both set
@ if [[ -n $$DAY && -n $$YEAR ]]; then \
go run scripts/cmd/prompt/main.go -day $(DAY) -year $(YEAR) -cookie $(AOC_SESSION_COOKIE); \
fi
# if DAY or YEAR are not yet, allow the go binary to default to today
@ if [[ -z $$DAY || -z $$YEAR ]]; then \
go run scripts/cmd/prompt/main.go -cookie $(AOC_SESSION_COOKIE); \
fi
+36 -1
View File
@@ -1 +1,36 @@
<img src="./300.png">
![300 stars!](./300.png)
## Running Locally
### Requirements
Go 1.16+ is required because [embed][embed] is used for input files.
Use `go run main.go -part <1 or 2>` will be usable to run the actual inputs for that day.
Use `go test -run RegExpToMatchFunctionNames .` to run examples and unit tests via the `main_test.go` files.
## Scripts (used for all years but 2019)
Makefile should be fairly self-documenting. Alternatively you can run the binaries yourself via `go run` or `go build`.
`make help` prints a help message.
### Make skeleton files
```sh
for ((i=1; i<26; i++)); do
make skeleton DAY=$i YEAR=2021
done
```
Note that skeletons use [embed][embed] and __will not compile__ without an `input.txt` file located in the same folder. Input files can be made via `make input`.
```sh
make skeleton DAY=5 YEAR=2020
make input DAY=5 YEAR=2020 AOC_SESSION_COOKIE=your_cookie
```
### Fetch inputs and write to input.txt files
Requires passing your cookie from AOC from either `-cookie` flag, or `AOC_SESSION_COOKIE` env variable.
```sh
make input DAY=1 YEAR=2020
```
[embed]: https://golang.org/pkg/embed/
+1 -1
View File
@@ -1,5 +1,5 @@
module github.com/alexchao26/advent-of-code-go
go 1.13
go 1.16
require golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
+82
View File
@@ -0,0 +1,82 @@
// Package aoc gets inputs and prompts from adventofcode.com
package aoc
import (
"context"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
func ParseFlags() (day, year int, cookie string) {
today := time.Now()
flag.IntVar(&day, "day", today.Day(), "day number to fetch, 1-25")
flag.IntVar(&year, "year", today.Year(), "AOC year")
// defaults to env variable
flag.StringVar(&cookie, "cookie", os.Getenv("AOC_SESSION_COOKIE"), "AOC session cookie")
flag.Parse()
if day > 25 || day < 1 {
log.Fatalf("day out of range: %d", day)
}
if year < 2015 {
log.Fatalf("year is before 2015: %d", year)
}
if cookie == "" {
log.Fatalf("no session cookie set on flag or env var (AOC_SESSION_COOKIE)")
}
return day, year, cookie
}
func GetWithAOCCookie(url string, cookie string) []byte {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
log.Fatalf("making request: %s", err)
}
sessionCookie := http.Cookie{
Name: "session",
Value: cookie,
}
req.AddCookie(&sessionCookie)
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatalf("making request: %s", err)
}
body, err := io.ReadAll(res.Body)
if err != nil {
log.Fatalf("reading response body: %s", err)
}
fmt.Println("response length is", len(body))
// specific error message from AOC site
if strings.HasPrefix(string(body), "Please don't repeatedly") {
log.Fatalf("Repeated request github.com/alexchao26/advent-of-code-go error")
}
return body
}
func WriteToFile(filename string, contents []byte) {
err := os.MkdirAll(filepath.Dir(filename), os.ModePerm)
if err != nil {
log.Fatalf("making directory: %s", err)
}
err = os.WriteFile(filename, contents, os.FileMode(0644))
if err != nil {
log.Fatalf("writing file: %s", err)
}
}
+29
View File
@@ -0,0 +1,29 @@
package aoc
import (
"fmt"
"path/filepath"
"strings"
"github.com/alexchao26/advent-of-code-go/util"
)
func GetInput(day, year int, cookie string) {
fmt.Printf("fetching for day %d, year %d\n", day, year)
// make the request
url := fmt.Sprintf("https://adventofcode.com/%d/day/%d/input", year, day)
body := GetWithAOCCookie(url, cookie)
if strings.HasPrefix(string(body), "Puzzle inputs differ by user") {
panic("'Puzzle inputs differ by user' response")
}
// write to file
filename := filepath.Join(util.Dirname(), "../..", fmt.Sprintf("%d/day%02d/input.txt", year, day))
WriteToFile(filename, body)
fmt.Println("Wrote to file: ", filename)
fmt.Println("Done!")
}
@@ -1,4 +1,4 @@
package main
package aoc
import (
"bytes"
@@ -8,25 +8,22 @@ import (
"golang.org/x/net/html"
"github.com/alexchao26/advent-of-code-go/scripts/fetchers"
"github.com/alexchao26/advent-of-code-go/util"
)
func main() {
// determine day to fetch
day, year, cookie := fetchers.ParseFlags()
fmt.Println("fetching for day", day)
func GetPrompt(day, year int, cookie string) {
fmt.Printf("fetching for day %d, year %d\n", day, year)
// make the request
url := fmt.Sprintf("https://adventofcode.com/%d/day/%d", year, day)
body := fetchers.GetWithAOCCookie(url, cookie)
body := GetWithAOCCookie(url, cookie)
// parse the dang html
prompt := parseHTML(body)
// write to file
filename := filepath.Join(util.Dirname(), "../../../", fmt.Sprintf("%d/day%02d/prompt.md", year, day))
fetchers.WriteToFile(filename, []byte(prompt))
filename := filepath.Join(util.Dirname(), "../../", fmt.Sprintf("%d/day%02d/prompt.md", year, day))
WriteToFile(filename, []byte(prompt))
fmt.Println("Wrote prompt to file: ", filename)
+8
View File
@@ -0,0 +1,8 @@
package main
import "github.com/alexchao26/advent-of-code-go/scripts/aoc"
func main() {
day, year, cookie := aoc.ParseFlags()
aoc.GetInput(day, year, cookie)
}
+8
View File
@@ -0,0 +1,8 @@
package main
import "github.com/alexchao26/advent-of-code-go/scripts/aoc"
func main() {
day, year, cookie := aoc.ParseFlags()
aoc.GetPrompt(day, year, cookie)
}
+16
View File
@@ -0,0 +1,16 @@
package main
import (
"flag"
"time"
"github.com/alexchao26/advent-of-code-go/scripts/skeleton"
)
func main() {
today := time.Now()
day := flag.Int("day", today.Day(), "day number to fetch, 1-25")
year := flag.Int("year", today.Year(), "AOC year")
flag.Parse()
skeleton.Run(*day, *year)
}
-27
View File
@@ -1,27 +0,0 @@
package main
import (
"fmt"
"path/filepath"
"github.com/alexchao26/advent-of-code-go/scripts/fetchers"
"github.com/alexchao26/advent-of-code-go/util"
)
func main() {
// determine day to fetch
day, year, cookie := fetchers.ParseFlags()
fmt.Println("fetching for day", day)
// make the request
url := fmt.Sprintf("https://adventofcode.com/%d/day/%d/input", year, day)
body := fetchers.GetWithAOCCookie(url, cookie)
// write to file
filename := filepath.Join(util.Dirname(), "../../..", fmt.Sprintf("%d/day%02d/input.txt", year, day))
fetchers.WriteToFile(filename, body)
fmt.Println("Wrote to file: ", filename)
fmt.Println("Done!")
}
-81
View File
@@ -1,81 +0,0 @@
package fetchers
import (
"flag"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
func panicWrap(err error, str string) {
panic(fmt.Sprintf("%s: %s", str, err))
}
func ParseFlags() (day, year int, cookie string) {
today := time.Now()
flag.IntVar(&day, "day", today.Day(), "day number to fetch, 1-25")
flag.IntVar(&year, "year", today.Year(), "AOC year")
// env variable set via .bash_profile/.zshenv/etc
flag.StringVar(&cookie, "cookie", os.Getenv("AOC_SESSION_COOKIE"), "AOC session cookie")
flag.Parse()
if day > 25 {
panic("day out of range")
}
if cookie == "" {
panic("No session cookie set on flag or env")
}
return day, year, cookie
}
func GetWithAOCCookie(url string, cookie string) []byte {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panicWrap(err, "making request")
}
sessionCookie := http.Cookie{
Name: "session",
Value: cookie,
}
req.AddCookie(&sessionCookie)
cli := http.Client{
Timeout: time.Second * 10,
}
res, err := cli.Do(req)
if err != nil {
panicWrap(err, "making request")
}
body, err := ioutil.ReadAll(res.Body)
fmt.Println("response length is", len(body))
if strings.HasPrefix(string(body), "Please don't repeatedly") {
panic("Repeated request github.com/alexchao26/advent-of-code-go error")
}
return body
}
func WriteToFile(filename string, contents []byte) {
MakeDir(filepath.Dir(filename))
err := ioutil.WriteFile(filename, contents, os.FileMode(0644))
if err != nil {
panicWrap(err, "writing file")
}
}
func MakeDir(dir string) {
err := os.MkdirAll(dir, os.ModePerm)
if err != nil {
panic(err)
}
}
+63
View File
@@ -0,0 +1,63 @@
// Package skeleton makes skeletons to be filled out with solutions.
package skeleton
import (
"embed"
"fmt"
"log"
"os"
"path/filepath"
"text/template"
"github.com/alexchao26/advent-of-code-go/util"
)
//go:embed tmpls/*.go
var fs embed.FS
// Run makes a skeleton main.go and main_test.go file for the given day and year
func Run(day, year int) {
if day > 25 || day <= 0 {
log.Fatalf("invalid -day value, must be 1 through 25, got %v", day)
}
if year < 2015 {
log.Fatalf("year is before 2015: %d", year)
}
ts, err := template.ParseFS(fs, "tmpls/*.go")
if err != nil {
log.Fatalf("parsing tmpls directory: %s", err)
}
mainFilename := filepath.Join(util.Dirname(), "../../", fmt.Sprintf("%d/day%02d/main.go", year, day))
testFilename := filepath.Join(util.Dirname(), "../../", fmt.Sprintf("%d/day%02d/main_test.go", year, day))
err = os.MkdirAll(filepath.Dir(mainFilename), os.ModePerm)
if err != nil {
log.Fatalf("making directory: %s", err)
}
ensureNotOverwriting(mainFilename)
ensureNotOverwriting(testFilename)
mainFile, err := os.Create(mainFilename)
if err != nil {
log.Fatalf("creating main.go file: %v", err)
}
testFile, err := os.Create(testFilename)
if err != nil {
log.Fatalf("creating main_test.go file: %v", err)
}
ts.ExecuteTemplate(mainFile, "main.go", nil)
ts.ExecuteTemplate(testFile, "main_test.go", nil)
fmt.Printf("templates made for %d-day%d\n", year, day)
}
func ensureNotOverwriting(filename string) {
_, err := os.Stat(filename)
if err == nil {
log.Fatalf("File already exists: %s", filename)
}
}
+5
View File
@@ -0,0 +1,5 @@
1
2
3
4
5
+57
View File
@@ -0,0 +1,57 @@
package main
import (
_ "embed"
"flag"
"fmt"
"strings"
"github.com/alexchao26/advent-of-code-go/cast"
"github.com/alexchao26/advent-of-code-go/util"
)
//go:embed input.txt
var input string
func init() {
// do this in init (not main) so test file has same input
input = strings.TrimRight(input, "\n")
if len(input) == 0 {
panic("empty input.txt file")
}
}
func main() {
var part int
flag.IntVar(&part, "part", 1, "part 1 or 2")
flag.Parse()
fmt.Println("Running part", part)
if part == 1 {
ans := part1(input)
util.CopyToClipboard(fmt.Sprintf("%v", ans))
fmt.Println("Output:", ans)
} else {
ans := part2(input)
util.CopyToClipboard(fmt.Sprintf("%v", ans))
fmt.Println("Output:", ans)
}
}
func part1(input string) int {
parsed := parseInput(input)
_ = parsed
return 0
}
func part2(input string) int {
return 0
}
func parseInput(input string) (ans []int) {
for _, line := range strings.Split(input, "\n") {
ans = append(ans, cast.ToInt(line))
}
return ans
}
+47
View File
@@ -0,0 +1,47 @@
package main
import (
"testing"
)
func Test_part1(t *testing.T) {
tests := []struct {
name string
input string
want int
}{
{
name: "actual",
input: input,
want: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := part1(tt.input); got != tt.want {
t.Errorf("part1() = %v, want %v", got, tt.want)
}
})
}
}
func Test_part2(t *testing.T) {
tests := []struct {
name string
input string
want int
}{
{
name: "actual",
input: input,
want: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := part2(tt.input); got != tt.want {
t.Errorf("part2() = %v, want %v", got, tt.want)
}
})
}
}
-150
View File
@@ -1,150 +0,0 @@
package main
import (
"fmt"
"os"
"path/filepath"
"text/template"
"github.com/alexchao26/advent-of-code-go/scripts/fetchers"
"github.com/alexchao26/advent-of-code-go/util"
)
type TemplateData struct {
Year int
Day string // a string to include the prefixing zero
}
var testTemplateString = `package main
import (
"testing"
)
func Test_part1(t *testing.T) {
tests := []struct {
name string
input string
want int
}{
// {"actual", util.ReadFile("input.txt"), ACTUAL_ANSWER},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := part1(tt.input); got != tt.want {
t.Errorf("part1() = %v, want %v", got, tt.want)
}
})
}
}
func Test_part2(t *testing.T) {
tests := []struct {
name string
input string
want int
}{
// {"actual", util.ReadFile("input.txt"), ACTUAL_ANSWER},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := part2(tt.input); got != tt.want {
t.Errorf("part2() = %v, want %v", got, tt.want)
}
})
}
}
`
var solutionTemplateString = `package main
import (
"flag"
"fmt"
"strings"
"github.com/alexchao26/advent-of-code-go/cast"
"github.com/alexchao26/advent-of-code-go/util"
)
func main() {
var part int
flag.IntVar(&part, "part", 1, "part 1 or 2")
flag.Parse()
fmt.Println("Running part", part)
if part == 1 {
ans := part1(util.ReadFile("./input.txt"))
util.CopyToClipboard(fmt.Sprintf("%v", ans))
fmt.Println("Output:", ans)
} else {
ans := part2(util.ReadFile("./input.txt"))
util.CopyToClipboard(fmt.Sprintf("%v", ans))
fmt.Println("Output:", ans)
}
}
func part1(input string) int {
parsed := parseInput(input)
_ = parsed
return 0
}
func part2(input string) int {
return 0
}
func parseInput(input string) (ans []int) {
for _, line := range strings.Split(input, "\n") {
ans = append(ans, cast.ToInt(l))
}
return ans
}
`
func main() {
day, year, _ := fetchers.ParseFlags()
data := TemplateData{
Year: year,
Day: fmt.Sprintf("%02d", day),
}
testTemp, err := template.New("test-template").Parse(testTemplateString)
if err != nil {
panic(err)
}
solutionTemp, err := template.New("solution-template").Parse(solutionTemplateString)
if err != nil {
panic(err)
}
solutionFilename := filepath.Join(util.Dirname(), "../../", fmt.Sprintf("%d/day%02d/main.go", year, day))
testFilename := filepath.Join(util.Dirname(), "../../", fmt.Sprintf("%d/day%02d/main_test.go", year, day))
fetchers.MakeDir(filepath.Dir(solutionFilename))
EnsureNotOverwriting(solutionFilename)
EnsureNotOverwriting(testFilename)
solutionWriter, err := os.Create(solutionFilename)
if err != nil {
panic(err)
}
testWriter, err := os.Create(testFilename)
if err != nil {
panic(err)
}
// note: data is no longer used, but keeping it for future reference of text/template
solutionTemp.Execute(solutionWriter, data)
testTemp.Execute(testWriter, data)
fmt.Println("templates made")
}
func EnsureNotOverwriting(filename string) {
_, err := os.Stat(filename)
if err == nil {
panic(fmt.Sprintf("File already exists: %s", filename))
}
}
+4 -4
View File
@@ -8,10 +8,10 @@ import (
"strings"
)
/*
ReadFile is a wrapper over io/ioutil.ReadFile but also determines the
dynamic absolute path to the file.
*/
// ReadFile is a wrapper over io/ioutil.ReadFile but also determines the dynamic
// absolute path to the file.
//
// Deprecated in favor of go:embed, refer to scripts/skeleton/tmpls
func ReadFile(pathFromCaller string) string {
// Docs: https://golang.org/pkg/runtime/#Caller
_, filename, _, ok := runtime.Caller(1)