aoc-2020/util.go

301 lines
5.6 KiB
Go

package main
import (
"bufio"
"errors"
"fmt"
"math"
"os"
)
var (
errItemAlreadySet = errors.New("cannot set item in direction as one is already set")
)
// ProcessLines maps a function on lines parsed from a file.
func ProcessLines(path string, f func(string) (stop bool, err error)) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
stop, err := f(scanner.Text())
if stop || err != nil {
return err
}
}
if err := scanner.Err(); err != nil {
return err
}
return nil
}
// ProcessGroups maps a function on groups of lines parsed from a file separated by some string.
func ProcessGroups(path, separator string, f func([]string) (stop bool, err error)) error {
group := []string{}
if err := ProcessLines(path, func(line string) (stop bool, err error) {
if line == separator {
stop, err = f(group)
if stop || err != nil {
return
}
group = []string{}
} else {
group = append(group, line)
}
return
}); err != nil {
return err
}
_, err := f(group)
return err
}
// StringSet is a set of strings..
type StringSet map[string]bool
// Add an item to a StringSet.
func (s *StringSet) Add(v string) {
(*s)[v] = true
}
// AddAll adds all items in a list to the set.
func (s *StringSet) AddAll(l []string) {
for _, v := range l {
s.Add(v)
}
}
// Items returns all values in the set as a list.
func (s StringSet) Items() []string {
values := []string{}
for k := range s {
values = append(values, k)
}
return values
}
// NewStringSet creates an empty string set.
func NewStringSet(values []string) StringSet {
v := StringSet{}
v.AddAll(values)
return v
}
// Union returns the union of two SringSets.
func Union(s1, s2 StringSet) StringSet {
union := NewStringSet(s1.Items())
union.AddAll(s2.Items())
return union
}
// Intersection returns the union of two SringSets.
func Intersection(s1, s2 StringSet) StringSet {
intersect := StringSet{}
for v := range s1 {
if _, ok := s2[v]; ok {
intersect.Add(v)
}
}
return intersect
}
// Difference returns the value of s1 with values of s2 removed.
func Difference(s1, s2 StringSet) StringSet {
difference := StringSet{}
for v := range s1 {
if _, ok := s2[v]; !ok {
difference.Add(v)
}
}
return difference
}
// Point represents a point on a coordinate system.
type Point struct {
x, y int
}
// AddPoints adds two points together.
func AddPoints(p1, p2 Point) Point {
return Point{
p1.x + p2.x,
p1.y + p2.y,
}
}
// PowInt is equivalent to math.Pow but for integers.
func PowInt(x, y int) int {
return int(math.Pow(float64(x), float64(y)))
}
// LinkedGridItem is a single item in a linked grid.
type LinkedGridItem struct {
location Point
left, right, up, down *LinkedGridItem
grid *LinkedGrid
}
func (item LinkedGridItem) String() string {
return fmt.Sprintf("%v", item.location)
}
// Location of the item.
func (item LinkedGridItem) Location() Point {
return item.location
}
// Left gives a pointer to the item on the left.
func (item LinkedGridItem) Left() *LinkedGridItem {
return item.left
}
// Up gives a pointer to the item above.
func (item LinkedGridItem) Up() *LinkedGridItem {
return item.up
}
// Right gives a pointer to the item on the right.
func (item LinkedGridItem) Right() *LinkedGridItem {
return item.right
}
// Down gives a pointer to the item below.
func (item LinkedGridItem) Down() *LinkedGridItem {
return item.down
}
// SetLeft gives a pointer to the item on the left.
func (item *LinkedGridItem) SetLeft(other *LinkedGridItem) error {
if item.left != nil || other.right != nil {
return errItemAlreadySet
}
item.left = other
other.right = item
return nil
}
// SetUp gives a pointer to the item on the up.
func (item *LinkedGridItem) SetUp(other *LinkedGridItem) error {
if item.up != nil || other.down != nil {
return errItemAlreadySet
}
item.up = other
other.down = item
return nil
}
// SetRight gives a pointer to the item on the right.
func (item *LinkedGridItem) SetRight(other *LinkedGridItem) error {
if item.right != nil || other.left != nil {
return errItemAlreadySet
}
item.right = other
other.left = item
return nil
}
// SetDown gives a pointer to the item on the down.
func (item *LinkedGridItem) SetDown(other *LinkedGridItem) error {
if item.down != nil || other.up != nil {
return errItemAlreadySet
}
item.down = other
other.up = item
return nil
}
// LinkedGrid is a 2d array of grid square.
type LinkedGrid [][]*LinkedGridItem
// Print a linked grid.
func (grid LinkedGrid) Print() {
for _, row := range grid {
for _, item := range row {
fmt.Print(item.String())
}
fmt.Print("\n")
}
}
// NewLinkedGrid initializes a new linked grid of a given size.
func NewLinkedGrid(width, height int) *LinkedGrid {
var w = make(LinkedGrid, height)
for y := 0; y < height; y++ {
w[y] = make([]*LinkedGridItem, width)
for x := 0; x < height; x++ {
item := LinkedGridItem{location: Point{x, y}}
if x != 0 {
_ = item.SetLeft(w[y][x-1])
}
if y != 0 {
_ = item.SetUp(w[y-1][x])
}
w[y][x] = &item
}
}
return &w
}
// MapInt maps a function onto a list of ints.
func MapInt(a []int, f func(int) (int, error)) error {
var err error
for i, v := range a {
a[i], err = f(v)
if err != nil {
return err
}
}
return nil
}
// ReduceInt maps a function onto a list of ints.
func ReduceInt(a []int, start int, f func(int, int) (int, error)) (int, error) {
var err error
result := start
for _, v := range a {
result, err = f(result, v)
if err != nil {
return 0, err
}
}
return result, nil
}