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)
}