-
Notifications
You must be signed in to change notification settings - Fork 2
/
aocgo.go
182 lines (140 loc) · 5.11 KB
/
aocgo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Package aocgo provides functions to get your Advent of Code puzzle inputs in a non-intrusive way.
package aocgo
import (
"fmt"
"strings"
"time"
"go.dalton.dog/aocgo/internal/cache"
"go.dalton.dog/aocgo/internal/resources"
"go.dalton.dog/aocgo/internal/session"
"go.dalton.dog/aocgo/internal/utils"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/log"
)
var correctTestColor = lipgloss.Color("#1d8509")
var incorrectTestColor = lipgloss.Color("#c40e4f")
var puzzleSolveColor = lipgloss.Color("#674dd9")
// InputData interface is a Generic wrapper around the possible forms you can get puzzle input data in
type InputData interface {
string | []string | [][]string | []byte
}
// AnswerData interface is for any form that the puzzle answer can be output as
type AnswerData interface {
int | string
}
// Func is a function that will take in
type Solver[input InputData, answer AnswerData] func(input) answer
// RunTest will run a given function with the given input, and compare it against a known output.
// The result will print with a color based on if your function returns the same result as was expected.
func RunTest[In InputData, Out AnswerData](title string, solver Solver[In, Out], inputData In, expected Out) {
start := time.Now()
answer := solver(inputData)
timeTaken := time.Since(start)
// Convert the answer to a string
answerStr := fmt.Sprintf("%v", answer)
expectedStr := fmt.Sprintf("%v", expected)
var outColor lipgloss.Color
if answerStr == expectedStr {
outColor = correctTestColor
} else {
outColor = incorrectTestColor
}
// Create styles using lipgloss
titleStyle := lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#FFFFFF")).
Background(outColor).
Padding(0, 1).Margin(1, 0, 0).
AlignHorizontal(lipgloss.Center)
borderStyle := lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(outColor).
Padding(0, 2)
// Pretty info
prettyAnswer := fmt.Sprintf("Answer : %v\n", answerStr)
prettyExpected := fmt.Sprintf("Expected: %v\n", expectedStr)
prettyTime := fmt.Sprintf("Runtime : %v", timeTaken)
// Render the answer inside the border
wrappedInfo := borderStyle.Render(prettyAnswer + prettyExpected + prettyTime)
// Render the title
titleBox := titleStyle.Width(lipgloss.Width(wrappedInfo)).Render(title)
// Combine the title and wrapped answer
output := lipgloss.JoinVertical(lipgloss.Top, titleBox, wrappedInfo)
// Display the output
fmt.Println(output)
}
// RunSolve will attempt to run the input function with the input data.
// It will print out information about the function run in a pretty table.
func RunSolve[In InputData, Out AnswerData](title string, solver Solver[In, Out], inputData In) {
start := time.Now()
answer := solver(inputData)
timeTaken := time.Since(start)
// Convert the answer to a string
answerStr := fmt.Sprintf("%v", answer)
// Create styles using lipgloss
titleStyle := lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#FFFFFF")).
Background(puzzleSolveColor).
Padding(0, 1).Margin(1, 0, 0).
AlignHorizontal(lipgloss.Center)
borderStyle := lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(puzzleSolveColor).
Padding(0, 2)
// Pretty info
prettyAnswer := fmt.Sprintf("Answer : %v\n", answerStr)
prettyTime := fmt.Sprintf("Runtime : %v", timeTaken)
// Render the answer inside the border
wrappedInfo := borderStyle.Render(prettyAnswer + prettyTime)
// Render the title
titleBox := titleStyle.Width(lipgloss.Width(wrappedInfo)).Render(title)
// Combine the title and wrapped answer
output := lipgloss.JoinVertical(lipgloss.Top, titleBox, wrappedInfo)
// Display the output
fmt.Println(output)
}
// GetInputAsByteArray will return the user's puzzle input, as determined by the file's working directory, as an array of bytes.
func GetInputAsByteArray() []byte {
year, day, err := utils.GetYearAndDayFromCWD()
if err != nil {
log.Fatal(err)
}
return getData(year, day)
}
// GetInputAsString will return the user's puzzle input, as determined by the file's working directory, as a single string.
func GetInputAsString() string {
return string(GetInputAsByteArray())
}
// GetInputAsLineArray will return the user's puzzle input, as determined by the file's working directory, as an array of strings, split on newline.
func GetInputAsLineArray() []string {
return strings.Split(GetInputAsString(), "\n")
}
// GetInputAsCharMatrix will return the user's puzzle input, as determined by the file's working directory, as a 2D matrix, split on newlines and then by every character
func GetInputAsCharMatrix() [][]string {
var out [][]string
for _, line := range GetInputAsLineArray() {
out = append(out, strings.Split(line, ""))
}
return out
}
func getData(year int, day int) []byte {
userToken, err := session.GetSessionToken(false)
if err != nil {
log.Fatal(err)
}
_, err = resources.NewUser(userToken)
if err != nil {
log.Fatal(err)
}
err = cache.StartupDBM(userToken)
if err != nil {
log.Fatal(err)
}
puzzle := resources.LoadOrCreatePuzzle(year, day, userToken)
input, err := puzzle.GetUserInput()
if err != nil {
log.Fatal(err)
}
return input
}