package main import "core:bytes" import "core:fmt" import "core:mem" import "core:io" import "core:os" Area :: struct { data: []u8, w: uint, h: uint, } // NOTE: Have to free Area.data parse_input_file :: proc(filepath: string) -> Area { data, ok := os.read_entire_file_from_filename(filepath) if !ok { panic("oh no, could not read file") } buf: bytes.Buffer bytes.buffer_init(&buf, data) defer bytes.buffer_destroy(&buf) area: Area buffer := make([dynamic]u8, 0, 10) defer delete(buffer) line: string err: io.Error for ; err != .EOF; line, err = bytes.buffer_read_string(&buf, '\n') { if line == "" || line == "\n" { continue } line = line[:len(line) - 1] if area.w == 0 { area.w = len(line) } area.h += 1 append(&buffer, line) } area.data = mem.clone_slice(buffer[:]) return area } print_area :: proc(area: Area) { fmt.printf("\n-=-=-=-=-\n") for c, i in area.data { if c == 'a' - 1 { fmt.printf("S") } else if c == 'z' + 1 { fmt.printf("E") } else { fmt.printf("%c", c) } if uint(i + 1) % area.w == 0 { fmt.printf("\n") } } fmt.printf("-=-=-=-=-\n\n") } print_distances :: proc(values: []int, w: uint) { fmt.printf("\n") for c, i in values { fmt.printf("%2d|", c if c < 999999 else -1) if uint(i + 1) % w == 0 { fmt.printf("\n") } } fmt.printf("\n") } task1 :: proc(area: Area, debug := false) -> int { if debug { print_area(area) } visited := make([]bool, len(area.data)) defer delete(visited) distances := make([]int, len(area.data)) defer delete(distances) start_i, end_i: int for c, i in area.data { if c == 'S' { start_i = i area.data[i] = 'a' } else if c == 'E' { area.data[i] = 'z' end_i = i } distances[i] = 999999 } distances[start_i] = 0 current := start_i for { visited[current] = true neighbors := [4]int{ current - int(area.w) if current >= int(area.w) else -1, current + int(area.w) if current < len(area.data) - int(area.w) else -1, current - 1 if current % int(area.w) > 0 else -1, current + 1 if current % int(area.w) < int(area.w) - 1 else -1, } dist := distances[current] new_dist := dist + 1 for n in neighbors { if n > -1 && area.data[n] <= area.data[current] + 1 { distances[n] = distances[n] if distances[n] < new_dist else new_dist } } least := 999999 next_i := -1 for d, i in distances { if !visited[i] && d < least { least = d next_i = i } } current = next_i if next_i == -1 { break } } if debug { print_area(area) print_distances(distances, area.w) } return distances[end_i] } // NOTE: Simply bruteforces the solution. There are more effecient solutions, I know. task2 :: proc(area: Area, debug := false) -> int { for c, i in area.data { if c == 'S' { area.data[i] = 'a' } } least := 999999 for i in 0 ..< len(area.data) { fmt.printf("%d/%d...", i, len(area.data)) if area.data[i] == 'a' { input := clone_area(area) defer delete(input.data) input.data[i] = 'S' result := task1(input, debug) if result < least { least = result } fmt.printf(" %d!\n", result) } else { fmt.printf(" SKIP!\n") } } return least } clone_area :: proc(area: Area) -> Area { return {data = mem.clone_slice(area.data), w = area.w, h = area.h} } main :: proc() { area := parse_input_file("input.txt") defer delete(area.data) { input := clone_area(area) defer delete(input.data) result1 := task1(input) fmt.printf("Task 1 result: %d\n", result1) } { input := clone_area(area) defer delete(input.data) result2 := task2(input) fmt.printf("Task 2 result: %d\n", result2) } }