Day 9 puzzle

Part One

I think that the first part of day 9 was pretty easy, just check every values and whether it is smaller than all the adjacent values. If so add that value plus 1 to res.

func oneDay9() {
    input, _ := ioutil.ReadFile("input9.txt")
    var arr = strings.Split(string(input), "\n")

    res := 0
    i, j := 0, 0

    isLow := func(y, x int) bool {
        // I put this function inside twoDay9()
        // because then I wouldn't have to parse i, j, and arr which would make it really messy.
        if y >= 0 && x >= 0 && y < len(arr) && x < len(arr[0]) {
            return arr[i][j] < arr[y][x]
        }
        return true
    }

    for i = 0; i < len(arr); i++ {
        for j = 0; j < len(arr[0]); j++ {
            if isLow(i+1, j) && isLow(i-1, j) && isLow(i, j+1) && isLow(i, j-1) {
                res += int(arr[i][j]-'0') + 1
            }
        }
    }

    fmt.Println(res)
}

Part Two

The idea of this solution is to use DFS (depth first search) to solve this problem.

  • First we find all the low points and add their positions to a stack
  • Then we start searching the adjacent positions for which values smoke can flow through.
  • Now we can keep doing step two until we can’t.

This can be shown using the following image:

  • In the image above the first part shows the DFS, and the second part shows the places that smoke can’t cross because they are the highest.
  • In the first part of the image:
    • Reds shows the lowest point
    • Oranges is the points that flow into red
    • Greens are the parts that flow into orange
    • Blues are the parts that flow into green
    • Yellows are the parts that flow into blue

Note that this can also BFS (breath first search) by changing the stack into a queue.

type lowPos struct {
	actual pos
	start  pos // the low position that we started at
}

type pos struct {
	i, j int
}

func twoDay9() {
	input, _ := ioutil.ReadFile("input9.txt")
	var arr = strings.Split(string(input), "\n")

	stack := []lowPos{}
	m := make(map[pos]int) // position : index in res
	res := []int{}         // size of basin
	visited := make(map[pos]bool)
	i, j := 0, 0

	isLow := func(y, x int) bool {
		// I put this function inside twoDay9()
		// because then I wouldn't have to parse i, j, and arr which would make it really messy.
		if y >= 0 && x >= 0 && y < len(arr) && x < len(arr[0]) {
			return arr[i][j] < arr[y][x]
		}
		return true
	}

	for i = 0; i < len(arr); i++ {
		for j = 0; j < len(arr[0]); j++ {
			if isLow(i+1, j) && isLow(i-1, j) && isLow(i, j+1) && isLow(i, j-1) {
				stack = append(stack, lowPos{pos{i, j}, pos{i, j}})
				res = append(res, 1)
				m[pos{i, j}] = len(res) - 1
			}
		}
	}

	canFlow := func(i, j, val int, start pos) {
		// This is for finding whether smoke can flow from one place to another.
		if i >= 0 && j >= 0 && i < len(arr) && j < len(arr[0]) &&
			!visited[pos{i, j}] && arr[i][j] != '9' && int(arr[i][j]-'0') > val {
			visited[pos{i, j}] = true
			res[m[start]]++
			stack = append(stack, lowPos{pos{i, j}, start})
		}
	}

	for len(stack) != 0 {
		pop := stack[len(stack)-1]
		stack = stack[:len(stack)-1]
		y, x, start := pop.actual.i, pop.actual.j, pop.start

		canFlow(y+1, x, int(arr[i][j]-'0'), start)
		canFlow(y-1, x, int(arr[i][j]-'0'), start)
		canFlow(y, x+1, int(arr[i][j]-'0'), start)
		canFlow(y, x-1, int(arr[i][j]-'0'), start)
	}

	sort.Ints(res)

	fmt.Println(res[len(res)-1] * res[len(res)-2] * res[len(res)-3]) // Last
}