Files
advent_of_code/2025/3/odin/main.odin
2026-01-11 15:57:03 +01:00

142 lines
3.5 KiB
Odin

package day3
import "core:fmt"
import "core:mem"
import "core:os"
import "core:strings"
import "core:time"
PUZZLE_INPUT :: `
987654321111111
811111111111119
234234234234278
818181911112111
`
Data_Is_Empty_Error :: distinct struct{}
Data_Is_Ragged_Error :: distinct struct {
line: int,
}
Illegal_Token_Error :: distinct struct {
line, col: int,
token: rune,
}
Parsing_Error :: union {
mem.Allocator_Error,
Illegal_Token_Error,
Data_Is_Ragged_Error,
Data_Is_Empty_Error,
}
Grid :: struct {
data: []int,
rows, cols: int,
}
parse :: proc(input: string, allocator := context.allocator) -> (grid: Grid, err: Parsing_Error) {
context.allocator = context.temp_allocator
defer context.allocator = allocator
lines := strings.split_lines(strings.trim_space(input)) or_return
if lines[0] == "" do return {}, Data_Is_Empty_Error{}
rows := len(lines)
cols := len(strings.trim_space(lines[0]))
data := make([dynamic]int, 0, rows * cols, allocator) or_return
for line, row in lines {
clean_line := strings.trim_space(line)
if len(clean_line) != cols do return {}, Data_Is_Ragged_Error{row}
for char, col in clean_line {
digit := int(char - '0')
if digit < 0 || digit > 9 do return {}, Illegal_Token_Error{row, col, char}
append(&data, digit) or_return
}
}
return Grid{data[:], rows, cols}, nil
}
remove_digits :: proc(
row: []int,
k: int,
allocator := context.allocator,
) -> (
output: []int,
err: mem.Allocator_Error,
) {
stack := make([dynamic]int, 0, len(row) - k, allocator) or_return
dels := k
for digit in row {
for len(stack) > 0 && digit > stack[len(stack) - 1] && dels > 0 {
pop(&stack)
dels -= 1
}
append(&stack, digit) or_return
}
return stack[:], .None
}
process :: proc(row: []int, n: int) -> (output: int, err: mem.Allocator_Error) {
digits := remove_digits(row, len(row) - n) or_return
for digit in digits[:n] {
output = 10 * output + digit
}
return
}
solve :: proc(grid: Grid, n: int) -> (solution: int, err: mem.Allocator_Error) {
context.allocator = context.temp_allocator
bank_solutions := make([]int, grid.rows) or_return
for row in 0 ..< grid.rows {
row_data := grid.data[row * grid.cols:(row + 1) * grid.cols]
bank_solutions[row] = process(row_data, n) or_return
}
for bank in bank_solutions do solution += bank
return
}
part_1 :: proc(grid: Grid) -> (solution: int, err: mem.Allocator_Error) {
return solve(grid, 2)
}
part_2 :: proc(grid: Grid) -> (solution: int, err: mem.Allocator_Error) {
return solve(grid, 12)
}
main :: proc() {
time_start := time.now()
input := string(os.read_entire_file("input") or_else panic("failed to read input"))
time_read := time.since(time_start)
data, parsing_error := parse(input, context.temp_allocator)
defer free_all(context.temp_allocator)
if parsing_error != nil do fmt.panicf("failed to parse: %v\n", parsing_error)
time_parse := time.since(time_start) - time_read
fmt.println("part 1: ", part_1(data) or_else panic("part 1 failed"))
time_solve_1 := time.since(time_start) - time_read - time_parse
fmt.println("part 2: ", part_2(data) or_else panic("part 2 failed"))
time_solve_2 := time.since(time_start) - time_read - time_parse - time_solve_1
time_total := time.since(time_start)
fmt.println("performance benchmarks [ms]:")
fmt.println("total\tread\tparse\tpart 1\tpart 2")
fmt.printf(
"%.3f\t%.3f\t%.3f\t%.3f\t%.3f\n",
time.duration_milliseconds(time_total),
time.duration_milliseconds(time_read),
time.duration_milliseconds(time_parse),
time.duration_milliseconds(time_solve_1),
time.duration_milliseconds(time_solve_2),
)
}