package main import "core:bytes" import "core:fmt" import "core:io" import "core:os" import "core:slice" Item :: struct { list: ^List, number: int, } List :: struct { parent: ^List, items: [dynamic]Item, } // NOTE: Have to free all of Packet parse_input_file :: proc(filepath: string) -> [dynamic]List { 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) packets := make([dynamic]List, 0, 10) line: string err: io.Error for ; err != .EOF; line, err = bytes.buffer_read_string(&buf, '\n') { if line == "" || line == "\n" { continue } line = line[:len(line) - 1] packet: List current: ^List for i := 0; i < len(line[:]); i += 1 { c := line[i] switch (c) { case '[': if current == nil { current = &packet current.items = make([dynamic]Item, 0, 10) } else { list := new(List) list.parent = current list.items = make([dynamic]Item, 0, 10) append(¤t.items, Item{list = list}) current = list } case ']': current = current.parent case '0' ..= '9': num := int(c - '0') if (line[i + 1] >= '0' && line[i + 1] <= '9') { num = (num * 10) + int(line[i + 1] - '0') i += 1 } append(¤t.items, Item{number = num}) case: } } append(&packets, packet) } return packets } free_list :: proc(list: ^List) { for item in list.items { if item.list != nil { free_list(item.list) free(item.list) } } } free_lists :: proc(lists: [dynamic]List) { for _, i in lists { free_list(&lists[i]) } delete(lists) } print_list :: proc(list: []Item) { fmt.printf(" [") for item in list { if item.list != nil { print_list(item.list.items[:]) } else { fmt.printf(" %d ", item.number) } } fmt.printf("] ") } compare_lists :: proc(a, b: []Item, depth := 0, debug := false) -> int { if debug { for _ in 0 ..< depth { fmt.printf(" ") } fmt.printf("- Compare ") print_list(a) fmt.printf(" vs ") print_list(b) fmt.printf("\n") } max_len := len(a) if len(a) < len(b) else len(b) for i in 0 ..< max_len { if a[i].list == nil && b[i].list == nil { x, y := a[i].number, b[i].number if debug { for _ in 0 ..< depth + 1 { fmt.printf(" ") } fmt.printf("- Compare %d vs %d\n", x, y) } if x < y { if debug { for _ in 0 ..< depth + 2 { fmt.printf(" ") } fmt.printf("- Left side is smaller, so inputs are in the right order\n") } return 1 } else if x > y { if debug { for _ in 0 ..< depth + 2 { fmt.printf(" ") } fmt.printf("- Right side is smaller, so inputs are not in the right order\n") } return -1 } } else { aa := a[i].list.items[:] if a[i].list != nil else []Item{{number = a[i].number}} bb := b[i].list.items[:] if b[i].list != nil else []Item{{number = b[i].number}} cmp := compare_lists(aa, bb, depth + 1, debug) if cmp != 0 { return cmp } } } if len(a) < len(b) { if debug { for _ in 0 ..< depth + 1 { fmt.printf(" ") } fmt.printf("- Left side ran out of items, so inputs are in the right order\n") } return 1 } if len(a) > len(b) { if debug { for _ in 0 ..< depth + 1 { fmt.printf(" ") } fmt.printf("- Right side ran out of items, so inputs are not in the right order\n") } return -1 } return 0 } task1 :: proc(packets: []List, debug := false) -> int { result: int num_pairs := len(packets) / 2 for i in 0 ..< num_pairs { packet_a := packets[i * 2].items[:] packet_b := packets[(i * 2) + 1].items[:] if debug { fmt.printf("== Pair %d ==\n", i + 1) } if compare_lists(packet_a, packet_b, 0, debug) == 1 { result += i + 1 if debug { fmt.printf("%d is okay\n", i + 1) } } if debug { fmt.printf("\n") } } return result } task2 :: proc(packets: []List, debug := false) -> int { result := 1 if debug { for packet in packets { print_list(packet.items[:]) fmt.printf("\n") } } slice.sort_by(packets, proc(a, b: List) -> bool { return compare_lists(a.items[:], b.items[:], 0, false) == 1 }) if debug { fmt.printf("===\n") for packet in packets { print_list(packet.items[:]) fmt.printf("\n") } } for packet, i in packets { if len(packet.items) == 1 && packet.items[0].list != nil && len(packet.items[0].list.items) == 1 { item := packet.items[0].list.items[0] if item.number == 2 || item.number == 6 { result *= (i + 1) } } } return result } add_task2_values :: proc(packets: ^[dynamic]List) { { outer := List { items = make([dynamic]Item, 0, 1), } inner := new(List) inner.items = make([dynamic]Item, 0, 1) append(&inner.items, Item{number = 2}) append(&outer.items, Item{list = inner}) append(packets, outer) } { outer := List { items = make([dynamic]Item, 0, 1), } inner := new(List) inner.items = make([dynamic]Item, 0, 1) append(&inner.items, Item{number = 6}) append(&outer.items, Item{list = inner}) append(packets, outer) } } main :: proc() { debug1: bool debug2: bool if len(os.args) > 1 { for arg in os.args { switch arg { case "-d1": debug1 = true case "-d2": debug2 = true } } } packets := parse_input_file("input.txt") defer free_lists(packets) result1 := task1(packets[:], debug1) fmt.printf("Task 1 result: %d\n", result1) add_task2_values(&packets) result2 := task2(packets[:], debug2) fmt.printf("Task 2 result: %d\n", result2) }