package day_09 import "core:fmt" import "core:log" import "core:os" import "core:slice" import "core:strconv" import "core:strings" Input :: struct { bounds: struct { x1, y1, x2, y2: int, }, red_tiles: [][2]int, } Result1 :: int Result2 :: int // --- Input --- // print_input :: proc(input: Input) { b: strings.Builder strings.builder_init(&b) defer strings.builder_destroy(&b) fmt.sbprint(&b, "\n") for y in input.bounds.y1 - 1 ..= input.bounds.y2 + 1 { for x in input.bounds.x1 - 1 ..= input.bounds.x2 + 1 { if slice.contains(input.red_tiles, [2]int{x, y}) { fmt.sbprintf(&b, "\x1b[%d;%dm%c", 31, 41, '#') } else { fmt.sbprintf(&b, "\x1b[%d;%dm%c", 90, 100, '.') } } fmt.sbprint(&b, "\x1b[0m\n") } log.infof("%s", strings.to_string(b)) } parse_input_file :: proc(filepath: string) -> Input { input: Input raw_data, ok := os.read_entire_file_from_filename(filepath) if !ok { panic("oh no, could not read file") } defer delete(raw_data) lines, err := strings.split_lines(strings.trim_space(string(raw_data))) if err != .None { panic("oh no, failed splitting into lines") } defer delete(lines) input.red_tiles = make([][2]int, len(lines)) for line, i in lines { coord := strings.split(line, ",") defer delete(coord) pos := [2]int{strconv.parse_int(coord[0]) or_else 0, strconv.parse_int(coord[1]) or_else 0} input.red_tiles[i] = pos if input.bounds.x1 == 0 || input.bounds.x1 >= pos.x { input.bounds.x1 = pos.x } if input.bounds.x2 == 0 || input.bounds.x2 <= pos.x { input.bounds.x2 = pos.x } if input.bounds.y1 == 0 || input.bounds.y1 >= pos.y { input.bounds.y1 = pos.y } if input.bounds.y2 == 0 || input.bounds.y2 <= pos.y { input.bounds.y2 = pos.y } } return input } free_input :: proc(input: ^Input) { delete(input.red_tiles) } // --- Input --- // // --- Helpers --- // // --- Helpers --- // // --- Task 1 --- // run_task1 :: proc(input: Input, iteration_count: int = 1000) -> Result1 { result: Result1 for i in 0 ..< len(input.red_tiles) { for j in i + 1 ..< len(input.red_tiles) { a := input.red_tiles[i] b := input.red_tiles[j] if a.x == b.x || a.y == b.y { continue } w := (a.x - b.x if a.x > b.x else b.x - a.x) + 1 h := (a.y - b.y if a.y > b.y else b.y - a.y) + 1 area := w * h if area > result { result = area } log.debugf("%v to %v => %d", a, b, area) } } return result } print_result1 :: proc(result: Result1) { log.infof("Task 1: %d", result) } // --- Task 1 --- // // --- Task 2 --- // Edge :: struct { a, b: [2]int, } is_vertical :: proc(e: Edge) -> bool { return e.a.x == e.b.x } is_horizontal :: proc(e: Edge) -> bool { return e.a.y == e.b.y } edge_intersection :: proc(a, b: Edge) -> bool { a_vertical := is_vertical(a) a_horizontal := is_horizontal(a) b_vertical := is_vertical(b) b_horizontal := is_horizontal(b) // Only perpendicular cases can "properly" intersect if a_vertical && b_horizontal { vx := a.a.x vy_min := min(a.a.y, a.b.y) vy_max := max(a.a.y, a.b.y) hy := b.a.y hx_min := min(b.a.x, b.b.x) hx_max := max(b.a.x, b.b.x) // 1) Check if intersection point lies on both segments (inclusive) if vx < hx_min || vx > hx_max { return false } if hy < vy_min || hy > vy_max { return false } // Intersection point ix := vx iy := hy // 2) Check if this point is an endpoint of each segment is_endpoint_a := (ix == a.a.x && iy == a.a.y) || (ix == a.b.x && iy == a.b.y) is_endpoint_b := (ix == b.a.x && iy == b.a.y) || (ix == b.b.x && iy == b.b.y) // If it's a shared endpoint (corner touch), allow it if is_endpoint_a && is_endpoint_b { return false } // Otherwise it's a "real" intersection (including your 2,1–2,5 with 2,3–7,3 case) return true } if a_horizontal && b_vertical { // Just swap roles return edge_intersection(b, a) } // Parallel segments (both vertical or both horizontal) are treated as no crossing. // Overlap along the same line is "on the boundary" and allowed. return false } // Doesn't actually work on actual input // Returns: 518586912 // Should be: 1476550548 run_task2 :: proc(input: Input) -> Result2 { result: Result2 edges := make([]Edge, len(input.red_tiles)) defer delete(edges) current := 0 for i in 0 ..< len(input.red_tiles) { j := (i + 1) % len(input.red_tiles) edges[current] = { a = input.red_tiles[i], b = input.red_tiles[j], } current += 1 } log.debugf("edges: %v", edges) for i in 0 ..< len(input.red_tiles) { for j in i + 1 ..< len(input.red_tiles) { a := input.red_tiles[i] b := input.red_tiles[j] if a.x == b.x || a.y == b.y { continue } x_min, x_max := min(a.x, b.x), max(a.x, b.x) y_min, y_max := min(a.y, b.y), max(a.y, b.y) area := ((x_max - x_min) + 1) * ((y_max - y_min) + 1) if area <= result { continue } A := [2]int{x_min, y_min} B := [2]int{x_min, y_max} C := [2]int{x_max, y_min} D := [2]int{x_max, y_max} AB := Edge { a = A, b = B, } BD := Edge { a = B, b = D, } DC := Edge { a = D, b = C, } CA := Edge { a = C, b = A, } log.debugf("%v %v %v %v", AB, BD, DC, CA) intersects: bool for e in edges { intersects = edge_intersection(AB, e) || edge_intersection(BD, e) || edge_intersection(DC, e) || edge_intersection(CA, e) if intersects { break } } log.debugf("%v to %v => %d%s", a, b, area, " INTERSECTS" if intersects else "") result = area if !intersects else result } } return result } print_result2 :: proc(result: Result2) { log.infof("Task 2: %d", result) } // --- Task 2 --- // run :: proc() { input := parse_input_file("input/day_09.txt") defer free_input(&input) result1 := run_task1(input) print_result1(result1) result2 := run_task2(input) print_result2(result2) }