package main import "core:bytes" import "core:fmt" import "core:io" import "core:os" import "core:strconv" import "core:strings" Op :: enum { Add, Mul, } Monkey :: struct { items: [dynamic]uint, new_op: Op, new_arg: uint, new_arg_old: bool, test: uint, if_true_index: int, if_false_index: int, num_inspected: int, } // NOTE: Have to free result. parse_input_file :: proc(filepath: string) -> [dynamic]Monkey { 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) monkeys := make([dynamic]Monkey, 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] monkey: Monkey monkey.items = make([dynamic]uint, 0, 2) line, err = bytes.buffer_read_string(&buf, '\n') line = line[:len(line) - 1] { item_index := strings.last_index_byte(line, ':') + 1 items_split := strings.split(line[item_index:], ",") for id in items_split { append(&monkey.items, uint(strconv.atoi(id[1:]))) } delete(items_split) } line, err = bytes.buffer_read_string(&buf, '\n') line = line[:len(line) - 1] { if strings.last_index_byte(line, '+') > 0 { monkey.new_op = .Add } else if strings.last_index_byte(line, '*') > 0 { monkey.new_op = .Mul } arg_index := strings.last_index_byte(line, ' ') + 1 if line[arg_index:] != "old" { monkey.new_arg = uint(strconv.atoi(line[arg_index:])) } else { monkey.new_arg_old = true } } line, err = bytes.buffer_read_string(&buf, '\n') line = line[:len(line) - 1] { index := strings.last_index_byte(line, ' ') + 1 monkey.test = uint(strconv.atoi(line[index:])) } line, err = bytes.buffer_read_string(&buf, '\n') line = line[:len(line) - 1] { index := strings.last_index_byte(line, ' ') + 1 monkey.if_true_index = strconv.atoi(line[index:]) } line, err = bytes.buffer_read_string(&buf, '\n') line = line[:len(line) - 1] { index := strings.last_index_byte(line, ' ') + 1 monkey.if_false_index = strconv.atoi(line[index:]) } _, err = bytes.buffer_read_string(&buf, '\n') append(&monkeys, monkey) } return monkeys } print_monkey_items :: proc(monkeys: []Monkey) { for monkey, i in monkeys { fmt.printf("Monkey %d: %v\n", i, monkey.items) } } task1 :: proc(monkeys: []Monkey, debug := false) -> int { if debug { print_monkey_items(monkeys) } for round in 1 ..= 20 { for monkey, monkey_index in monkeys { if debug { fmt.printf("Monkey %d:\n", monkey_index) } for item in monkey.items { if debug { fmt.printf("\tMonkey inspects an item with a worry level of %d.\n", item) } worry: uint if monkey.new_op == .Add { worry = item + (monkey.new_arg if !monkey.new_arg_old else item) } else if monkey.new_op == .Mul { worry = item * (monkey.new_arg if !monkey.new_arg_old else item) } if debug { fmt.printf( "\t\tWorry level is %s by %d to %d.\n", monkey.new_op, monkey.new_arg if !monkey.new_arg_old else item, worry, ) } worry /= 3 if debug { fmt.printf( "\t\tMonkey gets bored with item. Worry level is divided by 3 to %d.\n", worry, ) } throw_to: int if worry % monkey.test == 0 { if debug { fmt.printf("\t\tCurrent worry level is divisible by %d.\n", monkey.test) } throw_to = monkey.if_true_index } else { if debug { fmt.printf( "\t\tCurrent worry level is not divisible by %d.\n", monkey.test, ) } throw_to = monkey.if_false_index } if debug { fmt.printf( "\t\tItem with worry level %d is thrown to monkey %d.\n", worry, throw_to, ) } append(&monkeys[throw_to].items, worry) } monkeys[monkey_index].num_inspected += len(monkeys[monkey_index].items) delete(monkeys[monkey_index].items) monkeys[monkey_index].items = make([dynamic]uint, 0, 2) } if debug { fmt.printf("\nRound %d\n", round) print_monkey_items(monkeys) } } if debug { fmt.printf("\n") } first, second: int for monkey, i in monkeys { if debug { fmt.printf("Monkey %d inspected items %d times\n", i, monkey.num_inspected) } if monkey.num_inspected > first { second, first = first, monkey.num_inspected } else if monkey.num_inspected > second { second = monkey.num_inspected } } return first * second } task2 :: proc(monkeys: []Monkey, debug := false) -> int { if debug { print_monkey_items(monkeys) } mod_constant: uint = 1 for monkey, monkey_index in monkeys { mod_constant *= monkey.test } for round in 1 ..= 10000 { for monkey, monkey_index in monkeys { if debug { fmt.printf("Monkey %d:\n", monkey_index) } for item in monkey.items { if debug { fmt.printf("\tMonkey inspects an item with a worry level of %d.\n", item) } worry: uint if monkey.new_op == .Add { worry = item + (monkey.new_arg if !monkey.new_arg_old else item) } else if monkey.new_op == .Mul { worry = item * (monkey.new_arg if !monkey.new_arg_old else item) } if debug { fmt.printf( "\t\tWorry level is %s by %d to %d.\n", monkey.new_op, monkey.new_arg if !monkey.new_arg_old else item, worry, ) } worry %= mod_constant throw_to: int if worry % monkey.test == 0 { if debug { fmt.printf("\t\tCurrent worry level is divisible by %d.\n", monkey.test) } throw_to = monkey.if_true_index } else { if debug { fmt.printf( "\t\tCurrent worry level is not divisible by %d.\n", monkey.test, ) } throw_to = monkey.if_false_index } if debug { fmt.printf( "\t\tItem with worry level %d is thrown to monkey %d.\n", worry, throw_to, ) } append(&monkeys[throw_to].items, worry) } monkeys[monkey_index].num_inspected += len(monkeys[monkey_index].items) delete(monkeys[monkey_index].items) monkeys[monkey_index].items = make([dynamic]uint, 0, 2) } if debug { fmt.printf("\nRound %d\n", round) print_monkey_items(monkeys) } if round == 1 || round == 20 || round % 1000 == 0 { fmt.printf("\nAfter round %d\n", round) for monkey, i in monkeys { fmt.printf("Monkey %d inspected items %d times\n", i, monkey.num_inspected) } } } if debug { fmt.printf("\n") } first, second: int for monkey, i in monkeys { if debug { fmt.printf("Monkey %d inspected items %d times\n", i, monkey.num_inspected) } if monkey.num_inspected > first { second, first = first, monkey.num_inspected } else if monkey.num_inspected > second { second = monkey.num_inspected } } return first * second } free_monkeys :: proc(monkeys: []Monkey) { for monkey in monkeys { delete(monkey.items) } } main :: proc() { { monkeys := parse_input_file("input.txt") defer delete(monkeys) defer free_monkeys(monkeys[:]) result1 := task1(monkeys[:]) fmt.printf("Task 1 result: %d\n", result1) } { monkeys := parse_input_file("input.txt") defer delete(monkeys) defer free_monkeys(monkeys[:]) result2 := task2(monkeys[:]) fmt.printf("Task 2 result: %d\n", result2) } }