package main import "core:bytes" import "core:fmt" import "core:io" import "core:math" import "core:os" import "core:slice" Instruction :: [2]byte parse_input_file :: proc(filepath: string, buffer: []byte) -> []Instruction { 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) index: int line: []u8 err: io.Error for ; err != .EOF; line, err = bytes.buffer_read_bytes(&buf, '\n') { if len(line) == 0 { continue } buffer[index] = line[0] val := line[2] - 48 if line[3] >= 48 { val = (val * 10) + line[3] - 48 } buffer[index + 1] = val index += 2 } return transmute([]Instruction)buffer[:index / 2] } Grid :: struct { data: []byte, w: int, h: int, } pretty_print :: proc { pretty_print_basic, pretty_print_with_tail, } pretty_print_basic :: proc(grid: ^Grid) { for char, i in grid.data { if i > 0 && i % grid.w == 0 { fmt.printf("\n") } fmt.printf("%c", char) } fmt.printf("\n") } pretty_print_with_tail :: proc(grid: ^Grid, head: int, tail: []int) { for char, i in grid.data { if i > 0 && i % grid.w == 0 { if math.floor_div(i, grid.w) == 1 { fmt.printf("H:%v", []int{head % grid.w, head / grid.w}) } else if math.floor_div(i, grid.w) - 2 < len(tail) { ti := math.floor_div(i, grid.w) - 2 fmt.printf("%d:%v", ti + 1, []int{tail[ti] % grid.w, tail[ti] / grid.w}) } fmt.printf("\n") } fmt.printf("%c", char) } fmt.printf("\n\n") } is_touching :: proc(head, tail: int, playfield: ^Grid) -> bool { return( (head >= tail - playfield.w - 1 && head <= tail - playfield.w + 1) || (head >= tail - 1 && head <= tail + 1) || (head >= tail + playfield.w - 1 && head <= tail + playfield.w + 1) \ ) } get_new_tail :: proc(head, tail: int, playfield: ^Grid) -> int { cardinals := []int{-playfield.w, playfield.w, -1, 1} diagonals := []int{-playfield.w, +playfield.w, -1, 1} for c in cardinals { if head == tail + (c * 2) { return tail + c } } for d in diagonals { if head == tail + (d * 2) - 1 { return tail + d - 1 } if head == tail + (d * 2) + 1 { return tail + d + 1 } if head == tail + (d * 2) - playfield.w { return tail + d - playfield.w } if head == tail + (d * 2) + playfield.w { return tail + d + playfield.w } if head == tail + (d * 2) - 2 { return tail + d - 1 } if head == tail + (d * 2) + 2 { return tail + d + 1 } } fmt.printf("head %d %v\n", head, [2]int{head % playfield.w, head / playfield.w}) fmt.printf("tail %d %v\n", tail, [2]int{tail % playfield.w, tail / playfield.w}) panic("unreachable!") } task1 :: proc(instructions: []Instruction, playfield: ^Grid, debug := false) -> int { result := 1 slice.fill(playfield.data, '.') head := (playfield.w * (playfield.h / 2)) + (playfield.w / 2) tail := head for instruction in instructions { step: int step = -playfield.w if instruction[0] == 'U' else step step = playfield.w if instruction[0] == 'D' else step step = -1 if instruction[0] == 'L' else step step = 1 if instruction[0] == 'R' else step for _ in 0 ..< instruction[1] { head += step if !is_touching(head, tail, playfield) { tail = get_new_tail(head, tail, playfield) if playfield.data[tail] != 'T' { result += 1 } } playfield.data[tail] = 'T' } } if debug { pretty_print(playfield) } return result } task2 :: proc(instructions: []Instruction, playfield: ^Grid, debug := false) -> int { result := 1 slice.fill(playfield.data, '.') head := (playfield.w * (playfield.h / 2)) + (playfield.w / 2) tail := [9]int{head, head, head, head, head, head, head, head, head} for instruction in instructions { step: int step = -playfield.w if instruction[0] == 'U' else step step = playfield.w if instruction[0] == 'D' else step step = -1 if instruction[0] == 'L' else step step = 1 if instruction[0] == 'R' else step for _ in 0 ..< instruction[1] { head += step prev_index := head for _, i in tail { tail_byte := byte('1' + i) if !is_touching(prev_index, tail[i], playfield) { tail[i] = get_new_tail(prev_index, tail[i], playfield) if i + 1 == 9 && playfield.data[tail[i]] != '9' { result += 1 } } if i == len(tail) - 1 { playfield.data[tail[i]] = tail_byte } prev_index = tail[i] } } } if debug { pretty_print(playfield) } return result } main :: proc() { buffer: [16383]byte instructions := parse_input_file("input.txt", buffer[:]) width :: 1024 playfield_data: [width * width]byte playfield := Grid { data = playfield_data[:], w = width, h = width, } result1 := task1(instructions, &playfield) fmt.printf("Task 1 result: %d\n", result1) result2 := task2(instructions, &playfield) fmt.printf("Task 2 result: %d\n", result2) }