├── 10 ├── .gitignore └── main.jai ├── 11 ├── .gitignore └── main.jai ├── 12 ├── .gitignore └── main.jai ├── 13 ├── .gitignore └── main.jai ├── 14 ├── .gitignore └── main.jai ├── 15 ├── .gitignore └── main.jai ├── 16 └── main.py ├── 01 ├── .gitignore └── main.jai ├── 02 ├── .gitignore └── main.jai ├── 03 ├── .gitignore └── main.jai ├── 04 ├── .gitignore └── main.jai ├── 05 ├── .gitignore ├── main.jai └── data.jai ├── 06 ├── .gitignore └── main.jai ├── 07 ├── .gitignore └── main.jai ├── 08 ├── .gitignore └── main.jai ├── 09 ├── .gitignore └── main.jai └── LICENSE /01/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /02/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /03/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /04/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /05/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /06/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /07/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /08/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /09/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /10/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /11/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /12/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /13/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /14/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /15/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | .build/ 3 | main -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Alexey Kutepov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /01/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "File"; 3 | #import "String"; 4 | #import "Sort"; 5 | 6 | parse_input :: (content: string) -> []int { 7 | sum := 0; 8 | sums : [..]int; 9 | found : bool; 10 | left : string; 11 | right := content; 12 | while right.count > 0 { 13 | found, left, right = split_from_left(right, #char "\n"); 14 | if left.count > 0 { 15 | sum += string_to_int(left); 16 | } else { 17 | array_add(*sums, sum); 18 | sum = 0; 19 | } 20 | } 21 | if sum > 0 then array_add(*sums, sum); 22 | return sums; 23 | } 24 | 25 | part_1 :: (file_path: string) { 26 | content, ok := read_entire_file(file_path); 27 | if !ok { 28 | print("ERROR: could not read file: %", file_path); 29 | exit(1); 30 | } 31 | ans := 0; 32 | sums := parse_input(content); 33 | for sums if it > ans then ans = it; 34 | print("Part 1: %: %\n", file_path, ans); 35 | } 36 | 37 | part_2 :: (file_path: string) { 38 | content, ok := read_entire_file(file_path); 39 | if !ok { 40 | print("ERROR: could not read file: %", file_path); 41 | exit(1); 42 | } 43 | sums := parse_input(content); 44 | quick_sort(sums, (a: int, b: int) -> int { 45 | return b - a; 46 | }); 47 | assert(sums.count >= 3); 48 | ans := 0; 49 | for 0..2 ans += sums[it]; 50 | print("Part 2: %: %\n", file_path, ans); 51 | } 52 | 53 | main :: () { 54 | part_1("sample.txt"); 55 | part_1("input.txt"); 56 | part_2("sample.txt"); 57 | part_2("input.txt"); 58 | } 59 | -------------------------------------------------------------------------------- /06/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "String"; 3 | #import "File"; 4 | 5 | Marker :: struct(capacity: int) { 6 | items : [capacity]u8; 7 | begin, count : int; 8 | }; 9 | 10 | marker_string :: (using marker: Marker) -> string { 11 | sb : String_Builder; 12 | for 0..count-1 { 13 | append(*sb, items[(it + begin)%items.count]); 14 | } 15 | return builder_to_string(*sb); 16 | } 17 | 18 | marker_add :: (using marker : *Marker, char: u8) { 19 | items[(begin + count)%items.count] = char; 20 | if count < items.count { 21 | count += 1; 22 | } else { 23 | begin = (begin + 1)%items.count; 24 | } 25 | } 26 | 27 | marker_is_start :: (using marker : Marker) -> bool { 28 | if count < items.count { 29 | return false; 30 | } 31 | for i: 0..count-2 { 32 | for j: i+1..count-1 { 33 | if items[(begin + i)%items.count] == items[(begin + j)%items.count] { 34 | return false; 35 | } 36 | } 37 | } 38 | 39 | return true; 40 | } 41 | 42 | find_start :: ($capacity: int, message: string) -> int { 43 | marker : Marker(capacity); 44 | count := 0; 45 | for 0..message.count-1 { 46 | if marker_is_start(marker) { 47 | return count; 48 | } 49 | marker_add(*marker, message[it]); 50 | count += 1; 51 | } 52 | assert(false, "unreachable"); 53 | return 0; 54 | } 55 | 56 | main :: () { 57 | print("Samples:\n"); 58 | samples : []string = .[ 59 | "mjqjpqmgbljsphdztnvjfqwrcgsmlb", 60 | "bvwbjplbgvbhsrlpgdmjqwftvncz", 61 | "nppdvjthqldpwncqszvftbrmjlhg", 62 | "nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg", 63 | "zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw", 64 | ]; 65 | for samples { 66 | print(" % => %\n", it, find_start(4, it)); 67 | } 68 | content, ok := read_entire_file("input.txt"); 69 | assert(ok); 70 | print("Part 1: %\n", find_start(4, content)); 71 | print("Part 2: %\n", find_start(14, content)); 72 | } 73 | -------------------------------------------------------------------------------- /05/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "File"; 3 | #import "String"; 4 | #load "data.jai"; 5 | 6 | make_state :: (rows: []string) -> [][..]u8 { 7 | result : [..][..]u8; 8 | for row: rows { 9 | column : [..]u8; 10 | for 0..row.count-1 { 11 | array_add(*column, row[it]); 12 | } 13 | array_add(*result, column); 14 | } 15 | return result; 16 | } 17 | 18 | apply_moves1 :: (state: [][..]u8, moves: [][3]int) { 19 | for move: moves { 20 | for 0..move[0]-1 { 21 | src := *state[move[1]-1]; 22 | dst := *state[move[2]-1]; 23 | assert(src.count > 0); 24 | array_add(dst, (<= count); 36 | for src.count - count..src.count-1 { 37 | array_add(dst, (< string { 44 | builder: String_Builder; 45 | for state { 46 | append(*builder, it[it.count - 1]); 47 | } 48 | return builder_to_string(*builder); 49 | } 50 | 51 | part_1 :: (init_state: []string, moves: [][3]int) -> string { 52 | state := make_state(init_state); 53 | apply_moves1(state, moves); 54 | return get_message(state); 55 | } 56 | 57 | part_2 :: (init_state: []string, moves: [][3]int) -> string { 58 | state := make_state(init_state); 59 | apply_moves2(state, moves); 60 | return get_message(state); 61 | } 62 | 63 | main :: () { 64 | print("part 1: sample: %\n", part_1(sample_state, sample_moves)); 65 | print("part 1: input: %\n", part_1(input_state, input_moves)); 66 | print("part 2: sample: %\n", part_2(sample_state, sample_moves)); 67 | print("part 2: input: %\n", part_2(input_state, input_moves)); 68 | } 69 | -------------------------------------------------------------------------------- /02/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "File"; 3 | #import "String"; 4 | 5 | Outcome :: enum int { 6 | LOSE :: 0; 7 | DRAW :: 1; 8 | WIN :: 2; 9 | } 10 | 11 | Shape :: enum int { 12 | ROCK :: 0; 13 | PAPER :: 1; 14 | SCISSORS :: 2; 15 | } 16 | 17 | parse_input :: (content: string) -> [][2]int { 18 | input : [..][2]int; 19 | found: bool; 20 | left: string; 21 | right := content; 22 | score := 0; 23 | while right.count > 0 { 24 | found, left, right = split_from_left(right, #char "\n"); 25 | assert(left.count == 3); 26 | row : [2]int; 27 | row[0] = left[0] - #char "A"; 28 | row[1] = left[2] - #char "X"; 29 | array_add(*input, row); 30 | } 31 | return input; 32 | } 33 | 34 | part_1 :: (file_path: string) { 35 | PLAY : [3][3]Outcome : .[ 36 | // Rock Paper Scissors 37 | .[.DRAW, .WIN, .LOSE ], // Rock 38 | .[.LOSE, .DRAW, .WIN], // Paper 39 | .[.WIN, .LOSE, .DRAW], // Scissors 40 | ]; 41 | 42 | content, ok := read_entire_file(file_path); 43 | assert(ok); 44 | input := parse_input(content); 45 | score := 0; 46 | for 0..input.count-1 { 47 | opponent := input[it][0]; 48 | you := input[it][1]; 49 | score += cast(int)PLAY[opponent][you]*3 + you + 1; 50 | } 51 | print("Part 1: %: %\n", file_path, score); 52 | } 53 | 54 | part_2 :: (file_path: string) { 55 | PLAY : [3][3]Shape : .[ 56 | // Lose Draw Win 57 | .[.SCISSORS, .ROCK, .PAPER], // Rock 58 | .[.ROCK, .PAPER, .SCISSORS], // Paper 59 | .[.PAPER, .SCISSORS, .ROCK], // Scissors 60 | ]; 61 | 62 | content, ok := read_entire_file(file_path); 63 | assert(ok); 64 | input := parse_input(content); 65 | score := 0; 66 | for 0..input.count-1 { 67 | opponent := input[it][0]; 68 | outcome := input[it][1]; 69 | score += outcome*3 + cast(int)PLAY[opponent][outcome] + 1; 70 | } 71 | print("Part 2: %: %\n", file_path, score); 72 | } 73 | 74 | main :: () { 75 | part_1("sample.txt"); 76 | part_1("input.txt"); 77 | part_2("sample.txt"); 78 | part_2("input.txt"); 79 | } 80 | -------------------------------------------------------------------------------- /04/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "File"; 3 | #import "String"; 4 | 5 | Range :: struct { 6 | low, high: int; 7 | } 8 | 9 | parse_range :: (content: string) -> Range { 10 | found, left, right := split_from_left(content, #char "-"); 11 | return .{ 12 | low = string_to_int(left), 13 | high = string_to_int(right), 14 | }; 15 | } 16 | 17 | parse_range_pair :: (content: string) -> [2]Range { 18 | found, left, right := split_from_left(content, #char ","); 19 | result : [2]Range; 20 | result[0] = parse_range(left); 21 | result[1] = parse_range(right); 22 | return result; 23 | } 24 | 25 | parse_input :: (content: string) -> [][2]Range { 26 | input : [..][2]Range; 27 | found: bool; 28 | left: string; 29 | right := content; 30 | while right.count { 31 | found, left, right = split_from_left(right, #char "\n"); 32 | array_add(*input, parse_range_pair(left)); 33 | } 34 | return input; 35 | } 36 | 37 | range_contains_another :: (range1: Range, range2: Range) -> bool { 38 | return range1.low <= range2.low && range2.high <= range1.high; 39 | } 40 | 41 | part_1 :: (file_path: string) { 42 | content, ok := read_entire_file(file_path); 43 | assert(ok); 44 | input := parse_input(content); 45 | count := 0; 46 | for parse_input(content) { 47 | if range_contains_another(it[0], it[1]) || range_contains_another(it[1], it[0]) { 48 | count += 1; 49 | } 50 | } 51 | print("Part 1: %: %\n", file_path, count); 52 | } 53 | 54 | range_overlaps_another :: (range1: Range, range2: Range) -> bool { 55 | return range1.low <= range2.low && range2.low <= range1.high; 56 | } 57 | 58 | part_2 :: (file_path: string) { 59 | content, ok := read_entire_file(file_path); 60 | assert(ok); 61 | input := parse_input(content); 62 | count := 0; 63 | for parse_input(content) { 64 | if range_overlaps_another(it[0], it[1]) || range_overlaps_another(it[1], it[0]) { 65 | count += 1; 66 | } 67 | } 68 | print("Part 2: %: %\n", file_path, count); 69 | } 70 | 71 | main :: () { 72 | part_1("sample.txt"); 73 | part_1("input.txt"); 74 | part_2("sample.txt"); 75 | part_2("input.txt"); 76 | } 77 | -------------------------------------------------------------------------------- /03/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "File"; 3 | #import "String"; 4 | 5 | parse_input :: (content: string) -> []string { 6 | input : [..]string; 7 | found: bool; 8 | left: string; 9 | right := content; 10 | score := 0; 11 | while right.count > 0 { 12 | found, left, right = split_from_left(right, #char "\n"); 13 | array_add(*input, left); 14 | } 15 | return input; 16 | } 17 | 18 | priority :: (x: int) -> int { 19 | if #char "a" <= x && x <= #char "z" return x - #char "a" + 1; 20 | if #char "A" <= x && x <= #char "Z" return x - #char "A" + 27; 21 | assert(false, "unreachable"); 22 | return 0; 23 | } 24 | 25 | part_1 :: (file_path: string) { 26 | content, ok := read_entire_file(file_path); 27 | assert(ok); 28 | lines := parse_input(content); 29 | ans := 0; 30 | for 0..lines.count-1 { 31 | half_line := lines[it].count/2; 32 | left := slice(lines[it], 0, half_line); 33 | right := slice(lines[it], half_line, half_line); 34 | count_common := 0; 35 | for 0..half_line-1 { 36 | index := find_index_from_left(right, left[it]); 37 | if index >= 0 { 38 | ans += priority(left[it]); 39 | break; 40 | } 41 | } 42 | } 43 | print("Part 1: %: %\n", file_path, ans); 44 | } 45 | 46 | rucksack_stats :: (items: string) -> [52]int { 47 | result : [52]int; 48 | for 0..items.count-1 { 49 | result[priority(items[it])-1] += 1; 50 | } 51 | return result; 52 | } 53 | 54 | part_2 :: (file_path : string) { 55 | content, ok := read_entire_file(file_path); 56 | assert(ok); 57 | lines := parse_input(content); 58 | ans := 0; 59 | for 0..lines.count/3-1 { 60 | r1 := rucksack_stats(lines[it*3+0]); 61 | r2 := rucksack_stats(lines[it*3+1]); 62 | r3 := rucksack_stats(lines[it*3+2]); 63 | for 0..51 { 64 | if r1[it] > 0 && r2[it] > 0 && r3[it] > 0 { 65 | ans += it + 1; 66 | break; 67 | } 68 | } 69 | } 70 | print("Part 2: %: %\n", file_path, ans); 71 | } 72 | 73 | main :: () { 74 | part_1("sample.txt"); 75 | part_1("input.txt"); 76 | part_2("sample.txt"); 77 | part_2("input.txt"); 78 | } 79 | -------------------------------------------------------------------------------- /08/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "String"; 3 | #import "File"; 4 | 5 | parse_input :: (content: string) -> []string { 6 | result: [..]string; 7 | found: bool; 8 | left: string; 9 | right := content; 10 | while right.count > 0 { 11 | found, left, right = split_from_left(right, #char "\n"); 12 | array_add(*result, left); 13 | } 14 | return result; 15 | } 16 | 17 | is_out_of_bounds :: (grid: []string, x: int, y: int) -> bool { 18 | return !(0 <= y && y < grid.count && 0 <= x && x < grid[0].count); 19 | } 20 | 21 | viewing_distance_in_that_direction :: (grid: []string, x: int, y: int, dx: int, dy: int) -> int { 22 | value := grid[y][x]; 23 | count := 0; 24 | x += dx; 25 | y += dy; 26 | while !is_out_of_bounds(grid, x, y) { 27 | count += 1; 28 | if grid[y][x] >= value return count; 29 | x += dx; 30 | y += dy; 31 | } 32 | return count; 33 | } 34 | 35 | all_smaller_in_that_direction :: (grid: []string, x: int, y: int, dx: int, dy: int) -> bool { 36 | value := grid[y][x]; 37 | x += dx; 38 | y += dy; 39 | while !is_out_of_bounds(grid, x, y) { 40 | if grid[y][x] >= value return false; 41 | x += dx; 42 | y += dy; 43 | } 44 | return true; 45 | } 46 | 47 | directions : [][2]int = .[ 48 | .[-1, 0], 49 | .[1, 0], 50 | .[0, 1], 51 | .[0, -1], 52 | ]; 53 | 54 | is_visible :: (grid: []string, x: int, y: int) -> bool { 55 | for d: directions { 56 | if all_smaller_in_that_direction(grid, x, y, d[0], d[1]) return true; 57 | } 58 | return false; 59 | } 60 | 61 | part1 :: (file_path: string) { 62 | content, ok := read_entire_file(file_path); 63 | assert(ok); 64 | grid := parse_input(content); 65 | count := 0; 66 | for y: 1..grid.count-2 { 67 | for x: 1..grid[0].count-2 { 68 | if is_visible(grid, x, y) { 69 | count += 1; 70 | } 71 | } 72 | } 73 | print("Part 1: %: %\n", file_path, count + grid.count*2 + (grid[0].count-2)*2); 74 | } 75 | 76 | scenic_score :: (grid: []string, x: int, y: int) -> int { 77 | score := 1; 78 | for d: directions { 79 | score *= viewing_distance_in_that_direction(grid, x, y, d[0], d[1]); 80 | } 81 | return score; 82 | } 83 | 84 | part2 :: (file_path: string) { 85 | content, ok := read_entire_file(file_path); 86 | assert(ok); 87 | grid := parse_input(content); 88 | best_score := 0; 89 | for y: 0..grid.count-1 { 90 | for x: 0..grid[0].count-1 { 91 | score := scenic_score(grid, x, y); 92 | best_score = max(best_score, score); 93 | } 94 | } 95 | print("Part 2: %: %\n", file_path, best_score); 96 | } 97 | 98 | main :: () { 99 | part1("sample.txt"); 100 | part1("input.txt"); 101 | part2("sample.txt"); 102 | part2("input.txt"); 103 | } 104 | -------------------------------------------------------------------------------- /09/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "File"; 3 | #import "String"; 4 | #import "Math"; 5 | #import "Sort"; 6 | 7 | Motion :: struct { 8 | direction : enum { LEFT :: 0; RIGHT :: 1; UP :: 2; DOWN :: 3; }; 9 | length : int; 10 | } 11 | 12 | directions : [4]Vec2 : .[.{-1, 0}, .{1, 0}, .{0, -1}, .{0, 1}]; 13 | 14 | Vec2 :: struct { 15 | x, y: int; 16 | } 17 | 18 | operator == :: (a: Vec2, b: Vec2) -> bool { 19 | return a.x == b.x && a.y == b.y; 20 | } 21 | 22 | operator + :: (a: Vec2, b: Vec2) -> Vec2 { 23 | return .{ 24 | a.x + b.x, 25 | a.y + b.y, 26 | }; 27 | } 28 | 29 | parse_input :: (content : string) -> []Motion { 30 | result : [..]Motion; 31 | left: string; 32 | found: bool; 33 | right := content; 34 | while right.count > 0 { 35 | found, left, right = split_from_left(right, #char "\n"); 36 | motion : Motion; 37 | if left[0] == { 38 | case #char "L"; motion.direction = .LEFT; 39 | case #char "R"; motion.direction = .RIGHT; 40 | case #char "U"; motion.direction = .UP; 41 | case #char "D"; motion.direction = .DOWN; 42 | case; assert(false, "unreachable"); 43 | } 44 | motion.length = string_to_int(slice(left, 2, left.count - 2)); 45 | array_add(*result, motion); 46 | } 47 | return result; 48 | } 49 | 50 | sign :: (x: int) -> int { 51 | return cast(int)(x > 0) - cast(int)(x < 0); 52 | } 53 | 54 | adjust_tail :: (head: Vec2, tail: *Vec2) { 55 | dx := head.x - tail.x; 56 | dy := head.y - tail.y; 57 | if abs(dy) > 1 || abs(dx) > 1 { 58 | if abs(dy) > abs(dx) { 59 | tail.x = head.x; 60 | tail.y = head.y - sign(dy); 61 | } else if abs(dy) < abs(dx) { 62 | tail.y = head.y; 63 | tail.x = head.x - sign(dx); 64 | } else { 65 | tail.x = head.x - sign(dx); 66 | tail.y = head.y - sign(dy); 67 | } 68 | } 69 | } 70 | 71 | count_unique_positions :: (trace: []Vec2) -> int { 72 | quick_sort(trace, (a: Vec2, b: Vec2) -> int { 73 | if a.x != b.x return a.x - b.x; 74 | return a.y - b.y; 75 | }); 76 | count := 1; 77 | start := 0; 78 | for 0..trace.count-1 { 79 | if trace[start] != trace[it] { 80 | count += 1; 81 | start = it; 82 | } 83 | } 84 | return count; 85 | } 86 | 87 | solve :: ($N: int, part: int, file_path: string) { 88 | content, ok := read_entire_file(file_path); 89 | assert(ok); 90 | motions := parse_input(content); 91 | rope : [N]Vec2; 92 | trace : [..]Vec2; 93 | array_add(*trace, rope[N-1]); 94 | for motion: motions { 95 | for 0..motion.length-1 { 96 | rope[0] += directions[motion.direction]; 97 | for 0..N-2 adjust_tail(rope[it], *rope[it+1]); 98 | array_add(*trace, rope[N-1]); 99 | } 100 | } 101 | print("Part %: %: %\n", part, file_path, count_unique_positions(trace)); 102 | } 103 | 104 | main :: () { 105 | solve(2, 1, "sample.txt"); 106 | solve(2, 1, "sample2.txt"); 107 | solve(2, 1, "input.txt"); 108 | solve(10, 2, "sample.txt"); 109 | solve(10, 2, "sample2.txt"); 110 | solve(10, 2, "input.txt"); 111 | } 112 | -------------------------------------------------------------------------------- /10/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "File"; 3 | #import "String"; 4 | #import "Math"; 5 | 6 | Inst :: struct { 7 | type : enum { NOOP; ADDX; }; 8 | value : int; 9 | } 10 | 11 | parse_input :: (content: string) -> []Inst { 12 | result : [..]Inst; 13 | found: bool; 14 | left: string; 15 | right := content; 16 | while right.count > 0 { 17 | found, left, right = split_from_left(right, #char "\n"); 18 | inst : Inst; 19 | if starts_with(left, "noop") { 20 | inst.type = .NOOP; 21 | } else if starts_with(left, "addx") { 22 | inst.type = .ADDX; 23 | inst.value = string_to_int(slice(left, 5, left.count - 5)); 24 | } else { 25 | assert(false, "Unreachable. Unknown instruction. %", left); 26 | } 27 | array_add(*result, inst); 28 | } 29 | return result; 30 | } 31 | 32 | signal_strength :: (cycle: int, x: int) -> int { 33 | if cycle > 20 { 34 | if (cycle - 20)%40 == 0 { 35 | print("%: % %\n", cycle, x, cycle*x); 36 | return cycle*x; 37 | } else { 38 | return 0; 39 | } 40 | } else if (cycle == 20) { 41 | print("%: % %\n", cycle, x, cycle*x); 42 | return cycle*x; 43 | } 44 | 45 | return 0; 46 | } 47 | 48 | part1 :: (file_path: string) { 49 | content, ok := read_entire_file(file_path); 50 | assert(ok); 51 | program := parse_input(content); 52 | x := 1; 53 | cycle := 1; 54 | ans := 0; 55 | for program { 56 | if it.type == { 57 | case .NOOP; { 58 | ans += signal_strength(cycle, x); 59 | cycle += 1; 60 | } 61 | case .ADDX; { 62 | ans += signal_strength(cycle, x); 63 | cycle += 1; 64 | ans += signal_strength(cycle, x); 65 | cycle += 1; 66 | x += it.value; 67 | } 68 | } 69 | } 70 | print("Part 1: %: %\n", file_path, ans); 71 | } 72 | 73 | ROWS :: 6; 74 | COLUMNS :: 40; 75 | 76 | render_pixel :: (screen: *[ROWS][COLUMNS]u8, cycle: int, x: int) { 77 | sy := (cycle-1)/COLUMNS; 78 | sx := (cycle-1)%COLUMNS; 79 | if abs(x - sx) < 2 { 80 | (< string { 13 | assert(starts_with(s, prefix), "\"%\" does not start with \"%\"", s, prefix); 14 | return slice(s, prefix.count, s.count - prefix.count); 15 | } 16 | 17 | parse_point :: (s: string) -> Vec2 { 18 | comps := split(s, ", "); 19 | assert(comps.count == 2); 20 | return .{ 21 | x = string_to_int(check_and_strip_prefix(comps[0], "x=")), 22 | y = string_to_int(check_and_strip_prefix(comps[1], "y=")), 23 | }; 24 | } 25 | 26 | parse_input :: (content: string) -> (sensors: []Vec2, beacons: []Vec2) { 27 | sensors : [..]Vec2; 28 | beacons : [..]Vec2; 29 | for line: split(content, "\n") { 30 | if line.count == 0 continue; 31 | comps := split(line, ": "); 32 | assert(comps.count == 2); 33 | array_add(*sensors, parse_point(check_and_strip_prefix(comps[0], "Sensor at "))); 34 | array_add(*beacons, parse_point(check_and_strip_prefix(comps[1], "closest beacon is at "))); 35 | } 36 | return sensors, beacons; 37 | } 38 | 39 | Range :: struct { 40 | low, high: int; 41 | } 42 | 43 | scan_row :: (sensors: []Vec2, beacons: []Vec2, row: int) -> []Range { 44 | ranges: [..]Range; 45 | for sensor: sensors { 46 | d := abs(sensor.x - beacons[it_index].x) + abs(sensor.y - beacons[it_index].y); 47 | t := d - abs(sensor.y - row); 48 | if t > 0 { 49 | x1 := sensor.x - t; 50 | x2 := sensor.x + t; 51 | array_add(*ranges, .{x1, x2}); 52 | } 53 | } 54 | return ranges; 55 | } 56 | 57 | merge_and_bound :: (ranges: []Range, upper_bound: int) -> []Range { 58 | for *ranges { 59 | if it.low < 0 then it.low = 0; 60 | if it.high > upper_bound then it.high = upper_bound; 61 | } 62 | quick_sort(ranges, (a: Range, b: Range) -> int { 63 | return a.low - b.low; 64 | }); 65 | merged_ranges : [..]Range; 66 | for ranges { 67 | if merged_ranges.count == 0 { 68 | array_add(*merged_ranges, it); 69 | } else { 70 | m := *merged_ranges[merged_ranges.count-1]; 71 | if intersect(m, it) || m.high + 1 == it.low { 72 | m.high = max(m.high, it.high); 73 | } else { 74 | array_add(*merged_ranges, it); 75 | } 76 | } 77 | } 78 | return merged_ranges; 79 | } 80 | 81 | part1 :: (file_path: string, row: int) { 82 | content, ok := read_entire_file(file_path); 83 | assert(ok, "Can't open file %", file_path); 84 | sensors, beacons := parse_input(content); 85 | visited : Table(int, bool); 86 | for scan_row(sensors, beacons, row) { 87 | for col: it.low..it.high { 88 | if table_find_pointer(*visited, col) == null { 89 | table_add(*visited, col, true); 90 | } 91 | } 92 | } 93 | for beacon: beacons { 94 | if beacon.y == row && table_find_pointer(*visited, beacon.x) != null { 95 | table_remove(*visited, beacon.x); 96 | } 97 | } 98 | print("Part: 1, file: %, row: %, answer: %\n", file_path, row, visited.count); 99 | } 100 | 101 | intersect :: (a: Range, b: Range) -> bool { 102 | return a.high >= b.low && b.high >= a.low; 103 | } 104 | 105 | part2 :: (file_path: string, upper_bound: int) { 106 | content, ok := read_entire_file(file_path); 107 | assert(ok, "Can't open file %", file_path); 108 | sensors, beacons := parse_input(content); 109 | for row: 0..upper_bound { 110 | ranges := merge_and_bound(scan_row(sensors, beacons, row), upper_bound); 111 | if ranges.count > 1 { 112 | x := ranges[0].high + 1; 113 | y := row; 114 | print("%: % (%, %) %\n", row, ranges, x, y, x*4000000 + y); 115 | } 116 | } 117 | } 118 | 119 | main :: () { 120 | part1("sample.txt", 10); 121 | part1("input.txt", 10); 122 | part1("input.txt", 2000000); 123 | part2("sample.txt", 20); 124 | part2("input.txt", 4000000); 125 | } 126 | -------------------------------------------------------------------------------- /12/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "File"; 3 | #import "String"; 4 | 5 | Vec2 :: struct { 6 | x, y: int; 7 | } 8 | 9 | operator + :: (a: Vec2, b: Vec2) -> Vec2 { 10 | return .{a.x + b.x, a.y + b.y}; 11 | } 12 | 13 | operator == :: (a: Vec2, b: Vec2) -> bool { 14 | return a.x == b.x && a.y == b.y; 15 | } 16 | 17 | parse_input :: (content: string) -> (grid: []u8, size: Vec2, start: Vec2, end: Vec2) { 18 | lines := split(content, "\n"); 19 | if lines.count <= 0 then return .[], .{}, .{}, .{}; 20 | if lines[lines.count-1] == "" { 21 | lines.count -= 1; 22 | } 23 | height := lines.count; 24 | width := lines[0].count; 25 | grid : [..]u8; 26 | start, end : Vec2; 27 | for line, y: lines { 28 | assert(line.count == width); 29 | for x: 0..line.count-1 { 30 | if line[x] == { 31 | case #char "S"; start = .{x, y}; 32 | case #char "E"; end = .{x, y}; 33 | } 34 | array_add(*grid, line[x]); 35 | } 36 | } 37 | return grid, .{width, height}, start, end; 38 | } 39 | 40 | inbounds :: (using cell: Vec2, size: Vec2) -> bool { 41 | return 0 <= x && x < size.x && 0 <= y && y < size.y; 42 | } 43 | 44 | neighbors :: (cell: Vec2, size: Vec2) -> []Vec2 { 45 | result : [..]Vec2; 46 | directions :: Vec2.[ 47 | .{-1, 0}, 48 | .{1, 0}, 49 | .{0, 1}, 50 | .{0, -1}, 51 | ]; 52 | for directions { 53 | neighbor := cell + it; 54 | if inbounds(neighbor, size) { 55 | array_add(*result, neighbor); 56 | } 57 | } 58 | return result; 59 | } 60 | 61 | index :: (cell: Vec2, size: Vec2) -> int { 62 | return cell.y*size.x + cell.x; 63 | } 64 | 65 | print_visited :: (visited: []int, size: Vec2) { 66 | for y: 0..size.y-1 { 67 | for x: 0..size.x-1 { 68 | print("% ", visited[index(.{x, y}, size)]); 69 | } 70 | print("\n"); 71 | } 72 | } 73 | 74 | elevation :: (x: u8) -> int { 75 | if x == { 76 | case #char "S"; return 0; 77 | case #char "E"; return #char "z" - #char "a"; 78 | case; return x - #char "a"; 79 | } 80 | } 81 | 82 | shortest_path ::(grid: []u8, size: Vec2, start: Vec2, end: Vec2) -> int, bool { 83 | visited := NewArray(size.x*size.y, int); 84 | for *visited { 85 | (< 0 { 91 | new_wave : [..]Vec2; 92 | for cell: wave { 93 | for neighbor: neighbors(cell, size) { 94 | elevation_diff := elevation(grid[index(neighbor, size)]) - elevation(grid[index(cell, size)]); 95 | if visited[index(neighbor, size)] < 0 && elevation_diff <= 1 { 96 | if neighbor == end { 97 | return visited[index(cell, size)] + 1, true; 98 | } 99 | visited[index(neighbor, size)] = visited[index(cell, size)] + 1; 100 | array_add(*new_wave, neighbor); 101 | } 102 | } 103 | } 104 | wave = new_wave; 105 | } 106 | return 0, false; 107 | } 108 | 109 | part1 :: (file_path: string) { 110 | content, ok := read_entire_file(file_path); 111 | grid, size, start, end := parse_input(content); 112 | ans, found := shortest_path(grid, size, start, end); 113 | assert(found); 114 | print("Part 1: %: %\n", file_path, ans); 115 | } 116 | 117 | part2 :: (file_path: string) { 118 | content, ok := read_entire_file(file_path); 119 | grid, size, start, end := parse_input(content); 120 | ans := 10_000_000; 121 | for y: 0..size.y-1 { 122 | for x: 0..size.x-1 { 123 | if grid[index(.{x, y}, size)] == #char "a" { 124 | new_ans, found := shortest_path(grid, size, .{x, y}, end); 125 | if found then ans = min(new_ans, ans); 126 | } 127 | } 128 | } 129 | print("Part 2: %: %\n", file_path, ans); 130 | } 131 | 132 | main :: () { 133 | part1("sample.txt"); 134 | part1("input.txt"); 135 | part2("sample.txt"); 136 | part2("input.txt"); 137 | } 138 | -------------------------------------------------------------------------------- /14/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "File"; 3 | #import "String"; 4 | #import "Hash_Table"; 5 | Hash :: #import "Hash"; 6 | 7 | Vec2 :: struct { 8 | x, y: int; 9 | } 10 | 11 | BlockedTable :: Table ( 12 | Vec2, bool, 13 | given_compare_function = (a: Vec2, b: Vec2) -> bool { 14 | return a == b; 15 | }, 16 | given_hash_function = (a: Vec2) -> u32 { 17 | return Hash.get_hash(cast(u32)a.x*32768 + cast(u32)a.y); 18 | }, 19 | ); 20 | 21 | operator + :: (a: Vec2, b: Vec2) -> Vec2 { 22 | return .{a.x + b.x, a.y + b.y}; 23 | } 24 | 25 | operator == :: (a: Vec2, b: Vec2) -> bool { 26 | return a.x == b.x && a.y == b.y; 27 | } 28 | 29 | parse_input :: (content: string) -> (blocked: BlockedTable, lowest_y: int) { 30 | blocked: BlockedTable; 31 | lowest_y := 0; 32 | for split(content, "\n") { 33 | if it.count == 0 continue; 34 | path: [..]Vec2; 35 | for split(it, " -> ") { 36 | parts := split(it, ","); 37 | assert(parts.count == 2); 38 | cell := Vec2.{ 39 | x = string_to_int(parts[0]), 40 | y = string_to_int(parts[1]), 41 | }; 42 | array_add(*path, cell); 43 | lowest_y = max(lowest_y, cell.y); 44 | } 45 | for 0..path.count-2 { 46 | a := path[it]; 47 | b := path[it+1]; 48 | if a.x == b.x { 49 | x := a.x; 50 | y1 := a.y; 51 | y2 := b.y; 52 | if y1 > y2 then Swap(*y1, *y2); 53 | for y: y1..y2 table_add(*blocked, .{x, y}, true); 54 | } else if a.y == b.y { 55 | y := a.y; 56 | x1 := a.x; 57 | x2 := b.x; 58 | if x1 > x2 then Swap(*x1, *x2); 59 | for x: x1..x2 table_add(*blocked, .{x, y}, true); 60 | } else { 61 | assert(false, "unreachable"); 62 | } 63 | } 64 | } 65 | return blocked, lowest_y; 66 | } 67 | 68 | part1 :: (file_path: string) { 69 | content, ok := read_entire_file(file_path); 70 | sand_source :: Vec2.{500,0}; 71 | 72 | blocked, lowest_y := parse_input(content); 73 | 74 | count := 0; 75 | while true { 76 | sand := sand_source; 77 | while sand.y <= lowest_y { 78 | next_sand := sand + .{0, 1}; 79 | if table_find_pointer(*blocked, next_sand) == null { 80 | sand = next_sand; 81 | continue; 82 | } 83 | next_sand = sand + .{-1, 1}; 84 | if table_find_pointer(*blocked, next_sand) == null { 85 | sand = next_sand; 86 | continue; 87 | } 88 | next_sand = sand + .{1, 1}; 89 | if table_find_pointer(*blocked, next_sand) == null { 90 | sand = next_sand; 91 | continue; 92 | } 93 | count += 1; 94 | table_add(*blocked, sand, true); 95 | break; 96 | } 97 | if sand.y > lowest_y break; 98 | } 99 | print("Part 1: %: %\n", file_path, count); 100 | } 101 | 102 | part2 :: (file_path: string) { 103 | content, ok := read_entire_file(file_path); 104 | sand_source :: Vec2.{500,0}; 105 | 106 | blocked, lowest_y := parse_input(content); 107 | 108 | count := 0; 109 | while table_find_pointer(*blocked, sand_source) == null { 110 | sand := sand_source; 111 | while true { 112 | next_sand := sand + .{0, 1}; 113 | if table_find_pointer(*blocked, next_sand) == null && next_sand.y < lowest_y + 2 { 114 | sand = next_sand; 115 | continue; 116 | } 117 | next_sand = sand + .{-1, 1}; 118 | if table_find_pointer(*blocked, next_sand) == null && next_sand.y < lowest_y + 2 { 119 | sand = next_sand; 120 | continue; 121 | } 122 | next_sand = sand + .{1, 1}; 123 | if table_find_pointer(*blocked, next_sand) == null && next_sand.y < lowest_y + 2 { 124 | sand = next_sand; 125 | continue; 126 | } 127 | count += 1; 128 | table_add(*blocked, sand, true); 129 | break; 130 | } 131 | } 132 | print("Part 2: %: %\n", file_path, count); 133 | } 134 | 135 | main :: () { 136 | part1("sample.txt"); 137 | part1("input.txt"); 138 | part2("sample.txt"); 139 | part2("input.txt"); 140 | } 141 | -------------------------------------------------------------------------------- /16/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | def name_to_index(name): 4 | # return name 5 | return (name[1] - ord('A'))*26 + name[0] - ord('A') 6 | 7 | def parse_valves(content): 8 | valves = {} 9 | for line in content.splitlines(): 10 | if len(line) > 0: 11 | components = line.split(b"; ") 12 | node = components[0].split(b" has flow rate=") 13 | name = node[0].split(b"Valve ")[1]; 14 | rate = int(node[1]) 15 | neighbors = components[1].split(b", ") 16 | neighbors[0] = neighbors[0][-2:] 17 | valves[name_to_index(name)] = { 18 | "rate": rate, 19 | "neighbors": [name_to_index(n) for n in neighbors], 20 | } 21 | return valves 22 | 23 | def distance_map(valves): 24 | def bfs(start): 25 | distances = {start: 0} 26 | wave = [start] 27 | while len(wave) > 0: 28 | new_wave = [] 29 | for current in wave: 30 | for neighbor in valves[current]["neighbors"]: 31 | if neighbor not in distances: 32 | distances[neighbor] = distances[current] + 1 33 | new_wave.append(neighbor) 34 | wave = new_wave 35 | return distances 36 | return {name: bfs(name) for name, valve in valves.items() if valve["rate"] > 0 or name == name_to_index(b"AA")} 37 | 38 | def rate(valves, opened): 39 | result = 0 40 | for name, valve in valves.items(): 41 | if name in opened: 42 | result += valve["rate"] 43 | return result 44 | 45 | def part1(file_path): 46 | content = open(file_path, "rb").read() 47 | valves = parse_valves(content) 48 | distance = distance_map(valves) 49 | TIME_LIMIT = 30 50 | def bruteforce(here, opened, released = []): 51 | if len(released) >= TIME_LIMIT: 52 | return sum(released[0:TIME_LIMIT]) 53 | 54 | r = rate(valves, opened) 55 | max_released = 0 56 | all_open = True 57 | for there in distance: 58 | if there not in opened: 59 | all_open = False 60 | d = distance[here][there] 61 | max_released = max( 62 | max_released, 63 | bruteforce(there, opened.union({there}), released + [r]*(d + 1)) 64 | ) 65 | if all_open: 66 | return bruteforce(here, opened, released + [r]*(TIME_LIMIT - len(released))) 67 | else: 68 | return max_released 69 | print("Part 1", file_path, bruteforce(name_to_index(b"AA"), {name_to_index(b"AA")})) 70 | 71 | max_result = 0 72 | 73 | def part2(file_path): 74 | content = open(file_path, "rb").read() 75 | valves = parse_valves(content) 76 | distance = distance_map(valves) 77 | start = name_to_index(b"AA") 78 | TIME_LIMIT = 26 79 | 80 | def bruteforce_of_bruteforce(here, outer_opened, outer_released = []): 81 | if len(outer_released) >= TIME_LIMIT: 82 | chain = outer_released[0:TIME_LIMIT] 83 | x = sum(chain) 84 | global max_result 85 | if x > max_result: 86 | max_result = x 87 | print(max_result, chain) 88 | return 89 | 90 | r = rate(valves, outer_opened) 91 | all_open = True 92 | for there in distance: 93 | if there not in outer_opened: 94 | all_open = False 95 | d = distance[here][there] 96 | bruteforce_of_bruteforce(there, outer_opened.union({there}), outer_released + [r]*(d + 1)) 97 | 98 | def nested_bruteforce(here, inner_opened = set(), inner_released = []): 99 | if len(inner_released) >= TIME_LIMIT: 100 | chain = list(map(lambda x: x[0] + x[1], zip(inner_released, outer_released))) 101 | x = sum(chain) 102 | global max_result 103 | if x > max_result: 104 | max_result = x 105 | print(max_result, chain) 106 | return 107 | 108 | r = rate(valves, inner_opened) 109 | all_open = True 110 | for there in distance: 111 | if there not in inner_opened and there not in outer_opened: 112 | all_open = False 113 | d = distance[here][there] 114 | nested_bruteforce(there, inner_opened.union({there}), inner_released + [r]*(d + 1)) 115 | if all_open: 116 | nested_bruteforce(here, inner_opened, inner_released + [r]*(TIME_LIMIT - len(inner_released))) 117 | 118 | outer_released = outer_released + [r]*max(0, TIME_LIMIT - len(outer_released)) 119 | nested_bruteforce(start) 120 | 121 | bruteforce_of_bruteforce(start, {start}) 122 | 123 | # part1("sample.txt") 124 | # part1("input.txt") 125 | # part2("sample.txt") 126 | part2("input.txt") 127 | -------------------------------------------------------------------------------- /11/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "File"; 3 | #import "String"; 4 | #import "Sort"; 5 | 6 | Operation :: struct { 7 | kind: enum { ADD; MUL; SQR; }; 8 | argument: int; 9 | } 10 | 11 | Monkey :: struct { 12 | items: [..]int; 13 | operation: Operation; 14 | test: int; 15 | if_true: int; 16 | if_false: int; 17 | inspected: int; 18 | } 19 | 20 | strip_prefix :: (s: string, prefix: string) -> string { 21 | return slice(s, prefix.count, s.count - prefix.count); 22 | } 23 | 24 | check_and_strip_prefix :: (s: string, prefix: string) -> string { 25 | assert(starts_with(s, prefix)); 26 | return strip_prefix(s, prefix); 27 | } 28 | 29 | parse_monkey :: (lines: []string) -> Monkey { 30 | starting_items := check_and_strip_prefix(lines[0], " Starting items: "); 31 | operation := check_and_strip_prefix(lines[1], " Operation: new = old "); 32 | test := check_and_strip_prefix(lines[2], " Test: divisible by "); 33 | if_true := check_and_strip_prefix(lines[3], " If true: throw to monkey "); 34 | if_false := check_and_strip_prefix(lines[4], " If false: throw to monkey "); 35 | 36 | monkey: Monkey; 37 | for split(starting_items, ", ") { 38 | array_add(*monkey.items, string_to_int(it)); 39 | } 40 | if operation[0] == { 41 | case #char "+"; { 42 | operation = check_and_strip_prefix(operation, "+ "); 43 | monkey.operation = .{ 44 | kind = .ADD, 45 | argument = string_to_int(operation), 46 | }; 47 | } 48 | case #char "*"; { 49 | operation = check_and_strip_prefix(operation, "* "); 50 | if operation == "old" { 51 | monkey.operation = .{ 52 | kind = .SQR, 53 | }; 54 | } else { 55 | monkey.operation = .{ 56 | kind = .MUL, 57 | argument = string_to_int(operation), 58 | }; 59 | } 60 | } 61 | } 62 | monkey.test = string_to_int(test); 63 | monkey.if_true = string_to_int(if_true); 64 | monkey.if_false = string_to_int(if_false); 65 | return monkey; 66 | } 67 | 68 | parse_input :: (content: string) -> []Monkey { 69 | lines := split(content, "\n"); 70 | monkeys : [..]Monkey; 71 | while lines.count > 0 { 72 | monkey_lines : []string = ---; 73 | monkey_lines.data = lines.data + 1; 74 | monkey_lines.count = 5; 75 | array_add(*monkeys, parse_monkey(monkey_lines)); 76 | 77 | lines.data += 7; 78 | lines.count -= 7; 79 | } 80 | return monkeys; 81 | } 82 | 83 | operation_apply :: (using operation: Operation, value: int) -> int { 84 | if kind == { 85 | case .ADD; return value + argument; 86 | case .MUL; return value * argument; 87 | case .SQR; return value * value; 88 | } 89 | assert(false, "unreachable"); 90 | return 0; 91 | } 92 | 93 | monkey_turn_part1 :: (using monkey: *Monkey, monkeys: []Monkey) { 94 | for items { 95 | new_worry := operation_apply(operation, it)/3; 96 | if new_worry%test == 0 { 97 | array_add(*monkeys[if_true].items, new_worry); 98 | } else { 99 | array_add(*monkeys[if_false].items, new_worry); 100 | } 101 | inspected += 1; 102 | } 103 | items.count = 0; 104 | } 105 | 106 | part1 :: (file_path: string) { 107 | content, ok := read_entire_file(file_path); 108 | monkeys := parse_input(content); 109 | for 1..20 { 110 | for *monkeys { 111 | monkey_turn_part1(it, monkeys); 112 | } 113 | } 114 | quick_sort(monkeys, (a: Monkey, b: Monkey) -> int { 115 | return b.inspected - a.inspected; 116 | }); 117 | print("Part 1: %: %\n", file_path, monkeys[0].inspected*monkeys[1].inspected); 118 | } 119 | 120 | monkey_turn_part2 :: (using monkey: *Monkey, monkeys: []Monkey, limit: int) { 121 | for items { 122 | new_worry := operation_apply(operation, it)%limit; 123 | if new_worry%test == 0 { 124 | array_add(*monkeys[if_true].items, new_worry); 125 | } else { 126 | array_add(*monkeys[if_false].items, new_worry); 127 | } 128 | inspected += 1; 129 | } 130 | items.count = 0; 131 | } 132 | 133 | part2 :: (file_path: string) { 134 | content, ok := read_entire_file(file_path); 135 | monkeys := parse_input(content); 136 | limit := 1; 137 | for *monkeys limit *= it.test; 138 | for 1..10_000 { 139 | for *monkeys { 140 | monkey_turn_part2(it, monkeys, limit); 141 | } 142 | } 143 | quick_sort(monkeys, (a: Monkey, b: Monkey) -> int { 144 | return b.inspected - a.inspected; 145 | }); 146 | print("Part 2: %: %\n", file_path, monkeys[0].inspected*monkeys[1].inspected); 147 | } 148 | 149 | main :: () { 150 | part1("sample.txt"); 151 | part1("input.txt"); 152 | part2("sample.txt"); 153 | part2("input.txt"); 154 | } 155 | -------------------------------------------------------------------------------- /07/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "File"; 3 | #import "String"; 4 | 5 | INode :: struct { 6 | type : enum { FILE; DIRECTORY; }; 7 | name: string; 8 | parent: *INode; 9 | size: int; 10 | children: [..]*INode; 11 | } 12 | 13 | find_child :: (using node : *INode, child_name: string) -> *INode { 14 | for children if it.name == child_name return it; 15 | return null; 16 | } 17 | 18 | print_tree :: (using inode: *INode, level: int = 0) { 19 | for 0..level-1 print(" "); 20 | if type == .DIRECTORY && !ends_with(name, "/") { 21 | print("%/ (%)\n", name, size); 22 | } else { 23 | print("% (%)\n", name, size); 24 | } 25 | if type == .DIRECTORY for children print_tree(it, level + 1); 26 | } 27 | 28 | compute_dir_sizes :: (using inode: *INode) -> int { 29 | if type == .DIRECTORY { 30 | size = 0; 31 | for children size += compute_dir_sizes(it); 32 | } 33 | 34 | return size; 35 | } 36 | 37 | find_all_dirs_with_at_most_size :: (using inode: *INode, upper_size: int, dirs: *[..]*INode) { 38 | if type == .DIRECTORY && size <= upper_size then array_add(dirs, inode); 39 | for children find_all_dirs_with_at_most_size(it, upper_size, dirs); 40 | } 41 | 42 | find_smallest_dir_that_is_bigger_or_equal_to :: (using root: *INode, bottom_size: int, dir: **INode) { 43 | if type != .DIRECTORY return; 44 | if size >= bottom_size && (< size) then < string { 49 | n := prefix.count; 50 | if n > s.count then n = s.count; 51 | return slice(s, n, s.count - n); 52 | } 53 | 54 | main :: () { 55 | file_path :: "input.txt"; 56 | content, ok := read_entire_file(file_path); 57 | assert(ok); 58 | found: bool; 59 | left: string; 60 | right := content; 61 | line_count := 1; 62 | root : *INode = null; 63 | current_directory : *INode = null; 64 | while right.count > 0 { 65 | found, left, right = split_from_left(right, #char "\n"); 66 | command_prefix :: "$ "; 67 | cd_prefix :: "cd "; 68 | ls_prefix :: "ls"; 69 | if starts_with(left, command_prefix) { 70 | cmd := remove_prefix(left, command_prefix); 71 | if starts_with(cmd, cd_prefix) { 72 | name := remove_prefix(cmd, cd_prefix); 73 | print("%:%: Change directory to %\n", file_path, line_count, name); 74 | if (name == "..") { 75 | assert(current_directory != null); 76 | assert(current_directory.parent != null); 77 | current_directory = current_directory.parent; 78 | } else { 79 | if current_directory == null { 80 | current_directory = New(INode); 81 | current_directory.type = .DIRECTORY; 82 | current_directory.name = name; 83 | root = current_directory; 84 | } else { 85 | child := find_child(current_directory, name); 86 | assert(child != null); 87 | assert(child.type == .DIRECTORY); 88 | current_directory = child; 89 | } 90 | } 91 | } else if starts_with(cmd, ls_prefix) { 92 | assert(current_directory != null); 93 | print("%:%: List files\n", file_path, line_count); 94 | } else { 95 | assert(false, "unreachable"); 96 | } 97 | } else { 98 | assert(current_directory != null); 99 | found, type_or_size, name := split_from_left(left, #char " "); 100 | if type_or_size == "dir" { 101 | child := find_child(current_directory, name); 102 | if child == null { 103 | child = New(INode); 104 | child.type = .DIRECTORY; 105 | child.name = name; 106 | child.parent = current_directory; 107 | array_add(*current_directory.children, child); 108 | } 109 | assert(child.type == .DIRECTORY); 110 | } else { 111 | size := string_to_int(type_or_size); 112 | child := find_child(current_directory, name); 113 | if child == null { 114 | child = New(INode); 115 | child.type = .FILE; 116 | child.name = name; 117 | child.size = size; 118 | child.parent = current_directory; 119 | array_add(*current_directory.children, child); 120 | } 121 | assert(child.type == .FILE); 122 | assert(child.size == size); 123 | } 124 | print("%:%: type or size: %, name: %\n", file_path, line_count, type_or_size, name); 125 | } 126 | line_count += 1; 127 | } 128 | compute_dir_sizes(root); 129 | print("------------------------------\n"); 130 | print_tree(root); 131 | print("------------------------------\n"); 132 | dirs : [..]*INode; 133 | find_all_dirs_with_at_most_size(root, 100_000, *dirs); 134 | answer := 0; 135 | for dirs { 136 | print("%/ (%)\n", it.name, it.size); 137 | answer += it.size; 138 | } 139 | print("Part 1: %: %\n", file_path, answer); 140 | print("------------------------------\n"); 141 | total_size :: 70_000_000; 142 | required_free_space :: 30_000_000; 143 | actual_free_space := total_size - root.size; 144 | assert(required_free_space >= actual_free_space); 145 | need_to_free_up := required_free_space - actual_free_space; 146 | print("Need to free up: %\n", need_to_free_up); 147 | dir : *INode = null; 148 | find_smallest_dir_that_is_bigger_or_equal_to(root, need_to_free_up, *dir); 149 | assert(dir != null); 150 | print("Part 2: %: %\n", file_path, dir.size); 151 | } 152 | -------------------------------------------------------------------------------- /13/main.jai: -------------------------------------------------------------------------------- 1 | #import "Basic"; 2 | #import "String"; 3 | #import "File"; 4 | #import "Sort"; 5 | 6 | Packet :: struct { 7 | kind : enum { NUMBER; LIST; } 8 | number: int; 9 | children: [..]Packet; 10 | } 11 | 12 | parse_packet_number :: (source: string) -> (Packet, string) { 13 | assert(source.count > 0); 14 | index := 0; 15 | while index < source.count && #char "0" <= source[index] && source[index] <= #char "9" { 16 | index += 1; 17 | } 18 | assert(index > 0, "Number does not start with digit: |%|", source); 19 | 20 | return Packet.{ 21 | kind = .NUMBER, 22 | number = string_to_int(slice(source, 0, index)), 23 | }, slice(source, index, source.count - index); 24 | } 25 | 26 | parse_packet_list :: (source0: string) -> (Packet, string) { 27 | source := source0; 28 | 29 | assert(source.count > 0 && source[0] == #char "["); 30 | source = slice(source, 1, source.count - 1); 31 | 32 | packet := Packet.{ kind = .LIST }; 33 | 34 | if source.count > 0 && source[0] == #char "]" { 35 | return packet, slice(source, 1, source.count - 1); 36 | } 37 | 38 | item, rest := parse_packet(source); 39 | array_add(*packet.children, item); 40 | source = rest; 41 | 42 | while source.count > 0 && source[0] == #char "," { 43 | source = slice(source, 1, source.count - 1); 44 | item, rest = parse_packet(source); 45 | array_add(*packet.children, item); 46 | source = rest; 47 | } 48 | 49 | assert(source.count > 0 && source[0] == #char "]"); 50 | 51 | return packet, slice(source, 1, source.count - 1); 52 | } 53 | 54 | parse_packet :: (source: string) -> (Packet, string) { 55 | assert(source.count > 0); 56 | if source[0] == #char "[" { 57 | packet, rest := parse_packet_list(source); 58 | return packet, rest; 59 | } else { 60 | packet, rest := parse_packet_number(source); 61 | return packet, rest; 62 | } 63 | } 64 | 65 | Order :: enum { 66 | CORRECT; 67 | WRONG; 68 | CONTINUE; 69 | } 70 | 71 | check_order_of_int :: (l: int, r: int) -> Order { 72 | if l < r return .CORRECT; 73 | if l > r return .WRONG; 74 | return .CONTINUE; 75 | } 76 | 77 | packet_number :: (x: int) -> Packet { 78 | return .{ 79 | kind = .NUMBER, 80 | number = x, 81 | }; 82 | } 83 | 84 | packet_wrap_in_list :: (packet: Packet) -> Packet { 85 | list: Packet; 86 | list.kind = .LIST; 87 | array_add(*list.children, packet); 88 | return list; 89 | } 90 | 91 | check_order_of_packet :: (lp: Packet, rp: Packet) -> Order { 92 | if lp.kind == .NUMBER && rp.kind == .NUMBER { 93 | return check_order_of_int(lp.number, rp.number); 94 | } 95 | if lp.kind == .LIST && rp.kind == .LIST { 96 | n := min(lp.children.count, rp.children.count); 97 | for 0..n-1 { 98 | order := check_order_of_packet(*lp.children[it], *rp.children[it]); 99 | if order != .CONTINUE return order; 100 | } 101 | return check_order_of_int(lp.children.count, rp.children.count); 102 | } 103 | if lp.kind == .NUMBER && rp.kind == .LIST { 104 | return check_order_of_packet(packet_wrap_in_list(lp), rp); 105 | } 106 | if lp.kind == .LIST && rp.kind == .NUMBER { 107 | return check_order_of_packet(lp, packet_wrap_in_list(rp)); 108 | } 109 | assert(false, "unreachable"); 110 | return .CONTINUE; 111 | } 112 | 113 | part1 :: (file_path: string) { 114 | content, ok := read_entire_file(file_path); 115 | assert(ok); 116 | lines := split(content, "\n"); 117 | left_packet, right_packet: Packet; 118 | 119 | index := 1; 120 | answer := 0; 121 | while lines.count >= 2 { 122 | left_packet, content = parse_packet(lines[0]); 123 | right_packet, content = parse_packet(lines[1]); 124 | 125 | order := check_order_of_packet(left_packet, right_packet); 126 | assert(order != .CONTINUE); 127 | if order == .CORRECT then answer += index; 128 | 129 | lines.data += 2; lines.count -= 2; 130 | if lines.count > 0 && lines[0].count == 0 { 131 | lines.data += 1; lines.count -= 1; 132 | } 133 | index += 1; 134 | } 135 | print("Part 1: %: %\n", file_path, answer); 136 | } 137 | 138 | print_packet :: (using p: Packet) { 139 | if kind == { 140 | case .NUMBER; print("%", number); 141 | case .LIST; { 142 | print("["); 143 | for children { 144 | if it_index > 0 then print(","); 145 | print_packet(it); 146 | } 147 | print("]"); 148 | } 149 | } 150 | } 151 | 152 | packet_is_divider :: (packet: Packet) -> bool { 153 | return packet.kind == .LIST && 154 | packet.children.count == 1 && 155 | packet.children[0].kind == .LIST && 156 | packet.children[0].children.count == 1 && 157 | packet.children[0].children[0].kind == .NUMBER && 158 | (packet.children[0].children[0].number == 2 || 159 | packet.children[0].children[0].number == 6); 160 | } 161 | 162 | part2 :: (file_path: string) { 163 | content, ok := read_entire_file(file_path); 164 | assert(ok); 165 | lines := split(content, "\n"); 166 | packets : [..]Packet; 167 | for line: lines { 168 | if line.count == 0 continue; 169 | 170 | packet, rest := parse_packet(line); 171 | assert(rest.count == 0); 172 | array_add(*packets, packet); 173 | } 174 | array_add(*packets, packet_wrap_in_list(packet_wrap_in_list(packet_number(2)))); 175 | array_add(*packets, packet_wrap_in_list(packet_wrap_in_list(packet_number(6)))); 176 | bubble_sort(packets, (a: Packet, b: Packet) -> int { 177 | if check_order_of_packet(*a, *b) == { 178 | case .CORRECT; return -1; 179 | case .WRONG; return 1; 180 | } 181 | assert(false, "unreachable"); 182 | return 0; 183 | }); 184 | answer := 1; 185 | for packets { 186 | if packet_is_divider(it) { 187 | answer *= it_index + 1; 188 | } 189 | } 190 | print("Part 2: %: %\n", file_path, answer); 191 | } 192 | 193 | main :: () { 194 | part1("sample.txt"); 195 | part1("input.txt"); 196 | part2("sample.txt"); 197 | part2("input.txt"); 198 | } 199 | -------------------------------------------------------------------------------- /05/data.jai: -------------------------------------------------------------------------------- 1 | sample_state :: #run -> []string { 2 | return .["ZN", "MCD", "P"]; 3 | }; 4 | 5 | sample_moves :: #run -> [][3]int { 6 | return .[ 7 | .[1, 2, 1], 8 | .[3, 1, 3], 9 | .[2, 2, 1], 10 | .[1, 1, 2] 11 | ]; 12 | }; 13 | 14 | input_state :: #run -> []string { 15 | return .[ 16 | "SMRNWJVT", 17 | "BWDJQPCV", 18 | "BJFHDRP", 19 | "FRPBMND", 20 | "HVRPTB", 21 | "CBPT", 22 | "BJRPL", 23 | "NCSLTZBW", 24 | "LSG", 25 | ]; 26 | }; 27 | 28 | input_moves :: #run -> [][3]int { 29 | return .[ 30 | .[7, 3, 9], 31 | .[6, 2, 1], 32 | .[2, 4, 8], 33 | .[10, 8, 4], 34 | .[1, 2, 4], 35 | .[15, 4, 1], 36 | .[28, 1, 3], 37 | .[1, 2, 5], 38 | .[7, 5, 9], 39 | .[5, 9, 5], 40 | .[21, 3, 1], 41 | .[1, 6, 4], 42 | .[4, 9, 2], 43 | .[7, 9, 2], 44 | .[4, 2, 6], 45 | .[1, 9, 1], 46 | .[2, 4, 9], 47 | .[2, 7, 4], 48 | .[4, 3, 5], 49 | .[2, 7, 9], 50 | .[5, 2, 7], 51 | .[2, 9, 1], 52 | .[1, 9, 2], 53 | .[3, 3, 7], 54 | .[3, 5, 2], 55 | .[2, 6, 1], 56 | .[1, 9, 4], 57 | .[25, 1, 9], 58 | .[2, 5, 3], 59 | .[1, 1, 7], 60 | .[9, 7, 1], 61 | .[1, 2, 8], 62 | .[16, 9, 7], 63 | .[5, 9, 5], 64 | .[7, 1, 8], 65 | .[1, 9, 7], 66 | .[18, 7, 4], 67 | .[1, 3, 4], 68 | .[1, 1, 8], 69 | .[2, 8, 1], 70 | .[1, 3, 9], 71 | .[17, 4, 5], 72 | .[5, 8, 6], 73 | .[2, 8, 1], 74 | .[6, 6, 7], 75 | .[2, 9, 4], 76 | .[5, 1, 3], 77 | .[3, 3, 7], 78 | .[1, 1, 2], 79 | .[2, 3, 8], 80 | .[2, 4, 1], 81 | .[2, 8, 7], 82 | .[2, 9, 7], 83 | .[1, 2, 4], 84 | .[1, 2, 4], 85 | .[2, 1, 7], 86 | .[11, 7, 1], 87 | .[4, 4, 1], 88 | .[3, 1, 3], 89 | .[7, 1, 8], 90 | .[4, 2, 8], 91 | .[2, 1, 4], 92 | .[2, 4, 3], 93 | .[2, 1, 3], 94 | .[3, 7, 4], 95 | .[4, 8, 6], 96 | .[1, 7, 5], 97 | .[2, 4, 1], 98 | .[4, 6, 2], 99 | .[3, 1, 8], 100 | .[6, 8, 6], 101 | .[1, 2, 1], 102 | .[23, 5, 8], 103 | .[21, 8, 3], 104 | .[2, 6, 9], 105 | .[1, 8, 9], 106 | .[2, 3, 7], 107 | .[26, 3, 8], 108 | .[2, 9, 2], 109 | .[8, 6, 3], 110 | .[3, 5, 4], 111 | .[1, 7, 4], 112 | .[1, 7, 9], 113 | .[1, 5, 4], 114 | .[5, 2, 9], 115 | .[7, 9, 8], 116 | .[38, 8, 7], 117 | .[1, 1, 7], 118 | .[1, 4, 9], 119 | .[3, 4, 1], 120 | .[4, 3, 2], 121 | .[1, 1, 7], 122 | .[34, 7, 6], 123 | .[3, 4, 7], 124 | .[1, 4, 2], 125 | .[2, 1, 3], 126 | .[1, 2, 9], 127 | .[5, 3, 6], 128 | .[1, 4, 6], 129 | .[4, 2, 6], 130 | .[1, 9, 5], 131 | .[4, 7, 3], 132 | .[1, 5, 9], 133 | .[1, 9, 1], 134 | .[1, 3, 6], 135 | .[1, 9, 3], 136 | .[5, 7, 9], 137 | .[1, 1, 9], 138 | .[3, 9, 1], 139 | .[1, 3, 4], 140 | .[38, 6, 1], 141 | .[2, 9, 4], 142 | .[3, 3, 6], 143 | .[1, 9, 5], 144 | .[8, 6, 8], 145 | .[1, 3, 6], 146 | .[1, 6, 8], 147 | .[2, 4, 3], 148 | .[4, 8, 5], 149 | .[1, 6, 2], 150 | .[1, 2, 7], 151 | .[1, 6, 3], 152 | .[3, 8, 7], 153 | .[4, 7, 6], 154 | .[1, 4, 1], 155 | .[5, 1, 4], 156 | .[4, 6, 5], 157 | .[3, 5, 8], 158 | .[3, 5, 3], 159 | .[1, 8, 2], 160 | .[6, 3, 8], 161 | .[1, 5, 7], 162 | .[1, 2, 1], 163 | .[1, 7, 6], 164 | .[3, 4, 9], 165 | .[6, 8, 6], 166 | .[3, 8, 2], 167 | .[3, 6, 2], 168 | .[1, 4, 5], 169 | .[1, 6, 9], 170 | .[2, 6, 1], 171 | .[1, 4, 1], 172 | .[2, 5, 2], 173 | .[1, 8, 5], 174 | .[1, 9, 8], 175 | .[22, 1, 5], 176 | .[3, 2, 3], 177 | .[1, 8, 4], 178 | .[2, 3, 6], 179 | .[1, 6, 4], 180 | .[1, 3, 8], 181 | .[1, 2, 8], 182 | .[10, 5, 3], 183 | .[1, 6, 8], 184 | .[2, 8, 4], 185 | .[1, 6, 3], 186 | .[2, 2, 3], 187 | .[1, 8, 5], 188 | .[13, 3, 4], 189 | .[2, 1, 7], 190 | .[11, 1, 2], 191 | .[3, 4, 5], 192 | .[6, 1, 9], 193 | .[8, 2, 6], 194 | .[4, 2, 1], 195 | .[2, 6, 5], 196 | .[4, 1, 8], 197 | .[2, 8, 6], 198 | .[1, 7, 1], 199 | .[1, 7, 2], 200 | .[8, 6, 5], 201 | .[1, 8, 4], 202 | .[1, 1, 6], 203 | .[10, 5, 1], 204 | .[3, 9, 4], 205 | .[6, 1, 3], 206 | .[9, 4, 5], 207 | .[1, 2, 1], 208 | .[1, 4, 9], 209 | .[1, 6, 8], 210 | .[1, 2, 1], 211 | .[1, 4, 8], 212 | .[2, 8, 9], 213 | .[1, 8, 6], 214 | .[1, 3, 2], 215 | .[1, 1, 4], 216 | .[1, 2, 5], 217 | .[1, 1, 8], 218 | .[1, 6, 8], 219 | .[8, 5, 3], 220 | .[1, 8, 7], 221 | .[1, 7, 8], 222 | .[6, 9, 6], 223 | .[2, 9, 2], 224 | .[1, 9, 7], 225 | .[1, 8, 5], 226 | .[1, 2, 7], 227 | .[1, 2, 9], 228 | .[16, 5, 2], 229 | .[4, 5, 1], 230 | .[12, 3, 6], 231 | .[1, 5, 4], 232 | .[8, 6, 9], 233 | .[1, 8, 5], 234 | .[2, 9, 6], 235 | .[2, 2, 5], 236 | .[3, 1, 4], 237 | .[3, 6, 7], 238 | .[7, 9, 6], 239 | .[4, 7, 1], 240 | .[1, 5, 2], 241 | .[1, 3, 2], 242 | .[4, 1, 9], 243 | .[4, 9, 5], 244 | .[12, 6, 7], 245 | .[4, 2, 6], 246 | .[4, 2, 9], 247 | .[7, 6, 3], 248 | .[3, 9, 8], 249 | .[5, 2, 4], 250 | .[4, 5, 7], 251 | .[1, 9, 4], 252 | .[13, 7, 8], 253 | .[3, 7, 8], 254 | .[2, 3, 2], 255 | .[5, 1, 5], 256 | .[11, 4, 9], 257 | .[7, 9, 1], 258 | .[4, 4, 3], 259 | .[1, 6, 8], 260 | .[8, 8, 4], 261 | .[1, 1, 7], 262 | .[3, 4, 6], 263 | .[4, 2, 8], 264 | .[3, 5, 4], 265 | .[1, 4, 1], 266 | .[4, 9, 8], 267 | .[3, 5, 2], 268 | .[2, 2, 3], 269 | .[1, 6, 7], 270 | .[7, 1, 9], 271 | .[3, 7, 4], 272 | .[1, 4, 2], 273 | .[1, 6, 8], 274 | .[1, 5, 7], 275 | .[1, 6, 7], 276 | .[9, 4, 6], 277 | .[7, 9, 5], 278 | .[1, 4, 3], 279 | .[1, 5, 6], 280 | .[4, 3, 7], 281 | .[3, 3, 1], 282 | .[2, 2, 4], 283 | .[3, 1, 6], 284 | .[4, 5, 1], 285 | .[2, 5, 3], 286 | .[3, 6, 8], 287 | .[6, 7, 3], 288 | .[10, 3, 7], 289 | .[10, 6, 4], 290 | .[3, 1, 9], 291 | .[4, 7, 2], 292 | .[2, 3, 5], 293 | .[1, 3, 5], 294 | .[1, 1, 2], 295 | .[3, 9, 1], 296 | .[2, 1, 9], 297 | .[4, 2, 5], 298 | .[10, 4, 9], 299 | .[2, 8, 7], 300 | .[1, 2, 9], 301 | .[1, 9, 4], 302 | .[1, 1, 9], 303 | .[3, 7, 8], 304 | .[5, 9, 8], 305 | .[6, 5, 4], 306 | .[5, 9, 6], 307 | .[5, 8, 5], 308 | .[4, 5, 2], 309 | .[3, 7, 8], 310 | .[3, 9, 1], 311 | .[2, 5, 8], 312 | .[1, 4, 6], 313 | .[3, 6, 3], 314 | .[8, 4, 3], 315 | .[2, 6, 7], 316 | .[24, 8, 9], 317 | .[1, 6, 9], 318 | .[8, 9, 3], 319 | .[1, 1, 9], 320 | .[2, 8, 3], 321 | .[3, 9, 4], 322 | .[18, 3, 5], 323 | .[1, 2, 6], 324 | .[1, 6, 1], 325 | .[13, 9, 8], 326 | .[3, 4, 1], 327 | .[1, 4, 2], 328 | .[1, 5, 3], 329 | .[1, 9, 2], 330 | .[6, 2, 9], 331 | .[3, 3, 1], 332 | .[3, 7, 6], 333 | .[2, 1, 5], 334 | .[3, 6, 7], 335 | .[17, 8, 1], 336 | .[8, 5, 7], 337 | .[11, 7, 5], 338 | .[1, 4, 5], 339 | .[24, 1, 3], 340 | .[7, 5, 9], 341 | .[11, 5, 9], 342 | .[1, 5, 4], 343 | .[1, 5, 2], 344 | .[1, 4, 7], 345 | .[16, 3, 1], 346 | .[3, 5, 8], 347 | .[1, 9, 1], 348 | .[12, 9, 2], 349 | .[5, 3, 1], 350 | .[2, 8, 5], 351 | .[2, 3, 4], 352 | .[1, 8, 6], 353 | .[2, 5, 3], 354 | .[1, 6, 1], 355 | .[2, 4, 1], 356 | .[8, 2, 1], 357 | .[24, 1, 6], 358 | .[1, 9, 6], 359 | .[4, 2, 6], 360 | .[4, 3, 2], 361 | .[4, 9, 4], 362 | .[1, 7, 5], 363 | .[6, 9, 1], 364 | .[17, 6, 4], 365 | .[1, 1, 9], 366 | .[2, 9, 7], 367 | .[9, 6, 7], 368 | .[12, 7, 2], 369 | .[11, 1, 2], 370 | .[12, 4, 2], 371 | .[1, 1, 4], 372 | .[1, 5, 7], 373 | .[2, 6, 3], 374 | .[2, 4, 3], 375 | .[1, 1, 6], 376 | .[5, 4, 2], 377 | .[1, 7, 5], 378 | .[2, 6, 9], 379 | .[6, 2, 4], 380 | .[1, 5, 8], 381 | .[3, 4, 1], 382 | .[1, 8, 9], 383 | .[5, 4, 1], 384 | .[1, 1, 2], 385 | .[1, 3, 8], 386 | .[7, 1, 6], 387 | .[1, 8, 6], 388 | .[1, 3, 5], 389 | .[1, 1, 8], 390 | .[1, 4, 5], 391 | .[2, 5, 9], 392 | .[1, 8, 2], 393 | .[2, 6, 8], 394 | .[1, 8, 6], 395 | .[1, 6, 8], 396 | .[1, 8, 5], 397 | .[4, 9, 5], 398 | .[1, 9, 6], 399 | .[1, 6, 8], 400 | .[37, 2, 5], 401 | .[1, 2, 8], 402 | .[37, 5, 8], 403 | .[21, 8, 4], 404 | .[1, 3, 7], 405 | .[12, 4, 1], 406 | .[1, 7, 8], 407 | .[4, 6, 3], 408 | .[1, 4, 2], 409 | .[2, 2, 6], 410 | .[2, 3, 2], 411 | .[2, 2, 7], 412 | .[2, 7, 1], 413 | .[3, 5, 3], 414 | .[2, 2, 8], 415 | .[15, 8, 7], 416 | .[1, 7, 2], 417 | .[2, 5, 8], 418 | .[5, 4, 3], 419 | .[3, 6, 2], 420 | .[8, 1, 9], 421 | .[8, 9, 4], 422 | .[7, 8, 9], 423 | .[2, 8, 5], 424 | .[4, 1, 9], 425 | .[10, 3, 2], 426 | .[1, 6, 7], 427 | .[6, 7, 8], 428 | .[10, 4, 7], 429 | .[1, 3, 7], 430 | .[3, 9, 1], 431 | .[5, 8, 1], 432 | .[5, 2, 7], 433 | .[1, 4, 3], 434 | .[1, 5, 6], 435 | .[10, 1, 7], 436 | .[34, 7, 4], 437 | .[1, 6, 9], 438 | .[1, 7, 3], 439 | .[8, 4, 2], 440 | .[1, 9, 7], 441 | .[1, 7, 9], 442 | .[22, 4, 5], 443 | .[1, 3, 8], 444 | .[6, 5, 6], 445 | .[1, 8, 4], 446 | .[9, 9, 4], 447 | .[1, 3, 2], 448 | .[4, 2, 1], 449 | .[11, 2, 6], 450 | .[14, 4, 7], 451 | .[1, 2, 1], 452 | .[12, 5, 6], 453 | .[7, 7, 9], 454 | .[2, 5, 4], 455 | .[1, 8, 5], 456 | .[6, 6, 8], 457 | .[3, 7, 8], 458 | .[1, 2, 6], 459 | .[2, 4, 3], 460 | .[1, 3, 8], 461 | .[1, 2, 5], 462 | .[1, 3, 4], 463 | .[5, 8, 9], 464 | .[5, 1, 4], 465 | .[3, 8, 9], 466 | .[5, 4, 7], 467 | .[18, 6, 3], 468 | .[2, 8, 9], 469 | .[3, 6, 3], 470 | .[5, 7, 1], 471 | .[1, 6, 7], 472 | .[3, 9, 7], 473 | .[6, 7, 8], 474 | .[8, 9, 8], 475 | .[1, 7, 6], 476 | .[12, 3, 1], 477 | .[2, 9, 7], 478 | .[1, 8, 6], 479 | .[9, 8, 9], 480 | .[2, 8, 7], 481 | .[1, 7, 8], 482 | .[2, 1, 6], 483 | .[7, 1, 9], 484 | .[16, 9, 5], 485 | .[4, 3, 9], 486 | .[8, 1, 3], 487 | .[5, 5, 1], 488 | .[1, 4, 3], 489 | .[4, 7, 6], 490 | .[3, 8, 2], 491 | .[8, 3, 2], 492 | .[11, 2, 7], 493 | .[3, 6, 1], 494 | .[4, 3, 6], 495 | .[12, 5, 7], 496 | .[2, 3, 6], 497 | .[7, 1, 7], 498 | .[2, 5, 1], 499 | .[1, 9, 2], 500 | .[1, 5, 7], 501 | .[31, 7, 4], 502 | .[6, 9, 1], 503 | .[6, 4, 7], 504 | .[16, 4, 5], 505 | .[1, 9, 4], 506 | .[1, 7, 6], 507 | .[4, 4, 1], 508 | .[11, 5, 9], 509 | .[2, 5, 6], 510 | .[1, 2, 9], 511 | .[4, 5, 1], 512 | .[8, 9, 2], 513 | .[12, 6, 5], 514 | .[11, 5, 1], 515 | .[18, 1, 2], 516 | .[3, 4, 3], 517 | .[2, 6, 7], 518 | .[2, 9, 1], 519 | .[1, 5, 9], 520 | .[3, 9, 5], 521 | .[13, 2, 8], 522 | .[10, 8, 2], 523 | .[3, 8, 6], 524 | .[3, 3, 9], 525 | .[7, 1, 5], 526 | .[6, 5, 6], 527 | .[3, 1, 2], 528 | .[5, 7, 8], 529 | .[13, 2, 8], 530 | .[9, 6, 3], 531 | ]; 532 | }; 533 | --------------------------------------------------------------------------------