20223 day1
This commit is contained in:
116
2023/go/day01/day01.go
Normal file
116
2023/go/day01/day01.go
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
package day01
|
||||||
|
|
||||||
|
import (
|
||||||
|
"adventofcode2023/utils"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
func Part1(input string) int {
|
||||||
|
num := 0
|
||||||
|
lines := strings.Split(input, "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
for i:=0;i<len(line);i++ {
|
||||||
|
if line[i] >= '0' && line[i] <= '9' {
|
||||||
|
num += utils.MustAtoi(string(line[i])) * 10
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i:=len(line) - 1;i>=0;i-- {
|
||||||
|
if line[i] >= '0' && line[i] <= '9' {
|
||||||
|
num += utils.MustAtoi(string(line[i]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return num
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func Part2(input string) int {
|
||||||
|
digits := make(map[string]string)
|
||||||
|
digits["one"] = "1"
|
||||||
|
digits["two"] = "2"
|
||||||
|
digits["three"] = "3"
|
||||||
|
digits["four"] = "4"
|
||||||
|
digits["five"] = "5"
|
||||||
|
digits["six"] = "6"
|
||||||
|
digits["seven"] = "7"
|
||||||
|
digits["eight"] = "8"
|
||||||
|
digits["nine"] = "9"
|
||||||
|
|
||||||
|
digits_backwards := make(map[string]string)
|
||||||
|
digits_backwards["eno"] = "1"
|
||||||
|
digits_backwards["owt"] = "2"
|
||||||
|
digits_backwards["eerht"] = "3"
|
||||||
|
digits_backwards["ruof"] = "4"
|
||||||
|
digits_backwards["evif"] = "5"
|
||||||
|
digits_backwards["xis"] = "6"
|
||||||
|
digits_backwards["neves"] = "7"
|
||||||
|
digits_backwards["thgie"] = "8"
|
||||||
|
digits_backwards["enin"] = "9"
|
||||||
|
|
||||||
|
num := 0
|
||||||
|
found := false
|
||||||
|
lines := strings.Split(input, "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
for i:=0;i<len(line);i++ {
|
||||||
|
if line[i] >= '0' && line[i] <= '9' {
|
||||||
|
fmt.Println("first digit:", string(line[i]))
|
||||||
|
num += utils.MustAtoi(string(line[i])) * 10
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
if found { break;}
|
||||||
|
for key := range digits {
|
||||||
|
if strings.HasPrefix(line[i:], key) {
|
||||||
|
fmt.Println("first digit:", digits[key])
|
||||||
|
num += utils.MustAtoi(digits[key]) * 10
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
if found { break;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found = false
|
||||||
|
|
||||||
|
reverse := reverseString(line)
|
||||||
|
|
||||||
|
for i:=0;i<len(reverse);i++ {
|
||||||
|
if reverse[i] >= '0' && reverse[i] <= '9' {
|
||||||
|
fmt.Println("last digit:", string(reverse[i]))
|
||||||
|
num += utils.MustAtoi(string(reverse[i]))
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
if found { break;}
|
||||||
|
for key := range digits_backwards {
|
||||||
|
if strings.HasPrefix(reverse[i:], key) {
|
||||||
|
fmt.Println("last digit:", digits_backwards[key])
|
||||||
|
num += utils.MustAtoi(digits_backwards[key])
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
if found { break;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = false
|
||||||
|
|
||||||
|
}
|
||||||
|
return num
|
||||||
|
}
|
||||||
|
|
||||||
|
func reverseString(input string) string {
|
||||||
|
// Convert the string to a rune slice
|
||||||
|
runes := []rune(input)
|
||||||
|
|
||||||
|
// Get the length of the rune slice
|
||||||
|
length := len(runes)
|
||||||
|
|
||||||
|
// Reverse the order of runes
|
||||||
|
for i, j := 0, length-1; i < j; i, j = i+1, j-1 {
|
||||||
|
runes[i], runes[j] = runes[j], runes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the reversed rune slice back to a string
|
||||||
|
reversedString := string(runes)
|
||||||
|
|
||||||
|
return reversedString
|
||||||
|
}
|
||||||
28
2023/go/day01/day01_test.go
Normal file
28
2023/go/day01/day01_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package day01
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPart1(t *testing.T) {
|
||||||
|
r := Part1(
|
||||||
|
`1abc2
|
||||||
|
pqr3stu8vwx
|
||||||
|
a1b2c3d4e5f
|
||||||
|
treb7uchet`)
|
||||||
|
require.Equal(t, 142, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPart2(t *testing.T) {
|
||||||
|
r := Part2(
|
||||||
|
`two1nine
|
||||||
|
eightwothree
|
||||||
|
abcone2threexyz
|
||||||
|
xtwone3four
|
||||||
|
4nineeightseven2
|
||||||
|
zoneight234
|
||||||
|
7pqrstsixteen`)
|
||||||
|
require.Equal(t, 281, r)
|
||||||
|
}
|
||||||
1000
2023/go/day01/input.txt
Normal file
1000
2023/go/day01/input.txt
Normal file
File diff suppressed because it is too large
Load Diff
14
2023/go/go.mod
Normal file
14
2023/go/go.mod
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
module adventofcode2023
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/stretchr/testify v1.8.2
|
||||||
|
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
19
2023/go/go.sum
Normal file
19
2023/go/go.sum
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
|
||||||
|
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
83
2023/go/main.go
Normal file
83
2023/go/main.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
// "math/rand"
|
||||||
|
"os"
|
||||||
|
// "strings"
|
||||||
|
// "time"
|
||||||
|
"adventofcode2023/utils"
|
||||||
|
"adventofcode2023/day01"
|
||||||
|
)
|
||||||
|
// Usage: go run main.go <NN>
|
||||||
|
// assumes input is in day<NN>/input.txt
|
||||||
|
func main() {
|
||||||
|
d := day()
|
||||||
|
fmt.Printf("Running day %02d\n", d)
|
||||||
|
|
||||||
|
switch d {
|
||||||
|
case 1:
|
||||||
|
fmt.Printf("part 1: %d\n", day01.Part1(utils.Readfile(d)))
|
||||||
|
fmt.Printf("part 2: %d\n", day01.Part2(utils.Readfile(d)))
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("no such day: %d", d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads day from os.Args.
|
||||||
|
func day() int {
|
||||||
|
latest := 0
|
||||||
|
if len(os.Args) == 1 {
|
||||||
|
return latest
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Args[1] == "next" {
|
||||||
|
genNext(latest + 1)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
day := utils.MustAtoi(os.Args[1])
|
||||||
|
return day
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func genNext(n int) {
|
||||||
|
os.Mkdir(fmt.Sprintf("day%02d", n), 0755)
|
||||||
|
f, err := os.Create(fmt.Sprintf("day%02d/day%02d.go", n, n))
|
||||||
|
utils.PanicOnErr(err)
|
||||||
|
defer f.Close()
|
||||||
|
f.WriteString(fmt.Sprintf(`package day%02d
|
||||||
|
|
||||||
|
func Part1(input string) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func Part2(input string) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
`, n))
|
||||||
|
fmt.Printf("wrote day%02d/day%02d.go\n", n, n)
|
||||||
|
|
||||||
|
f, err = os.Create(fmt.Sprintf("day%02d/day%02d_test.go", n, n))
|
||||||
|
utils.PanicOnErr(err)
|
||||||
|
defer f.Close()
|
||||||
|
f.WriteString(fmt.Sprintf(`package day%02d
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPart1(t *testing.T) {
|
||||||
|
r := Part1("")
|
||||||
|
require.Equal(t, 0, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPart2(t *testing.T) {
|
||||||
|
r := Part2("")
|
||||||
|
require.Equal(t, 0, r)
|
||||||
|
}
|
||||||
|
`, n))
|
||||||
|
fmt.Printf("wrote day%02d/day%02d_test.go\n", n, n)
|
||||||
|
|
||||||
|
}
|
||||||
74
2023/go/utils/grid2d/grid2d.go
Normal file
74
2023/go/utils/grid2d/grid2d.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package grid2d
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"adventofcode2023/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Grid[T any] struct {
|
||||||
|
sizeX, sizeY int
|
||||||
|
matrix [][]T
|
||||||
|
empty T
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGrid[T any](sizeX, sizeY int, empty T) *Grid[T] {
|
||||||
|
matrix := make([][]T, sizeY)
|
||||||
|
rows := make([]T, sizeX*sizeY)
|
||||||
|
for i := 0; i < sizeX*sizeY; i++ {
|
||||||
|
rows[i] = empty
|
||||||
|
}
|
||||||
|
|
||||||
|
j := 0
|
||||||
|
for i := 0; i < sizeY; i++ {
|
||||||
|
matrix[i] = rows[j : j+sizeX : j+sizeX]
|
||||||
|
j += sizeX
|
||||||
|
}
|
||||||
|
return &Grid[T]{
|
||||||
|
sizeX: sizeX,
|
||||||
|
sizeY: sizeY,
|
||||||
|
matrix: matrix,
|
||||||
|
empty: empty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Grid[T]) SizeX() int {
|
||||||
|
return g.sizeX
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Grid[T]) SizeY() int {
|
||||||
|
return g.sizeY
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Grid[T]) Get(x, y int) T {
|
||||||
|
if x < 0 || x >= g.sizeX {
|
||||||
|
return g.empty
|
||||||
|
}
|
||||||
|
if y < 0 || y >= g.sizeY {
|
||||||
|
return g.empty
|
||||||
|
}
|
||||||
|
return g.matrix[y][x]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Grid[T]) Set(x, y int, v T) {
|
||||||
|
if x < 0 || x >= g.sizeX {
|
||||||
|
panic("invalid x")
|
||||||
|
}
|
||||||
|
if y < 0 || y >= g.sizeY {
|
||||||
|
panic("invalid y")
|
||||||
|
}
|
||||||
|
g.matrix[y][x] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Grid[T]) StringWithFormatter(formatter func(T, int, int) string) string {
|
||||||
|
var r strings.Builder
|
||||||
|
for j := 0; j < g.sizeY; j++ {
|
||||||
|
for i := 0; i < g.sizeX; i++ {
|
||||||
|
_, err := r.WriteString(formatter(g.matrix[j][i], i, j))
|
||||||
|
utils.PanicOnErr(err)
|
||||||
|
}
|
||||||
|
_, err := r.WriteRune('\n')
|
||||||
|
utils.PanicOnErr(err)
|
||||||
|
}
|
||||||
|
return r.String()
|
||||||
|
}
|
||||||
56
2023/go/utils/inputFile.go
Normal file
56
2023/go/utils/inputFile.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenInputFile(day int) string {
|
||||||
|
var d string
|
||||||
|
if day < 10 {
|
||||||
|
d = fmt.Sprintf("0%d", day)
|
||||||
|
} else {
|
||||||
|
d = fmt.Sprintf("%d", day)
|
||||||
|
}
|
||||||
|
|
||||||
|
pwd, _ := os.Getwd()
|
||||||
|
path := fmt.Sprintf("%s\\day%s\\input.txt", pwd, d)
|
||||||
|
fi, _ := os.Stat(path)
|
||||||
|
if fi != nil {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Creating new input file...")
|
||||||
|
f, _ := os.OpenFile(path, os.O_CREATE, 0700)
|
||||||
|
f.WriteString(readHttp(2022, day))
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHttp(year, day int) string {
|
||||||
|
fmt.Println("Fetching data into file...")
|
||||||
|
|
||||||
|
url := fmt.Sprintf("https://adventofcode.com/%d/day/%d/input", year, day)
|
||||||
|
session := os.Getenv("sessionAoC")
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.AddCookie(&http.Cookie{Name: "session", Value: session})
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(body)
|
||||||
|
}
|
||||||
|
|
||||||
45
2023/go/utils/inputs/inputs.go
Normal file
45
2023/go/utils/inputs/inputs.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package inputs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"adventofcode2023/utils"
|
||||||
|
"adventofcode2023/utils/grid2d"
|
||||||
|
sparsegrid "adventofcode2023/utils/sparseGrid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ToInts(input string, sep string) []int {
|
||||||
|
var r []int
|
||||||
|
for _, line := range strings.Split(input, sep) {
|
||||||
|
if line != "" {
|
||||||
|
r = append(r, utils.MustAtoi(line))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToGrid2D[T any](input, rowSep, colSep string, empty T, conv func(string) T) *grid2d.Grid[T] {
|
||||||
|
lines := strings.Split(input, rowSep)
|
||||||
|
|
||||||
|
grid := grid2d.NewGrid(len(lines[0]), len(lines), empty)
|
||||||
|
for y, line := range lines {
|
||||||
|
for x, v := range strings.Split(line, colSep) {
|
||||||
|
grid.Set(x, y, conv(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToSparseGrid[T comparable](input, rowSep, colSep string, empty T, conv func(string) T) *sparsegrid.SparseGrid[T] {
|
||||||
|
lines := strings.Split(input, rowSep)
|
||||||
|
|
||||||
|
grid := sparsegrid.NewGrid(empty)
|
||||||
|
for y, line := range lines {
|
||||||
|
for x, v := range strings.Split(line, colSep) {
|
||||||
|
grid.Set(x, y, conv(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid
|
||||||
|
}
|
||||||
81
2023/go/utils/sparseGrid/sparseGrid.go
Normal file
81
2023/go/utils/sparseGrid/sparseGrid.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package sparsegrid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"adventofcode2023/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SparseGrid[T comparable] struct {
|
||||||
|
minX, maxX, minY, maxY int
|
||||||
|
data map[string]T
|
||||||
|
empty T
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGrid[T comparable](empty T) *SparseGrid[T] {
|
||||||
|
return &SparseGrid[T]{
|
||||||
|
minX: utils.MaxInt,
|
||||||
|
maxX: utils.MinInt,
|
||||||
|
minY: utils.MaxInt,
|
||||||
|
maxY: utils.MinInt,
|
||||||
|
data: map[string]T{},
|
||||||
|
empty: empty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SparseGrid[T]) SizeX() (int, int) {
|
||||||
|
return g.minX, g.maxX
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SparseGrid[T]) SizeY() (int, int) {
|
||||||
|
return g.minY, g.maxY
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SparseGrid[T]) Visited() int {
|
||||||
|
return len(g.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SparseGrid[T]) Get(x, y int) T {
|
||||||
|
k := key(x, y)
|
||||||
|
v, ok := g.data[k]
|
||||||
|
if !ok {
|
||||||
|
return g.empty
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SparseGrid[T]) Set(x, y int, v T) {
|
||||||
|
k := key(x, y)
|
||||||
|
current, ok := g.data[k]
|
||||||
|
if ok && v == current {
|
||||||
|
return
|
||||||
|
} else if !ok && v == g.empty {
|
||||||
|
return
|
||||||
|
} else if v == g.empty {
|
||||||
|
delete(g.data, k)
|
||||||
|
} else {
|
||||||
|
g.data[k] = v
|
||||||
|
g.minX = utils.Min(g.minX, x)
|
||||||
|
g.maxX = utils.Max(g.maxX, x)
|
||||||
|
g.minY = utils.Min(g.minY, y)
|
||||||
|
g.maxY = utils.Max(g.maxY, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SparseGrid[T]) StringWithFormatter(formatter func(T, int, int) string) string {
|
||||||
|
var r strings.Builder
|
||||||
|
for j := g.minY; j <= g.maxY; j++ {
|
||||||
|
for i := g.minX; i <= g.maxX; i++ {
|
||||||
|
_, err := r.WriteString(formatter(g.Get(i, j), i, j))
|
||||||
|
utils.PanicOnErr(err)
|
||||||
|
}
|
||||||
|
_, err := r.WriteRune('\n')
|
||||||
|
utils.PanicOnErr(err)
|
||||||
|
}
|
||||||
|
return r.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func key(x, y int) string {
|
||||||
|
return fmt.Sprintf("%d:%d", x, y)
|
||||||
|
}
|
||||||
222
2023/go/utils/utils.go
Normal file
222
2023/go/utils/utils.go
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PanicOnErr(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MaxInt = int(^uint(0) >> 1)
|
||||||
|
const MinInt = ^MaxInt
|
||||||
|
|
||||||
|
func Max[T constraints.Ordered](a, b T) T {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func Min[T constraints.Ordered](a, b T) T {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceMinMax[T constraints.Ordered](slice []T) (*T, *T) {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
min := &slice[0]
|
||||||
|
max := &slice[0]
|
||||||
|
for i, v := range slice {
|
||||||
|
if v < *min {
|
||||||
|
min = &slice[i]
|
||||||
|
}
|
||||||
|
if v > *max {
|
||||||
|
max = &slice[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return min, max
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustAtoi(s string) int {
|
||||||
|
v, err := strconv.Atoi(s)
|
||||||
|
PanicOnErr(err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns key from map[T]int which has the max value
|
||||||
|
func MapFindMax(m interface{}) interface{} {
|
||||||
|
var maxK interface{} = nil
|
||||||
|
var maxV = MinInt
|
||||||
|
iter := reflect.ValueOf(m).MapRange()
|
||||||
|
for iter.Next() {
|
||||||
|
k := iter.Key()
|
||||||
|
v := int(iter.Value().Int())
|
||||||
|
if v > maxV {
|
||||||
|
maxV = v
|
||||||
|
maxK = k.Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxK
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns key from map[T]int which has the min value
|
||||||
|
func MapFindMin(m interface{}) interface{} {
|
||||||
|
var minK interface{} = nil
|
||||||
|
var minV = MaxInt
|
||||||
|
iter := reflect.ValueOf(m).MapRange()
|
||||||
|
for iter.Next() {
|
||||||
|
k := iter.Key()
|
||||||
|
v := int(iter.Value().Int())
|
||||||
|
if v < minV {
|
||||||
|
minV = v
|
||||||
|
minK = k.Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return minK
|
||||||
|
}
|
||||||
|
|
||||||
|
func Readfile(day int) string {
|
||||||
|
filename := fmt.Sprintf("day%02d/input.txt", day)
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
PanicOnErr(err)
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
reader := bufio.NewReader(file)
|
||||||
|
contents, err := io.ReadAll(reader)
|
||||||
|
PanicOnErr(err)
|
||||||
|
|
||||||
|
return strings.TrimSuffix(string(contents), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadfileAsSlice(day int) []string {
|
||||||
|
filename := fmt.Sprintf("day%02d/input.txt", day)
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
PanicOnErr(err)
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
arr := make([]string, 0)
|
||||||
|
sc := bufio.NewScanner(file)
|
||||||
|
for sc.Scan() {
|
||||||
|
arr = append(arr, sc.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseToStruct(re *regexp.Regexp, input string, target interface{}) bool {
|
||||||
|
m := re.FindStringSubmatch(input)
|
||||||
|
if m == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var useOffset bool
|
||||||
|
|
||||||
|
for i, name := range re.SubexpNames() {
|
||||||
|
if i == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var field reflect.Value
|
||||||
|
if name == "" {
|
||||||
|
// use offset
|
||||||
|
if i == 1 {
|
||||||
|
useOffset = true
|
||||||
|
} else if !useOffset {
|
||||||
|
panic("can't mix named and unnamed subexpressions")
|
||||||
|
}
|
||||||
|
field = reflect.ValueOf(target).Elem().Field(i - 1)
|
||||||
|
} else {
|
||||||
|
// use name
|
||||||
|
if i == 1 {
|
||||||
|
useOffset = false
|
||||||
|
} else if useOffset {
|
||||||
|
panic("can't mix named and unnamed subexpressions")
|
||||||
|
}
|
||||||
|
field = reflect.ValueOf(target).Elem().FieldByName(name)
|
||||||
|
}
|
||||||
|
if field.Kind() == reflect.String {
|
||||||
|
field.SetString(m[i])
|
||||||
|
} else if field.Kind() == reflect.Int {
|
||||||
|
v, err := strconv.Atoi(m[i])
|
||||||
|
PanicOnErr(err)
|
||||||
|
field.SetInt(int64(v))
|
||||||
|
} else if field.Kind() == reflect.Uint8 {
|
||||||
|
if len(m[i]) != 1 {
|
||||||
|
panic(fmt.Sprintf("expecting 1 char, got: %s", m[i]))
|
||||||
|
}
|
||||||
|
field.SetUint(uint64(m[i][0]))
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintf("unknown kind: %s", field.Kind()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustParseToStruct(re *regexp.Regexp, input string, target interface{}) {
|
||||||
|
if !ParseToStruct(re, input, target) {
|
||||||
|
panic(fmt.Errorf("failed to parse: %s", input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CharToLower(c byte) byte {
|
||||||
|
return strings.ToLower(string(c))[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func CharToUpper(c byte) byte {
|
||||||
|
return strings.ToUpper(string(c))[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func Contains(haystack []string, needle string) bool {
|
||||||
|
for _, s := range haystack {
|
||||||
|
if s == needle {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func Abs[T constraints.Signed](x T) T {
|
||||||
|
if x < 0 {
|
||||||
|
return -x
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func Gcd(x, y int) int {
|
||||||
|
if x <= 0 || y <= 0 {
|
||||||
|
panic(fmt.Errorf("invalid input: %d, %d", x, y))
|
||||||
|
}
|
||||||
|
if x == y {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
if x > y {
|
||||||
|
return Gcd(x-y, y)
|
||||||
|
} else {
|
||||||
|
return Gcd(x, y-x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sign[T constraints.Signed](x T) int {
|
||||||
|
if x < 0 {
|
||||||
|
return -1
|
||||||
|
} else if x > 0 {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user