diff --git a/d04/main.odin b/d04/main.odin new file mode 100644 index 0000000..5525fed --- /dev/null +++ b/d04/main.odin @@ -0,0 +1,161 @@ +package main + +import "core:math" +import "core:slice" +import "core:strconv" +import "core:strings" +import "core:bytes" +import "core:fmt" +import "core:os" + +import "../util" + +map_batch :: proc(batch: []string) -> map[string]string { + batch_map := make(map[string]string) + for line in batch { + parts := strings.split(line, " ") + for part in parts { + entry := strings.split(part, ":") + batch_map[entry[0]] = entry[1] + } + } + + return batch_map +} + +EXPECTED_FIELDS :: []string{ + "byr", + "iyr", + "eyr", + "hgt", + "hcl", + "ecl", + "pid", +} + +part_1 :: proc(lines: []string) { + result := 0 + + batches := make([dynamic]map[string]string) + batch: []string + + batch_idx := 0 + for line, idx in lines { + if len(line) == 0 { + batch := lines[batch_idx:idx] + append(&batches, map_batch(batch)) + batch_idx = idx + 1 + } + } + + batch_loop: for batch in batches { + for field in EXPECTED_FIELDS { + _, ok := batch[field] + if !ok do continue batch_loop + } + + result += 1 + } + + fmt.printfln("Part 1: %d", result) +} + +part_2 :: proc(lines: []string) { + result := 0 + + batches := make([dynamic]map[string]string) + batch: []string + + batch_idx := 0 + for line, idx in lines { + if len(line) == 0 { + batch := lines[batch_idx:idx] + append(&batches, map_batch(batch)) + batch_idx = idx + 1 + } + } + + batch_loop: for batch in batches { + for field in EXPECTED_FIELDS { + val, ok := batch[field] + if !ok do continue batch_loop + + switch field { + case "byr": + if len(val) != 4 do continue batch_loop + year, ok := strconv.parse_int(val); assert(ok) + if year < 1920 || year > 2002 do continue batch_loop + + case "iyr": + if len(val) != 4 do continue batch_loop + year, ok := strconv.parse_int(val); assert(ok) + if year < 2010 || year > 2020 do continue batch_loop + + case "eyr": + if len(val) != 4 do continue batch_loop + year, ok := strconv.parse_int(val); assert(ok) + if year < 2020 || year > 2030 do continue batch_loop + + case "hgt": + if len(val) < 3 do continue batch_loop + unit := val[len(val)-2:] + length, ok := strconv.parse_int(val[:len(val)-2]); assert(ok) + switch unit { + case "cm": + if length < 150 || length > 193 do continue batch_loop + case "in": + if length < 59 || length > 76 do continue batch_loop + case: continue batch_loop + } + + case "hcl": + if len(val) != 7 do continue batch_loop + if val[0] != '#' do continue batch_loop + for letter in val[1:] { + switch letter { + case '0'..='9': continue + case 'a'..='f': continue + case: continue batch_loop + } + } + + case "ecl": + EXPECTED_COLORS :: []string{ + "amb", + "blu", + "brn", + "gry", + "grn", + "hzl", + "oth", + } + if !slice.contains(EXPECTED_COLORS, val) do continue batch_loop + + case "pid": + if len(val) != 9 do continue batch_loop + for letter in val { + switch letter { + case '0'..='9': continue + case: continue batch_loop + } + } + } + } + + result += 1 + } + + 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) +}