package main import "core:unicode/utf8" import "core:math" import "core:slice" import "core:strconv" import "core:strings" import "core:bytes" import "core:fmt" import "core:os" import "../util" is_inside_boundries :: proc(row_idx, col_idx: int, layout: [][]rune) -> bool { return col_idx >= 0 && col_idx < len(layout[0]) && row_idx >= 0 && row_idx < len(layout) } get_adjacent_occupied :: proc(row_idx, col_idx: int, layout: [][]rune) -> i64 { adjacent_occupied: i64 = 0 for adjacent_row_idx in -1..=1 { for adjacent_col_idx in -1..=1 { if adjacent_col_idx == 0 && adjacent_row_idx == 0 do continue neighbour_row_idx := row_idx + adjacent_row_idx neighbour_col_idx := col_idx + adjacent_col_idx if is_inside_boundries(neighbour_row_idx, neighbour_col_idx, layout) && layout[neighbour_row_idx][neighbour_col_idx] == '#' { adjacent_occupied += 1 } } } return adjacent_occupied } get_adjacent_visible :: proc(row_idx, col_idx: int, layout: [][]rune) -> i64 { visible_occupied: i64 = 0 directions := [8]struct{dr, dc: int}{ {-1, -1}, {-1, 0}, {-1, 1}, { 0, -1}, { 0, 1}, { 1, -1}, { 1, 0}, { 1, 1}, } for dir in directions { r, c := row_idx + dir.dr, col_idx + dir.dc for is_inside_boundries(r, c, layout) { tile := layout[r][c] if tile == '#' { visible_occupied += 1 break } else if tile == 'L' { break } r += dir.dr c += dir.dc } } return visible_occupied } part_1 :: proc(lines: []string) { result: i64 = 0 layout := slice.mapper(lines, proc(line: string) -> []rune { return utf8.string_to_runes(line) }) changed := true for changed { new_layout := slice.clone(layout) for row, row_idx in layout do new_layout[row_idx] = slice.clone(row) changed = false for row, row_idx in layout { for tile, col_idx in row { if tile == '#' { adjacent_occupied := get_adjacent_occupied(row_idx, col_idx, layout) if adjacent_occupied >= 4 { new_layout[row_idx][col_idx] = 'L' changed = true } } else if tile == 'L' { adjacent_occupied := get_adjacent_occupied(row_idx, col_idx, layout) if adjacent_occupied == 0 { new_layout[row_idx][col_idx] = '#' changed = true } } } } layout = new_layout } result = slice.reduce(layout, i64(0), proc(res: i64, row: []rune) -> i64 { return res + i64(slice.count(row, '#')) }) fmt.printfln("Part 1: %d", result) } part_2 :: proc(lines: []string) { result: i64 = 0 layout := slice.mapper(lines, proc(line: string) -> []rune { return utf8.string_to_runes(line) }) changed := true for changed { new_layout := slice.clone(layout) for row, row_idx in layout do new_layout[row_idx] = slice.clone(row) changed = false for row, row_idx in layout { for tile, col_idx in row { if tile == '#' { adjacent_occupied := get_adjacent_visible(row_idx, col_idx, layout) if adjacent_occupied >= 5 { new_layout[row_idx][col_idx] = 'L' changed = true } } else if tile == 'L' { adjacent_occupied := get_adjacent_visible(row_idx, col_idx, layout) if adjacent_occupied == 0 { new_layout[row_idx][col_idx] = '#' changed = true } } } } layout = new_layout } result = slice.reduce(layout, i64(0), proc(res: i64, row: []rune) -> i64 { return res + i64(slice.count(row, '#')) }) fmt.printfln("Part 2: %d", result) } main :: proc() { context.allocator = context.temp_allocator defer free_all(context.temp_allocator) INPUT :: #load("input.txt", string) lines := strings.split(INPUT, "\n") lines = lines[:len(lines)-1] part_1(lines) part_2(lines) }