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 }