~williewillus/advent_of_code

362c77c7715509b248209a8a093fb563a1cd0cba — Vincent Lee 7 months ago 81b3326
2022: d14p1
2 files changed, 81 insertions(+), 40 deletions(-)

M aoc_2022/day14/day14.go
M aoc_2022/test.exp
M aoc_2022/day14/day14.go => aoc_2022/day14/day14.go +80 -39
@@ 2,38 2,13 @@ package day14

import (
	"bufio"
	"container/heap"
	//	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
	"williewillus/aoc_2022/util"
)

// cargo culted from container/heap doc
// An IntHeap is a min-heap of ints.
type IntHeap []int

func (h IntHeap) Len() int           { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

func (h *IntHeap) Push(x interface{}) {
	// Push and Pop use pointer receivers because they modify the slice's length,
	// not just its contents.
	*h = append(*h, x.(int))
}

func (h *IntHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

// End cargo cult

type vec2 util.Vec2

func parse(s string) (int, int) {


@@ 57,10 32,72 @@ func iorder(a, b int) (int, int) {
	}
}

const spawnX int = 500
const spawnY int = 0

func posOccupied(state map[int][]int, pos vec2) bool {
	col, ok := state[pos.X]
	if !ok {
		return false
	}
	idx := sort.SearchInts(col, pos.Y)
	return idx < len(col) && col[idx] == pos.Y
}

// Get the next occupied y coordinate "below" (greater than) the given x, y coordinate.
// Returns -1 if one doesn't exist
func getNextOccupiedBelow(state map[int][]int, pos vec2) int {
	col, ok := state[pos.X]
	if !ok {
		return -1
	}
	nextYIdx := sort.Search(len(col), func(idx int) bool {
		return col[idx] > pos.Y
	})
	
	if nextYIdx < len(col) {
		return col[nextYIdx]
	} else {
		return -1
	}
}

// Spawn a sand at spawnX, spawnY and simulate it until it comes to rest
// Second return value is false if the particle falls off the board, vec2 holds the last known step before falling off
// if it's true, then vec2 is the place it came to rest
func simulateNextSand(state map[int][]int) (vec2, bool) {
	pos := vec2{spawnX, spawnY}
	for {
		// Try moving down
		nextOccupied := getNextOccupiedBelow(state, pos)
		if nextOccupied == -1 {
			return pos, false
		}

		toMoveDownTo := vec2{pos.X, nextOccupied - 1}

		if toMoveDownTo == pos {
			// Already as far down as we can go, try moving sideways
			downLeft := vec2{pos.X-1, pos.Y + 1}
			downRight := vec2{pos.X+1, pos.Y + 1}
			if !posOccupied(state, downLeft) {
				pos = downLeft
			} else if !posOccupied(state, downRight) {
				pos = downRight
			} else {
				// Nowhere to go, come to rest
				return pos, true
			}
		} else {
			pos = toMoveDownTo
		}
	}
}

func Run() (string, string) {
	scanner := bufio.NewScanner(os.Stdin)
	// x -> heap of filled y's
	state := make(map[int]*IntHeap)
	// x -> filled Y coords (sorted)
	state := make(map[int][]int)
	for scanner.Scan() {
		line := scanner.Text()
		points := []vec2{}


@@ 78,22 115,26 @@ func Run() (string, string) {

			for x := startX; x <= endX; x++ {
				for y := startY; y <= endY; y++ {
					h, ok := state[x]
					if !ok {
						h = &IntHeap{}
					}
					heap.Push(h, y)
					state[x] = h
					state[x] = append(state[x], y)
				}
			}
		}
	}

	/*
		for x, ys := range state {
			fmt.Printf("top at x=%d is y=%d\n", x, (*ys)[0])
	for _, col := range state {
		sort.Ints(col)
	}

	p1 := 0
	for ; ; p1++ {
		restPos, didRest := simulateNextSand(state)
		if !didRest {
			break
		} else {
			state[restPos.X] = append(state[restPos.X], restPos.Y)
			sort.Ints(state[restPos.X])
		}
	*/
	}	

	return "incomplete", "incomplete"
	return strconv.Itoa(p1), "incomplete"
}

M aoc_2022/test.exp => aoc_2022/test.exp +1 -1
@@ 44,7 44,7 @@ Part 2: 386
Part 1: 6272
Part 2: 22288
--> Running day 14
Part 1: incomplete
Part 1: 805
Part 2: incomplete
--> Running day 15
Part 1: unimplemented