Added command suite

This commit is contained in:
Ceferino Patino 2024-12-11 17:37:27 -06:00
commit c66bf9075c
Signed by: c4patino
SSH key fingerprint: SHA256:Wu+cU1t+7zVT9wzew/4meNmeulo66NzMqc22MdDBgXI
11 changed files with 570 additions and 58 deletions

49
2024/cmd/generate/cmd.go Normal file
View file

@ -0,0 +1,49 @@
package generate
import (
"fmt"
"log"
"os"
"strconv"
"github.com/spf13/cobra"
)
func handleErr(err error, folder string) {
os.RemoveAll(folder)
log.Fatal(err)
}
var GenerateCmd = &cobra.Command{
Use: "generate",
Short: "Generate a new day directory",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
day, err := strconv.Atoi(args[0])
if err != nil {
log.Fatal(err)
}
folder := fmt.Sprintf("./day%02d", day)
if err := os.Mkdir(folder, 0755); err != nil {
handleErr(err, folder)
}
if err := generateMainCode(day); err != nil {
handleErr(err, folder)
}
if err := generateTestCode(day); err != nil {
handleErr(err, folder)
}
if err := retrieveInput(day); err != nil {
handleErr(err, folder)
}
filename := fmt.Sprintf("./day%02d/test.txt", day)
if err := os.WriteFile(filename, []byte{}, 0755); err != nil {
handleErr(err, folder)
}
},
}

View file

@ -0,0 +1,59 @@
package generate
import (
"errors"
"fmt"
"io"
"net/http"
"net/http/cookiejar"
"os"
)
// https://adventofcode.com/2024/day/{day}/answer
func retrieveInput(day int) error {
cookieValue := os.Getenv("COOKIE")
if cookieValue == "" {
return errors.New("COOKIE environment variable not set")
}
jar, err := cookiejar.New(nil)
if err != nil {
return err
}
client := &http.Client{Jar: jar}
url := fmt.Sprintf("https://adventofcode.com/2024/day/%d/input", day)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
req.Header.Set("User-Agent", "github.com/C4theBomb/advent-of-code by c4patino@gmail.com")
cookie := &http.Cookie{Name: "session", Value: cookieValue}
req.AddCookie(cookie)
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return errors.New(fmt.Sprintf("Unexpected status code: %d\nBody: %s", resp.StatusCode, string(body)))
}
filename := fmt.Sprintf("./day%02d/input.txt", day)
if err := os.WriteFile(filename, []byte(body), 0755); err != nil {
return err
}
return nil
}

View file

@ -0,0 +1,66 @@
package generate
import (
"fmt"
"os"
"os/exec"
)
var mainCodeTemplate = `package day%02d
import (
"bufio"
"fmt"
"os"
)
func Part1() int {
return -1
}
func Part2() int {
return -1
}
func Run(filename string) (int, int) {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fmt.Println(line)
}
if err := scanner.Err(); err != nil {
panic(err)
}
part1 := Part1()
part2 := Part2()
return part1, part2
}
`
func generateMainCode(day int) error {
testCode := fmt.Sprintf(mainCodeTemplate, day)
filename := fmt.Sprintf("./day%02d/day%02d.go", day, day)
if err := os.WriteFile(filename, []byte(testCode), 0755); err != nil {
return err
}
cmd := exec.Command("go", "fmt", filename)
if err := cmd.Run(); err != nil {
return err
}
return nil
}

View file

@ -0,0 +1,70 @@
package generate
import (
"fmt"
"os"
"os/exec"
)
var testCodeTemplate = `package day%02d
import (
"os"
"testing"
)
func TestDay%02d(t *testing.T) {
filename := "test.txt"
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Fatalf("test file does not exist: %%v", filename)
}
part1, part2 := Run(filename)
expectedPart1 := -1
if part1 != expectedPart1 {
t.Fatalf("unexpected part1:\nwant:\t%%d\ngot:\t%%d", expectedPart1, part1)
}
expectedPart2 := -1
if part2 != expectedPart2 {
t.Fatalf("unexpected part2:\nwant:\t%%d\ngot:\t%%d", expectedPart2, part2)
}
}
// func TestDay%02dSolutions(t *testing.T) {
// filename := "input.txt"
// if _, err := os.Stat(filename); os.IsNotExist(err) {
// t.Fatalf("test file does not exist: %%v", filename)
// }
//
// part1, part2 := Run(filename)
//
// expectedPart1 := -1
// if part1 != expectedPart1 {
// t.Fatalf("unexpected part1:\nwant:\t%%d\ngot:\t%%d", expectedPart1, part1)
// }
//
// expectedPart2 := -1
// if part2 != expectedPart2 {
// t.Fatalf("unexpected part2:\nwant:\t%%d\ngot:\t%%d", expectedPart2, part2)
// }
// }
`
func generateTestCode(day int) error {
testCode := fmt.Sprintf(testCodeTemplate, day, day, day)
filename := fmt.Sprintf("./day%02d/day%02d_test.go", day, day)
if err := os.WriteFile(filename, []byte(testCode), 0755); err != nil {
return err
}
cmd := exec.Command("go", "fmt", filename)
if err := cmd.Run(); err != nil {
return err
}
return nil
}

87
2024/cmd/root.go Normal file
View file

@ -0,0 +1,87 @@
package cmd
import (
"fmt"
"log"
"os"
"strconv"
"cpatino.com/advent-of-code/2024/cmd/generate"
"cpatino.com/advent-of-code/2024/cmd/submit"
"github.com/spf13/cobra"
"cpatino.com/advent-of-code/2024/day1"
"cpatino.com/advent-of-code/2024/day10"
"cpatino.com/advent-of-code/2024/day11"
"cpatino.com/advent-of-code/2024/day2"
"cpatino.com/advent-of-code/2024/day3"
"cpatino.com/advent-of-code/2024/day4"
"cpatino.com/advent-of-code/2024/day5"
"cpatino.com/advent-of-code/2024/day6"
"cpatino.com/advent-of-code/2024/day7"
"cpatino.com/advent-of-code/2024/day8"
"cpatino.com/advent-of-code/2024/day9"
)
var days = map[int]func(string) (int, int){
1: day1.Run,
10: day10.Run,
11: day11.Run,
2: day2.Run,
3: day3.Run,
4: day4.Run,
5: day5.Run,
6: day6.Run,
7: day7.Run,
8: day8.Run,
9: day9.Run,
}
var rootCmd = &cobra.Command{
Use: "advent-of-code",
Short: "Advent of Code 2024",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
day, err := strconv.Atoi(args[0])
if err != nil {
log.Fatal(err)
}
file, _ := cmd.Flags().GetString("file")
if file == "" {
file = fmt.Sprintf("./day%d/input.txt", day)
}
if _, err := os.Stat(file); os.IsNotExist(err) {
log.Fatalf("file does not exist: %s", file)
}
log.Printf("Day: %d, Input File: %s\n", day, file)
part1, part2 := days[day](file)
log.Printf("Part 1: %d\n", part1)
log.Printf("Part 2: %d\n", part2)
},
}
func init() {
rootCmd.AddCommand(generate.GenerateCmd)
rootCmd.AddCommand(submit.SubmitCmd)
rootCmd.Flags().String("file", "", "Path to a file")
}
func Execute() error {
if len(os.Args) == 1 {
return rootCmd.Execute()
}
subCmd := os.Args[1]
for _, cmd := range rootCmd.Commands() {
if cmd.Use == subCmd {
return cmd.Execute()
}
}
return rootCmd.Execute()
}

40
2024/cmd/submit/cmd.go Normal file
View file

@ -0,0 +1,40 @@
package submit
import (
"log"
"os"
"strconv"
"github.com/spf13/cobra"
)
func handleErr(err error, folder string) {
os.RemoveAll(folder)
log.Fatal(err)
}
var SubmitCmd = &cobra.Command{
Use: "submit",
Short: "Submit solution to Advent of Code 2024",
Args: cobra.ExactArgs(3),
Run: func(cmd *cobra.Command, args []string) {
day, err := strconv.Atoi(args[0])
if err != nil {
log.Fatal(err)
}
part, err := strconv.Atoi(args[1])
if err != nil {
log.Fatal(err)
}
answer, err := strconv.Atoi(args[2])
if err != nil {
log.Fatal(err)
}
if err := submitInput(day, part, answer); err != nil {
log.Fatal(err)
}
},
}

View file

@ -0,0 +1,63 @@
package submit
import (
"errors"
"fmt"
"io"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"strings"
)
func submitInput(day, level, answer int) error {
cookieValue := os.Getenv("COOKIE")
if cookieValue == "" {
return errors.New("COOKIE environment variable not set")
}
jar, err := cookiejar.New(nil)
if err != nil {
return err
}
data := url.Values{}
data.Set("level", fmt.Sprintf("%d", level))
data.Set("answer", fmt.Sprintf("%d", answer))
client := &http.Client{Jar: jar}
reqUrl := fmt.Sprintf("https://adventofcode.com/2024/day/%d/answer", day)
req, err := http.NewRequest("POST", reqUrl, strings.NewReader(data.Encode()))
if err != nil {
return err
}
req.Header.Set("User-Agent", "github.com/C4theBomb/advent-of-code by c4patino@gmail.com")
cookie := &http.Cookie{Name: "session", Value: cookieValue}
req.AddCookie(cookie)
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return errors.New(fmt.Sprintf("Unexpected status code: %d\nBody: %s", resp.StatusCode, string(body)))
}
filename := fmt.Sprintf("./day%02d/input.txt", day)
if err := os.WriteFile(filename, []byte(body), 0755); err != nil {
return err
}
return nil
}

114
2024/cmd/update/main.go Normal file
View file

@ -0,0 +1,114 @@
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)
func main() {
const mainTemplate = `package cmd
import (
"fmt"
"log"
"os"
"strconv"
"github.com/spf13/cobra"
"cpatino.com/advent-of-code/2024/cmd/generate"
"cpatino.com/advent-of-code/2024/cmd/submit"
%s
)
var days = map[int]func(string) (int, int){
%s
}
var rootCmd = &cobra.Command{
Use: "advent-of-code",
Short: "Advent of Code 2024",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
day, err := strconv.Atoi(args[0])
if err != nil {
log.Fatal(err)
}
file, _ := cmd.Flags().GetString("file")
if file == "" {
file = fmt.Sprintf("./day%%d/input.txt", day)
}
if _, err := os.Stat(file); os.IsNotExist(err) {
log.Fatalf("file does not exist: %%s", file)
}
log.Printf("Day: %%d, Input File: %%s\n", day, file)
part1, part2 := days[day](file)
log.Printf("Part 1: %%d\n", part1)
log.Printf("Part 2: %%d\n", part2)
},
}
func init() {
rootCmd.AddCommand(generate.GenerateCmd)
rootCmd.AddCommand(submit.SubmitCmd)
rootCmd.Flags().String("file", "", "Path to a file")
}
func Execute() error {
if len(os.Args) == 1 {
return rootCmd.Execute()
}
subCmd := os.Args[1]
for _, cmd := range rootCmd.Commands() {
if cmd.Use == subCmd {
return cmd.Execute()
}
}
return rootCmd.Execute()
}
`
var imports, mappings strings.Builder
err := filepath.Walk("./", func(path string, info os.FileInfo, err error) error {
if !info.IsDir() || !strings.HasPrefix(info.Name(), "day") {
return nil
}
dayStr := info.Name()
dayNum := strings.TrimPrefix(dayStr, "day")
imports.WriteString(fmt.Sprintf("\t\"cpatino.com/advent-of-code/2024/%s\"\n", dayStr))
mappings.WriteString(fmt.Sprintf("\t%s: %s.Run,\n", dayNum, dayStr))
return nil
})
importsStr := strings.TrimSpace(imports.String())
mappingsStr := strings.TrimSpace(mappings.String())
if err != nil {
fmt.Println("Error walking the path:", err)
os.Exit(1)
}
mainCode := fmt.Sprintf(mainTemplate, importsStr, mappingsStr)
os.WriteFile("./cmd/root.go", []byte(mainCode), 0644)
cmd := exec.Command("go", "fmt", "./cmd/root.go")
if err := cmd.Run(); err != nil {
fmt.Println("Error running go fmt:", err)
os.Exit(1)
}
}

View file

@ -1,3 +1,10 @@
module cpatino.com/advent-of-code/2024
go 1.23.3
require github.com/spf13/cobra v1.8.1
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
)

10
2024/go.sum Normal file
View file

@ -0,0 +1,10 @@
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -1,67 +1,14 @@
//go:generate go run ./cmd/update
package main
import (
"flag"
"fmt"
"os"
"strconv"
"log"
"cpatino.com/advent-of-code/2024/day1"
"cpatino.com/advent-of-code/2024/day10"
"cpatino.com/advent-of-code/2024/day11"
"cpatino.com/advent-of-code/2024/day2"
"cpatino.com/advent-of-code/2024/day3"
"cpatino.com/advent-of-code/2024/day4"
"cpatino.com/advent-of-code/2024/day5"
"cpatino.com/advent-of-code/2024/day6"
"cpatino.com/advent-of-code/2024/day7"
"cpatino.com/advent-of-code/2024/day8"
"cpatino.com/advent-of-code/2024/day9"
"cpatino.com/advent-of-code/2024/cmd"
)
var days = map[int]func(string) (int, int){
1: day1.Run,
2: day2.Run,
3: day3.Run,
4: day4.Run,
5: day5.Run,
6: day6.Run,
7: day7.Run,
8: day8.Run,
9: day9.Run,
10: day10.Run,
11: day11.Run,
}
func main() {
var fileArg = flag.String("file", "", "input filename")
flag.Parse()
log.SetFlags(0)
args := flag.Args()
if len(flag.Args()) == 0 {
fmt.Println("Usage: go run . [OPTIONS] <day>")
os.Exit(1)
}
day, err := strconv.Atoi(args[0])
if err != nil {
panic(err)
}
var inputFile = fmt.Sprintf("day%d/input.txt", day)
if *fileArg != "" {
inputFile = *fileArg
}
if _, err := os.Stat(inputFile); os.IsNotExist(err) {
fmt.Printf("test file does not exist: %v\n", inputFile)
os.Exit(1)
}
fmt.Printf("Day: %d, Input File: %s\n", day, inputFile)
part1, part2 := days[day](inputFile)
fmt.Printf("Part 1: %d\n", part1)
fmt.Printf("Part 2: %d\n", part2)
cmd.Execute()
}