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