package main import ( "fmt" "math" ) const ( springX = 500 springY = 0 ) const ( KindSand = 1 << iota KindClay KindWater KindFlow KindSpring NumKinds ) var KindRunes = [NumKinds]rune{ KindSand: '.', KindClay: '#', KindWater: '~', KindFlow: '|', KindSpring: '+', } type Map struct { data map[int]map[int]int minX int minY int maxX int maxY int done bool changed bool } func (m *Map) Set(kind, x, y int) { if m.data[y] == nil { m.data[y] = make(map[int]int) } if m.data[y][x] != kind { m.data[y][x] = kind m.changed = true } if x < m.minX { m.minX = x } if x > m.maxX { m.maxX = x } } func (m *Map) Get(x, y int) int { if m.data[y] == nil { return KindSand } if k, ok := m.data[y][x]; ok { return k } return KindSand } func (m *Map) ParseInput(input []string) { m.minY = math.MaxInt32 m.maxY = 0 m.minX = math.MaxInt32 m.maxX = 0 m.Set(KindSpring, springX, springY) for _, line := range input { var a, b rune var posA, startB, endB int _, err := fmt.Sscanf(line, "%c=%d, %c=%d..%d", &a, &posA, &b, &startB, &endB) if err != nil { panic(err) } if a == 'x' { for y := startB; y <= endB; y++ { m.Set(KindClay, posA, y) } if posA < m.minX { m.minX = posA } if posA > m.maxX { m.maxX = posA } if startB < m.minY { m.minY = startB } if endB > m.maxY { m.maxY = endB } } else { for x := startB; x <= endB; x++ { m.Set(KindClay, x, posA) } if posA < m.minY { m.minY = posA } if posA > m.maxY { m.maxY = posA } if startB < m.minX { m.minX = startB } if endB > m.maxX { m.maxX = endB } } } } func (m *Map) Print() { for y := 0; y <= m.maxY; y++ { for x := m.minX; x <= m.maxX; x++ { fmt.Printf("%c", KindRunes[m.Get(x, y)]) } fmt.Println() } } func (m *Map) Count(mask int) int { var sum int for y := m.minY; y <= m.maxY; y++ { for x := m.minX; x <= m.maxX; x++ { if m.Get(x, y)&mask != 0 { sum++ } } } return sum } func NewMap(input []string) *Map { m := &Map{} m.data = make(map[int]map[int]int) m.ParseInput(input) return m } type Coordinate struct { X int Y int } func (m *Map) Tick() { m.changed = false for y := 0; y <= m.maxY; y++ { for x := m.minX; x <= m.maxX; x++ { switch m.Get(x, y) { case KindSpring, KindFlow: switch m.Get(x, y+1) { case KindSand: m.Set(KindFlow, x, y+1) case KindClay, KindWater: m.HorizontalFlow(x, y, -1) m.HorizontalFlow(x, y, +1) } case KindClay: m.HorizontalSettle(x+1, y, +1) } } } m.done = !m.changed } func (m *Map) HorizontalSettle(x, y, dir int) bool { if m.Get(x, y) != KindFlow { return false } switch m.Get(x+dir, y) { case KindClay, KindWater: if m.Get(x, y+1)&(KindClay|KindWater) != 0 { m.Set(KindWater, x, y) return true } case KindFlow: if m.HorizontalSettle(x+dir, y, dir) && m.Get(x, y+1)&(KindClay|KindWater) != 0 { m.Set(KindWater, x, y) return true } } return false } func (m *Map) HorizontalFlow(x, y, dir int) { if m.Get(x+dir, y) == KindSand && m.Get(x, y+1)&(KindClay|KindWater) != 0 { m.Set(KindFlow, x+dir, y) m.HorizontalFlow(x+dir, y, dir) } } func (m *Map) Flood() { for !m.done { m.Tick() } }