🍯 Glaze

package main

import "core:fmt"
import "core:os"

parse_input_file :: proc(filepath: string, buffer: []int) -> (stride: int, sliced: []int) {
	data, ok := os.read_entire_file_from_filename(filepath)
	if !ok {
		panic("oh no, could not read file")
	}

	index: int
	for b, i in data {
		if b == '\n' {
			stride = i if stride == 0 else stride
			continue
		}
		buffer[index] = cast(int)(b - 48)
		index += 1
	}
	sliced = buffer[:index]

	return
}

Vec2 :: [2]int

check_visible :: proc(start: Vec2, dir: Vec2, stride: int, heights: []int) -> (bool, int) {
	max := heights[(start[1] * stride) + start[0]]
	current := start + dir
	steps: int
	for {
		if current[0] < 0 || current[0] >= stride {
			return true, steps
		}
		if current[1] < 0 || current[1] >= stride {
			return true, steps
		}
		i := (current[1] * stride) + current[0]
		if heights[i] >= max {
			return false, steps + 1
		}

		current += dir
		steps += 1
	}
	panic("unreachable!")
}

task1 :: proc(stride: int, heights: []int) -> int {
	result: int

	for y in 0 ..< stride {
		for x in 0 ..< stride {
			pos := Vec2{x, y}
			dirs := []Vec2{{0, -1}, {-1, 0}, {1, 0}, {0, 1}}
			visible: bool
			for d in dirs {
				if found_edge, _ := check_visible(pos, d, stride, heights); found_edge {
					visible = true
					break
				}
			}
			if visible {
				result += 1
			}
		}
	}

	return result
}

task2 :: proc(stride: int, heights: []int) -> int {
	result: int

	for y in 0 ..< stride {
		for x in 0 ..< stride {
			pos := Vec2{x, y}
			dirs := []Vec2{{0, -1}, {-1, 0}, {1, 0}, {0, 1}}
			score := 1
			for d in dirs {
				_, distance := check_visible(pos, d, stride, heights)
				score *= distance
			}
			result = score if score > result else result
		}
	}

	return result
}

main :: proc() {
	buffer: [65353]int
	stride, heights := parse_input_file("input.txt", buffer[:])

	result1 := task1(stride, heights)
	fmt.printf("Task 1 result: %d\n", result1)

	result2 := task2(stride, heights)
	fmt.printf("Task 2 result: %d\n", result2)
}