From dbb8aa35c85cacd797cd2407c2f09e1eb436642a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Val=C3=A9rien?= <60972074+brayevalerien@users.noreply.github.com> Date: Wed, 17 Jan 2024 11:03:06 +0100 Subject: [PATCH 1/2] added brayevalerien's solutions added my solutions to days 1-5 and 8-12. More to come if I can. --- 2023/01/brayevalerien/part1.v | 36 ++++++ 2023/01/brayevalerien/part2.v | 76 ++++++++++++ 2023/02/brayevalerien/part1.v | 67 ++++++++++ 2023/02/brayevalerien/part2.v | 64 ++++++++++ 2023/03/brayevalerien/part1.v | 198 ++++++++++++++++++++++++++++++ 2023/03/brayevalerien/part2.v | 224 ++++++++++++++++++++++++++++++++++ 2023/04/brayevalerien/part1.v | 54 ++++++++ 2023/04/brayevalerien/part2.v | 56 +++++++++ 2023/08/brayevalerien/part1.v | 85 +++++++++++++ 2023/08/brayevalerien/part2.v | 127 +++++++++++++++++++ 2023/09/brayevalerien/part1.v | 59 +++++++++ 2023/09/brayevalerien/part2.v | 64 ++++++++++ 2023/10/brayevalerien/part1.v | 147 ++++++++++++++++++++++ 2023/10/brayevalerien/part2.v | 161 ++++++++++++++++++++++++ 2023/11/brayevalerien/part1.v | 113 +++++++++++++++++ 2023/11/brayevalerien/part2.v | 148 ++++++++++++++++++++++ 16 files changed, 1679 insertions(+) create mode 100644 2023/01/brayevalerien/part1.v create mode 100644 2023/01/brayevalerien/part2.v create mode 100644 2023/02/brayevalerien/part1.v create mode 100644 2023/02/brayevalerien/part2.v create mode 100644 2023/03/brayevalerien/part1.v create mode 100644 2023/03/brayevalerien/part2.v create mode 100644 2023/04/brayevalerien/part1.v create mode 100644 2023/04/brayevalerien/part2.v create mode 100644 2023/08/brayevalerien/part1.v create mode 100644 2023/08/brayevalerien/part2.v create mode 100644 2023/09/brayevalerien/part1.v create mode 100644 2023/09/brayevalerien/part2.v create mode 100644 2023/10/brayevalerien/part1.v create mode 100644 2023/10/brayevalerien/part2.v create mode 100644 2023/11/brayevalerien/part1.v create mode 100644 2023/11/brayevalerien/part2.v diff --git a/2023/01/brayevalerien/part1.v b/2023/01/brayevalerien/part1.v new file mode 100644 index 0000000..875d66e --- /dev/null +++ b/2023/01/brayevalerien/part1.v @@ -0,0 +1,36 @@ +module main + +import os + +fn main() { + input_path := 'trebuchet-part1.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + mut sum := 0 // will contain the final result + for line in lines { + line_nums := get_line_nums(line) + sum += get_line_number(line_nums) + } + println('Final result: ${sum}') +} + +// Given a string, returns the array of all numbers in this string +fn get_line_nums(line string) []rune { + nums := []rune{len: 10, init: index.str()[0]} + mut line_nums := []rune{} + for c in line { + if rune(c) in nums { + line_nums << rune(c) + } + } + return line_nums +} + +// Given a array of digits as runes, returns the 2-digits number formed by +// the first digit in the array, concatenated with the last digit in the array. +fn get_line_number(nums []rune) int { + // runes cannot be concatenated easily. So first cast to string, concatenate and cast to int. + mut res := rune(nums[0]).str() + res += rune(nums[nums.len - 1]).str() + return res.int() +} diff --git a/2023/01/brayevalerien/part2.v b/2023/01/brayevalerien/part2.v new file mode 100644 index 0000000..911480b --- /dev/null +++ b/2023/01/brayevalerien/part2.v @@ -0,0 +1,76 @@ +module main + +import os + +fn main() { + input_path := 'trebuchet-part2.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + mut sum := 0 // will contain the final result + for line in lines { + line_nums := get_line_nums(line) + sum += get_line_number(line_nums) + } + println('Final result: ${sum}') +} + +fn index(a []string, s string) int { + mut i := 0 + for sp in a { + if sp == s { + return i + } else { + i += 1 + } + } + return -1 +} + +// Given a string, returns the array of all numbers in this string +// BUT! numbers can be spelled out with letters as well. +// e.g. 'xtwone3four' -> [`2`, `1`, `3`, `4`] +fn get_line_nums(line string) []rune { + nums := []rune{len: 10, init: index.str()[0]} + mut line_nums := []rune{} + for i in 0 .. line.len { + if rune(line[i]) in nums { + line_nums << rune(line[i]) + } else { + spelled_num := get_spelled_number(line[i..]) + if spelled_num != -1 { + line_nums << spelled_num.str()[0] + } + } + } + return line_nums +} + +// Given a string, returns the number that s starts with, or -1 if s does not start with a number +// e.g. 'xtwone3four' -> -1 +// 'twone3four' -> `2` +// '3four' -> -1 +fn get_spelled_number(s string) int { + spelled_nums := ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'] + min_len := 3 // minimum length of a spelled out number + max_len := 5 // maximum length of a spelled out number + for num_len in min_len .. (max_len + 1) { + if num_len <= s.len { + index_in_spelled_nums := index(spelled_nums, s[..num_len]) + if index_in_spelled_nums != -1 { + return index_in_spelled_nums + } + } else { + break + } + } + return -1 +} + +// Given a array of digits as runes, returns the 2-digits number formed by +// the first digit in the array, concatenated with the last digit in the array. +fn get_line_number(nums []rune) int { + // runes cannot be concatenated easily. So first cast to string, concatenate and cast to int. + mut res := rune(nums[0]).str() + res += rune(nums[nums.len - 1]).str() + return res.int() +} diff --git a/2023/02/brayevalerien/part1.v b/2023/02/brayevalerien/part1.v new file mode 100644 index 0000000..2b1b666 --- /dev/null +++ b/2023/02/brayevalerien/part1.v @@ -0,0 +1,67 @@ +module main + +import math +import os + +struct Game { + id int // game ID + red int // maximum number of red that appeared in the game + green int // maximum number of green that appeared in the game + blue int // maximum number of blue that appeared in the game +} + +// Constructs a Game from a line of the input file +fn Game.new(s string) Game { + split := s.rsplit(': ') + id := split[1][5..] + record := split[0].rsplit('; ') + mut red := 0 + mut green := 0 + mut blue := 0 + for set in record { + mut count_red := 0 + mut count_green := 0 + mut count_blue := 0 + counts := set.rsplit(', ') + for count in counts { + if count.ends_with(' red') { + count_red += count.split(' red')[0].int() + } else if count.ends_with(' green') { + count_green += count.split(' green')[0].int() + } else if count.ends_with(' blue') { + count_blue += count.split(' blue')[0].int() + } + } + red = math.max(red, count_red) + green = math.max(green, count_green) + blue = math.max(blue, count_blue) + } + return Game{ + id: id.int() + red: red + green: green + blue: blue + } +} + +fn is_valid(game Game) bool { + // maximum number of cubes of each color in the bag + max_red := 12 + max_green := 13 + max_blue := 14 + return game.red <= max_red && game.green <= max_green && game.blue <= max_blue +} + +fn main() { + input_path := 'cube.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + mut result := 0 + for line in lines { + game := Game.new(line) + if is_valid(game) { + result += game.id + } + } + println('Final result: ${result}') +} diff --git a/2023/02/brayevalerien/part2.v b/2023/02/brayevalerien/part2.v new file mode 100644 index 0000000..6990352 --- /dev/null +++ b/2023/02/brayevalerien/part2.v @@ -0,0 +1,64 @@ +module main + +import math +import os + +struct Game { + id int // game ID + red int // maximum number of red that appeared in the game + green int // maximum number of green that appeared in the game + blue int // maximum number of blue that appeared in the game +} + +// Constructs a Game from a line of the input file +fn Game.new(s string) Game { + split := s.rsplit(': ') + id := split[1][5..] + record := split[0].rsplit('; ') + mut red := 0 + mut green := 0 + mut blue := 0 + for set in record { + mut count_red := 0 + mut count_green := 0 + mut count_blue := 0 + counts := set.rsplit(', ') + for count in counts { + if count.ends_with(' red') { + count_red += count.split(' red')[0].int() + } else if count.ends_with(' green') { + count_green += count.split(' green')[0].int() + } else if count.ends_with(' blue') { + count_blue += count.split(' blue')[0].int() + } + } + red = math.max(red, count_red) + green = math.max(green, count_green) + blue = math.max(blue, count_blue) + } + return Game{ + id: id.int() + red: red + green: green + blue: blue + } +} + +// Return the power of a set of cubes of a game. +// The power of a set of cubes is equal to the numbers of red, green, and blue cubes multiplied together. +fn set_power(game Game) int { + // maximum number of cubes of each color in the bag + return game.red * game.green * game.blue +} + +fn main() { + input_path := 'cube.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + mut result := 0 + for line in lines { + game := Game.new(line) + result += set_power(game) + } + println('Final result: ${result}') +} diff --git a/2023/03/brayevalerien/part1.v b/2023/03/brayevalerien/part1.v new file mode 100644 index 0000000..58975a1 --- /dev/null +++ b/2023/03/brayevalerien/part1.v @@ -0,0 +1,198 @@ +module main + +import os + +// Schematic of an engine +struct Schematic { + grid []string + width int + height int +} + +// Describes a position in a schematic +struct Cursor { + schematic Schematic +mut: + x int + y int +} + +fn Schematic.new(lines []string) Schematic { + if lines.len == 0 { + panic('Cannot build grid of empty input.') + } + if lines[0].len == 0 { + panic('Cannot build grid from input with empty first line.') + } + return Schematic{ + grid: lines + width: lines[0].len + height: lines.len + } +} + +fn Cursor.new(schematic Schematic) Cursor { + return Cursor{ + schematic: schematic + x: 0 + y: 0 + } +} + +fn (schematic &Schematic) str() string { + mut result := '' + for line in schematic.grid { + result += line + '\n' + } + return result +} + +fn (cursor &Cursor) str() string { + return '(${cursor.x}, ${cursor.y})' +} + +// Moves cursor to the right. If the right border is touched, moves to the beginning of next line. +// Returns true iff the cursor is still in the grid. +fn (mut cursor Cursor) right() bool { + if cursor.y < cursor.schematic.width - 1 { + cursor.y += 1 + return true + } else { + if cursor.x < cursor.schematic.height - 1 { + cursor.y = 0 + cursor.x += 1 + return true + } else { + return false + } + } +} + +fn (mut cursor Cursor) move_to(x int, y int) { + cursor.schematic.check_dims(x, y) + cursor.x = x + cursor.y = y +} + +fn (schematic &Schematic) is_in_grid(x int, y int) bool { + return 0 <= x && x <= schematic.width - 1 && 0 <= y && y <= schematic.height - 1 +} + +// Makes sure a position (x, y) is inside the grid, panics otherwise. +fn (schematic &Schematic) check_dims(x int, y int) { + if !schematic.is_in_grid(x, y) { + panic('Cannot get element at (${x}, ${y}): grid dimensions are (${schematic.width}, ${schematic.height})') + } +} + +// Returns the rune at position (x, y), offset by (dir_x, dir_y) (that is (x+dir_x, y+dir_y)) +// (x, y) needs to be inside the grid, but (x+dir_x, y+dir_y) can be outside. +// In this case, will return `.`. This allows borders to be ignored. +fn (schematic &Schematic) get_at_off(x int, y int, dir_x int, dir_y int) rune { + schematic.check_dims(x, y) + if !schematic.is_in_grid(x + dir_x, y + dir_y) { + return `.` + } else { + return schematic.grid[x + dir_x][y + dir_y] + } +} + +// Returns the rune at position (x, y) +fn (schematic &Schematic) get_at(x int, y int) rune { + return schematic.get_at_off(x, y, 0, 0) +} + +// Returns true iff the provided rune is a digit. +fn is_num(r rune) bool { + nums := []rune{len: 10, init: index.str()[0]} + return r in nums +} + +// Returns true iff the provided rune is not a number and not `.`. +fn is_char(r rune) bool { + return !is_num(r) && r != `.` +} + +// Returns true iff the cursor is next to a character +fn (cursor &Cursor) has_char_next_to_it() bool { + // Possible directions: + // nw n ne + // w . e + // sw s se + dirs := [ + [-1, 0], // n + [-1, 1], // ne + [0, 1], // e + [1, 1], // se + [1, 0], // s + [1, -1], // sw + [0, -1], // w + [-1, -1], // nw + ] + for dir in dirs { + if is_char(cursor.schematic.get_at_off(cursor.x, cursor.y, dir[0], dir[1])) { + return true + } + } + return false +} + +// Returns the whole number on which the cursor is, and moves the cursor after the number. +// e.g. in '..457*.', cursor on 5: returns 457 and moves cursor to '*'. +fn (mut cursor Cursor) get_num_at() int { + // 1. Find num beginning + // 2. Find num ending + // 3. Concatenate all digits between beginning and ending + // 4. Move cursor to ending + mut beg := cursor.y + for 0 < beg { + if !is_num(cursor.schematic.get_at(cursor.x, beg - 1)) { + break + } else { + beg -= 1 + } + } + mut end := cursor.y + for end < cursor.schematic.width - 1 { + if !is_num(cursor.schematic.get_at(cursor.x, end + 1)) { + break + } else { + end += 1 + } + } + mut num := cursor.schematic.get_at(cursor.x, beg).str() + for y in beg + 1 .. end + 1 { + num += cursor.schematic.get_at(cursor.x, y).str() + } + cursor.move_to(cursor.x, end) + return num.int() +} + +// Returns the final result +fn (mut cursor Cursor) get_sum() int { + mut result := 0 + for { + // Check if cursor is a number. + // If it is not, do nothing. + // If it is, check all adjacent runes to find character + if is_num(cursor.schematic.get_at(cursor.x, cursor.y)) { + if cursor.has_char_next_to_it() { + result += cursor.get_num_at() + } + } + if !cursor.right() { + break + } + } + return result +} + +fn main() { + input_path := 'schematic.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + schematic := Schematic.new(lines) + mut cursor := Cursor.new(schematic) + + println('Final result: ${cursor.get_sum()}') +} diff --git a/2023/03/brayevalerien/part2.v b/2023/03/brayevalerien/part2.v new file mode 100644 index 0000000..1251439 --- /dev/null +++ b/2023/03/brayevalerien/part2.v @@ -0,0 +1,224 @@ +module main + +import os +import math + +// Schematic of an engine +struct Schematic { + grid []string + width int + height int +} + +// Describes a position in a schematic +struct Cursor { + schematic Schematic +mut: + x int + y int +} + +fn Schematic.new(lines []string) Schematic { + if lines.len == 0 { + panic('Cannot build grid of empty input.') + } + if lines[0].len == 0 { + panic('Cannot build grid from input with empty first line.') + } + return Schematic{ + grid: lines + width: lines[0].len + height: lines.len + } +} + +fn Cursor.new(schematic Schematic) Cursor { + return Cursor{ + schematic: schematic + x: 0 + y: 0 + } +} + +fn (schematic &Schematic) str() string { + mut result := '' + for line in schematic.grid { + result += line + '\n' + } + return result +} + +fn (cursor &Cursor) str() string { + return '(${cursor.x}, ${cursor.y})' +} + +// Moves cursor to the right. If the right border is touched, moves to the beginning of next line. +// Returns true iff the cursor is still in the grid. +fn (mut cursor Cursor) right() bool { + if cursor.y < cursor.schematic.width - 1 { + cursor.y += 1 + return true + } else { + if cursor.x < cursor.schematic.height - 1 { + cursor.y = 0 + cursor.x += 1 + return true + } else { + return false + } + } +} + +fn (mut cursor Cursor) move_to(x int, y int) { + cursor.schematic.check_dims(x, y) + cursor.x = x + cursor.y = y +} + +fn (schematic &Schematic) is_in_grid(x int, y int) bool { + return 0 <= x && x <= schematic.width - 1 && 0 <= y && y <= schematic.height - 1 +} + +// Makes sure a position (x, y) is inside the grid, panics otherwise. +fn (schematic &Schematic) check_dims(x int, y int) { + if !schematic.is_in_grid(x, y) { + panic('Cannot get element at (${x}, ${y}): grid dimensions are (${schematic.width}, ${schematic.height})') + } +} + +// Returns the rune at position (x, y), offset by (dir_x, dir_y) (that is (x+dir_x, y+dir_y)) +// (x, y) needs to be inside the grid, but (x+dir_x, y+dir_y) can be outside. +// In this case, will return `.`. This allows borders to be ignored. +fn (schematic &Schematic) get_at_off(x int, y int, dir_x int, dir_y int) rune { + schematic.check_dims(x, y) + if !schematic.is_in_grid(x + dir_x, y + dir_y) { + return `.` + } else { + return schematic.grid[x + dir_x][y + dir_y] + } +} + +// Returns the rune at position (x, y) +fn (schematic &Schematic) get_at(x int, y int) rune { + return schematic.get_at_off(x, y, 0, 0) +} + +// Returns true iff the provided rune is a digit. +fn is_num(r rune) bool { + nums := []rune{len: 10, init: index.str()[0]} + return r in nums +} + +// Given a list of coordinates, returns a list of these coordinates +// But where only one coordinate is kept per group of contiguous coordinates along the y axis. +// e.g. [[7, 6], [9, 5], [9, 6]] -> [[7, 6], [9, 6]] +fn keep_single_contiguous(coords [][]int) [][]int { + if coords.len < 2 { + return coords + } + mut groups := [][][]int{} // groups of contiguous coordinates + mut current_group := [coords[0]] + for i in 1 .. coords.len { + previous := coords[i - 1] + current := coords[i] + if previous[0] == current[0] && previous[1] + 1 == current[1] { + // same line and next to each other -> same group + current_group << current + } else { + // beginning a new group + groups << current_group + current_group = [current] + } + } + groups << current_group + mut result := [][]int{} + for group in groups { + result << group[0] + } + return result +} + +// Returns the gear ratio iff cursor is on a gear symbol (return 0 if not) +// i.e. `*` rune with exactly two adjacent numbers. +// Gear ratio is then the product of these numbers +fn (cursor &Cursor) gear_ratio() int { + if cursor.schematic.get_at(cursor.x, cursor.y) != `*` { + return 0 + } + println('gear_ratio >>> Found `*` at (${cursor.x}, ${cursor.y}).') + mut digits_at := [][]int{} // arrays of indices around cursor where this is a digit + for x in math.max(cursor.x - 1, 0) .. math.min(cursor.x + 2, cursor.schematic.height) { + for y in math.max(cursor.y - 1, 0) .. math.min(cursor.y + 2, cursor.schematic.width) { + if is_num(cursor.schematic.get_at(x, y)) { + digits_at << [x, y] + } + } + } + nums_at := keep_single_contiguous(digits_at) + println('gear_ratio >>> -> Numbers around at: ${nums_at}') + if nums_at.len != 2 { + println('gear_ratio >>> X not multiplying: digits at ${digits_at}') + return 0 + } + mut cursor_cp := Cursor.new(cursor.schematic) + cursor_cp.move_to(nums_at[0][0], nums_at[0][1]) + first := cursor_cp.get_num_at() + cursor_cp = Cursor.new(cursor.schematic) + cursor_cp.move_to(nums_at[1][0], nums_at[1][1]) + second := cursor_cp.get_num_at() + println('gear_ratio >>> -> Multiplying ${first} and ${second}') + return first * second +} + +// Returns the whole number on which the cursor is, and moves the cursor after the number. +// e.g. in '..457*.', cursor on 5: returns 457 and moves cursor to '*'. +fn (mut cursor Cursor) get_num_at() int { + // 1. Find num beginning + // 2. Find num ending + // 3. Concatenate all digits between beginning and ending + // 4. Move cursor to ending + mut beg := cursor.y + for 0 < beg { + if !is_num(cursor.schematic.get_at(cursor.x, beg - 1)) { + break + } else { + beg -= 1 + } + } + mut end := cursor.y + for end < cursor.schematic.width - 1 { + if !is_num(cursor.schematic.get_at(cursor.x, end + 1)) { + break + } else { + end += 1 + } + } + mut num := cursor.schematic.get_at(cursor.x, beg).str() + for y in beg + 1 .. end + 1 { + num += cursor.schematic.get_at(cursor.x, y).str() + } + cursor.move_to(cursor.x, end) + return num.int() +} + +// Returns the final result +fn (mut cursor Cursor) get_sum() int { + mut result := 0 + for { + result += cursor.gear_ratio() + if !cursor.right() { + break + } + } + return result +} + +fn main() { + input_path := 'schematic.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + schematic := Schematic.new(lines) + mut cursor := Cursor.new(schematic) + println(schematic) + println('Final result: ${cursor.get_sum()}') +} diff --git a/2023/04/brayevalerien/part1.v b/2023/04/brayevalerien/part1.v new file mode 100644 index 0000000..e22553f --- /dev/null +++ b/2023/04/brayevalerien/part1.v @@ -0,0 +1,54 @@ +module main + +import os +import math + +fn main() { + input_path := 'scratchcards.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + mut sum := 0 // will contain the final result + for line in lines { + winning := get_winning(line) + nums := get_nums(line) + sum += card_value(winning, nums) + } + println('Final result: ${sum}') +} + +fn get_winning(line string) []int { + card := line.split(': ')[1].split(' | ') + winning_str := card[0].split(' ') + mut winning := []int{} + for s in winning_str { + if s != '' { + winning << s.int() + } + } + return winning +} + +fn get_nums(line string) []int { + card := line.split(': ')[1].split(' | ') + nums_str := card[1].split(' ') + mut nums := []int{} + for s in nums_str { + if s != '' { + nums << s.int() + } + } + return nums +} + +fn card_value(winning []int, nums []int) int { + mut matched := 0 // count of matched numbers + for num in nums { + if num in winning { + matched += 1 + } + } + if matched == 0 { + return 0 + } + return int(math.pow(2, matched-1)) +} diff --git a/2023/04/brayevalerien/part2.v b/2023/04/brayevalerien/part2.v new file mode 100644 index 0000000..836931d --- /dev/null +++ b/2023/04/brayevalerien/part2.v @@ -0,0 +1,56 @@ +module main + +import os +import arrays +import math + +fn main() { + input_path := 'scratchcards.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + mut copies := []int{len: lines.len, init: 1} // number of remaining copies per card + for i in 0 .. lines.len { + line := lines[i] + winning := get_winning(line) + nums := get_nums(line) + for j in i + 1 .. math.min(i + 1 + matches(winning, nums), lines.len) { + copies[j] += 1 * copies[i] + } + } + println('Final result: ${arrays.sum(copies)!}') +} + +fn get_winning(line string) []int { + card := line.split(': ')[1].split(' | ') + winning_str := card[0].split(' ') + mut winning := []int{} + for s in winning_str { + if s != '' { + winning << s.int() + } + } + return winning +} + +fn get_nums(line string) []int { + card := line.split(': ')[1].split(' | ') + nums_str := card[1].split(' ') + mut nums := []int{} + for s in nums_str { + if s != '' { + nums << s.int() + } + } + return nums +} + +// Number of winning numbers in a card +fn matches(winning []int, nums []int) int { + mut matched := 0 // count of matched numbers + for num in nums { + if num in winning { + matched += 1 + } + } + return matched +} diff --git a/2023/08/brayevalerien/part1.v b/2023/08/brayevalerien/part1.v new file mode 100644 index 0000000..4766789 --- /dev/null +++ b/2023/08/brayevalerien/part1.v @@ -0,0 +1,85 @@ +module main + +import os + +fn main() { + input_path := 'haunted_wasteland-part1.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + sequence := Sequence{lines[0].split(''), 0} + network := build_network(lines[2..]) + res := traverse_network(network, sequence) + println('Final result: ${res}') +} + +// Sequence of instructions, looped +struct Sequence { + arr []string // array of 'L' and 'R' +mut: + idx int +} + +fn (mut iter Sequence) next() ?string { + if iter.arr.len <= iter.idx { + iter.idx = 0 + } + defer { + iter.idx++ + } + return iter.arr[iter.idx] +} + +struct Node { + name string + left string + right string +} + +fn (node &Node) str() string { + return '${node.name}: (${node.left}, ${node.right})' +} + +fn (node &Node) is_final() bool { + return node.name == 'ZZZ' +} + +// Builds a network from the list of its nodes described as in the input file +fn build_network(nodes []string) []Node { + mut network := []Node{} + for node in nodes { + network << Node{node.split(' = ')[0], node.split('(')[1].split(',')[0], node.split(', ')[1].split(')')[0]} + } + return network +} + +fn (network []Node) get_node_by_name(name string) ?Node { + for node in network { + if node.name == name { + return node + } + } + return none +} + +// Goes through the whole network using a sequence of directions and returns the number of steps taken +fn traverse_network(network []Node, sequence Sequence) int { + mut res := 0 + mut current := network.get_node_by_name('AAA') or { panic('Could not find node named "AAA"') } + for dir in sequence { + if current.is_final() { + return res + } else { + if dir == 'L' { + current = network.get_node_by_name(current.left) or { + panic('Could not find node named ${current.left}') + } + } else { + current = network.get_node_by_name(current.right) or { + panic('Could not find node named ${current.right}') + } + } + res++ + } + } + return res +} diff --git a/2023/08/brayevalerien/part2.v b/2023/08/brayevalerien/part2.v new file mode 100644 index 0000000..4e764fc --- /dev/null +++ b/2023/08/brayevalerien/part2.v @@ -0,0 +1,127 @@ +module main + +import os +import math + +fn main() { + input_path := 'haunted_wasteland-part2.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + sequence := Sequence{lines[0].split(''), 0} + network := build_network(lines[2..]) + res := traverse_network(network, sequence) + println('Final result: ${res}') +} + +// Sequence of instructions, looped +struct Sequence { + arr []string // array of 'L' and 'R' +mut: + idx int +} + +fn (mut iter Sequence) next() ?string { + if iter.arr.len <= iter.idx { + iter.idx = 0 + } + defer { + iter.idx++ + } + return iter.arr[iter.idx] +} + +struct Node { + name string + left string + right string +} + +fn (node &Node) str() string { + return '${node.name}: (${node.left}, ${node.right})' +} + +fn (node &Node) is_start() bool { + return node.name.ends_with('A') +} + +fn (node &Node) is_final() bool { + return node.name.ends_with('Z') +} + +// Builds a network from the list of its nodes described as in the input file +fn build_network(nodes []string) []Node { + mut network := []Node{} + for node in nodes { + network << Node{node.split(' = ')[0], node.split('(')[1].split(',')[0], node.split(', ')[1].split(')')[0]} + } + return network +} + +// Returns all starting nodes of the network, i.e. nodes with a name ending with 'A' +fn (network []Node) get_start_nodes() []Node { + mut start_nodes := []Node{} + for node in network { + if node.is_start() { + start_nodes << node + } + } + return start_nodes +} + +fn (network []Node) get_node_by_name(name string) ?Node { + for node in network { + if node.name == name { + return node + } + } + return none +} + +// Given a current node and a direction, takes a step into the direction, from the current node +// in the network and returns the resulting node. +fn (network []Node) step(node Node, dir string) Node { + if dir == 'L' { + return network.get_node_by_name(node.left) or { + panic('Could not find node named ${node.left}') + } + } else { + return network.get_node_by_name(node.right) or { + panic('Could not find node named ${node.right}') + } + } +} + +fn all_final(nodes []Node) bool { + for node in nodes { + if !node.is_final() { + return false + } + } + return true +} + +fn array_lcm(nums []int) i64 { + mut res := i64(1) + for n in nums { + res = math.lcm(res, n) + } + return res +} + +// Goes through the whole network using a sequence of directions and returns the number of steps taken +fn traverse_network(network []Node, sequence Sequence) i64 { + mut paths := network.get_start_nodes() + mut lengths := []int{len: paths.len} + for i in 0 .. paths.len { + mut length := 0 + for dir in sequence { + if paths[i].is_final() { + lengths[i] = length + break + } + paths[i] = network.step(paths[i], dir) + length++ + } + } + return array_lcm(lengths) +} diff --git a/2023/09/brayevalerien/part1.v b/2023/09/brayevalerien/part1.v new file mode 100644 index 0000000..ea5fc34 --- /dev/null +++ b/2023/09/brayevalerien/part1.v @@ -0,0 +1,59 @@ +module main + +import os +import math +import arrays + +fn main() { + input_path := 'mirage_maintenance.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + mut sum := 0 + sequences := get_sequences(lines) + for sequence in sequences { + sum += extrapolate(sequence) + } + println('Final result: ${sum}') +} + +fn get_sequences(lines []string) [][]int { + mut result := [][]int{} + for line in lines { + result << line.split(' ').map(fn (s string) int { + return s.int() + }) + } + return result +} + +fn all_zeroes(sequence []int) bool { + return arrays.sum(sequence.map(fn (x int) int { + return math.abs(x) + })) or { panic('Could not check if all elements of ${sequence} were 0.') } == 0 +} + +// Given a sequence, finds the next number +fn extrapolate(sequence []int) int { + dif := get_all_differences(sequence) + last := []int{len: dif.len - 1, init: dif[index][dif[index].len - 1]} + return arrays.sum(last) or { panic('Could not extrapolate sequence ${sequence}') } +} + +// Return the list of all differences of the sequence +fn get_all_differences(sequence []int) [][]int { + mut res := [][]int{} + res << sequence + for { + dif := differences(res[res.len - 1]) + res << dif + if all_zeroes(dif) { + return res + } + } + return res +} + +// Given a sequence, returns the sequence of differences +fn differences(sequence []int) []int { + return []int{len: sequence.len - 1, init: sequence[index + 1] - sequence[index]} +} diff --git a/2023/09/brayevalerien/part2.v b/2023/09/brayevalerien/part2.v new file mode 100644 index 0000000..75c5f90 --- /dev/null +++ b/2023/09/brayevalerien/part2.v @@ -0,0 +1,64 @@ +module main + +import os +import math +import arrays + +fn main() { + input_path := 'mirage_maintenance.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + mut sum := 0 + sequences := get_sequences(lines) + for sequence in sequences { + sum += extrapolate(sequence) + } + println('Final result: ${sum}') +} + +fn get_sequences(lines []string) [][]int { + mut result := [][]int{} + for line in lines { + result << line.split(' ').map(fn (s string) int { + return s.int() + }) + } + return result +} + +fn all_zeroes(sequence []int) bool { + return arrays.sum(sequence.map(fn (x int) int { + return math.abs(x) + })) or { panic('Could not check if all elements of ${sequence} were 0.') } == 0 +} + +// Given a sequence, finds the next number +fn extrapolate(sequence []int) int { + dif := get_all_differences(sequence) + // first is the reversed list of first elements in the differences list + first := []int{len: dif.len - 1, init: dif[index][0]}.reverse() + mut res := first[0] + for i in 1 .. first.len { + res = first[i] - res + } + return res +} + +// Return the list of all differences of the sequence +fn get_all_differences(sequence []int) [][]int { + mut res := [][]int{} + res << sequence + for { + dif := differences(res[res.len - 1]) + res << dif + if all_zeroes(dif) { + return res + } + } + return res +} + +// Given a sequence, returns the sequence of differences +fn differences(sequence []int) []int { + return []int{len: sequence.len - 1, init: sequence[index + 1] - sequence[index]} +} diff --git a/2023/10/brayevalerien/part1.v b/2023/10/brayevalerien/part1.v new file mode 100644 index 0000000..708325b --- /dev/null +++ b/2023/10/brayevalerien/part1.v @@ -0,0 +1,147 @@ +module main + +import os + +fn main() { + input_path := 'pipe_maze-part1.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + terrain := build_terrain(lines) + loop := terrain.get_loop() + // The final result is the length of the loop divided by two (farthest point from the animal) + println('Final result: ${loop.len / 2}') +} + +// 0 -----> x +// | +// | +// v +// y +struct Coords { + x int + y int +} + +struct Terrain { + pipes [][]string + width int + height int + animal Coords +} + +fn (coords &Coords) str() string { + return '(${coords.x}, ${coords.y})' +} + +fn (terrain &Terrain) str() string { + mut res := '${terrain.height} x ${terrain.width} terrain with animal at ${terrain.animal} \n' + for line in terrain.pipes[0..terrain.height - 1] { + res += line.join(' ') + '\n' + } + res += terrain.pipes[terrain.height - 1].join(' ') + return res +} + +fn (coords &Coords) get_north() Coords { + return Coords{coords.x, coords.y - 1} +} + +fn (coords &Coords) get_east() Coords { + return Coords{coords.x + 1, coords.y} +} + +fn (coords &Coords) get_south() Coords { + return Coords{coords.x, coords.y + 1} +} + +fn (coords &Coords) get_west() Coords { + return Coords{coords.x - 1, coords.y} +} + +fn build_terrain(lines []string) Terrain { + pipes := [][]string{len: lines.len, init: lines[index].split('')} + width := pipes[0].len + height := pipes.len + for x in 0 .. width { + for y in 0 .. height { + if pipes[y][x] == 'S' { + animal := Coords{x, y} + return Terrain{pipes, width, height, animal} + } + } + } + return Terrain{pipes, width, height, Coords{-1, -1}} +} + +fn (terrain &Terrain) is_valid(position Coords) bool { + return 0 <= position.x && 0 <= position.y && position.x < terrain.width + && position.y < terrain.height +} + +fn (terrain &Terrain) get_pipe(position Coords) !string { + if terrain.is_valid(position) { + return terrain.pipes[position.y][position.x] + } + return error('Position ${position} is not in terrain (of dimension ${terrain.height} x ${terrain.width}).') +} + +// Given a position in the terrain, returns the array of connected positions. +// Takes in account the shape of the pipe in this position and the shape of surrounding pipes +fn (terrain &Terrain) get_connected(position Coords) []Coords { + mut res := []Coords{} + current_pipe := terrain.get_pipe(position) or { panic(err) } + if current_pipe in ['S', '|', 'L', 'J'] { // current pipe might be able to connect to north + if north_pipe := terrain.get_pipe(position.get_north()) { + if north_pipe in ['S', '|', '7', 'F'] { // north pipe can connect to current pipe + res << position.get_north() + } + } + } + if current_pipe in ['S', '-', 'L', 'F'] { // current pipe might be able to connect to east + if east_pipe := terrain.get_pipe(position.get_east()) { + if east_pipe in ['S', '-', '7', 'J'] { // east pipe can connect to current pipe + res << position.get_east() + } + } + } + if current_pipe in ['S', '|', '7', 'F'] { // current pipe might be able to connect to south + if south_pipe := terrain.get_pipe(position.get_south()) { + if south_pipe in ['S', '|', 'L', 'J'] { // south pipe can connect to current pipe + res << position.get_south() + } + } + } + if current_pipe in ['S', '-', 'J', '7'] { // current pipe might be able to connect to west + if west_pipe := terrain.get_pipe(position.get_west()) { + if west_pipe in ['S', '-', 'L', 'F'] { // west pipe can connect to current pipe + res << position.get_west() + } + } + } + return res +} + +// Returns the list of all Coords in the loop. +// Element i is next to element i+1 and element 0 is the animal. +fn (terrain &Terrain) get_loop() []Coords { + // Key idea to go through the loop: + // Start from the position of the animal and at any point keep in memory two position: + // - previous: the position where we are comming from + // - next: the position where we are going + // At each step, find the connected pipes (there should be 2) and select the one which is different from the one at the previous position. + // Stop when the position of the animal is reached, meaning we went through the whole loop once. + mut res := [terrain.animal] + mut connected := terrain.get_connected(terrain.animal) + mut previous := connected[0] + mut next := connected[1] + for { + if next == terrain.animal { + return res + } + previous = res[res.len - 1] + res << next + connected = terrain.get_connected(next) + next = if connected[0] != previous { connected[0] } else { connected[1] } + } + return res +} diff --git a/2023/10/brayevalerien/part2.v b/2023/10/brayevalerien/part2.v new file mode 100644 index 0000000..9a26d97 --- /dev/null +++ b/2023/10/brayevalerien/part2.v @@ -0,0 +1,161 @@ +module main + +import os + +fn main() { + input_path := 'pipe_maze-part2.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + terrain := build_terrain(lines) + loop := terrain.get_loop() + println('Final result: ${terrain.count_inside_loop(loop)}') +} + +// 0 -----> x +// | +// | +// v +// y +struct Coords { + x int + y int +} + +struct Terrain { + pipes [][]string + width int + height int + animal Coords +} + +fn (coords &Coords) str() string { + return '(${coords.x}, ${coords.y})' +} + +fn (terrain &Terrain) str() string { + mut res := '${terrain.height} x ${terrain.width} terrain with animal at ${terrain.animal} \n' + for line in terrain.pipes[0..terrain.height - 1] { + res += line.join(' ') + '\n' + } + res += terrain.pipes[terrain.height - 1].join(' ') + return res +} + +fn (coords &Coords) get_north() Coords { + return Coords{coords.x, coords.y - 1} +} + +fn (coords &Coords) get_east() Coords { + return Coords{coords.x + 1, coords.y} +} + +fn (coords &Coords) get_south() Coords { + return Coords{coords.x, coords.y + 1} +} + +fn (coords &Coords) get_west() Coords { + return Coords{coords.x - 1, coords.y} +} + +fn build_terrain(lines []string) Terrain { + pipes := [][]string{len: lines.len, init: lines[index].split('')} + width := pipes[0].len + height := pipes.len + for x in 0 .. width { + for y in 0 .. height { + if pipes[y][x] == 'S' { + animal := Coords{x, y} + return Terrain{pipes, width, height, animal} + } + } + } + return Terrain{pipes, width, height, Coords{-1, -1}} +} + +fn (terrain &Terrain) is_valid(position Coords) bool { + return 0 <= position.x && 0 <= position.y && position.x < terrain.width + && position.y < terrain.height +} + +fn (terrain &Terrain) get_pipe(position Coords) !string { + if terrain.is_valid(position) { + return terrain.pipes[position.y][position.x] + } + return error('Position ${position} is not in terrain (of dimension ${terrain.height} x ${terrain.width}).') +} + +// Given a position in the terrain, returns the array of connected positions. +// Takes in account the shape of the pipe in this position and the shape of surrounding pipes +fn (terrain &Terrain) get_connected(position Coords) []Coords { + mut res := []Coords{} + current_pipe := terrain.get_pipe(position) or { panic(err) } + if current_pipe in ['S', '|', 'L', 'J'] { // current pipe might be able to connect to north + if north_pipe := terrain.get_pipe(position.get_north()) { + if north_pipe in ['S', '|', '7', 'F'] { // north pipe can connect to current pipe + res << position.get_north() + } + } + } + if current_pipe in ['S', '-', 'L', 'F'] { // current pipe might be able to connect to east + if east_pipe := terrain.get_pipe(position.get_east()) { + if east_pipe in ['S', '-', '7', 'J'] { // east pipe can connect to current pipe + res << position.get_east() + } + } + } + if current_pipe in ['S', '|', '7', 'F'] { // current pipe might be able to connect to south + if south_pipe := terrain.get_pipe(position.get_south()) { + if south_pipe in ['S', '|', 'L', 'J'] { // south pipe can connect to current pipe + res << position.get_south() + } + } + } + if current_pipe in ['S', '-', 'J', '7'] { // current pipe might be able to connect to west + if west_pipe := terrain.get_pipe(position.get_west()) { + if west_pipe in ['S', '-', 'L', 'F'] { // west pipe can connect to current pipe + res << position.get_west() + } + } + } + return res +} + +// Returns the list of all Coords in the loop. +// Element i is next to element i+1 and element 0 is the animal. +fn (terrain &Terrain) get_loop() []Coords { + // Key idea to go through the loop: + // Start from the position of the animal and at any point keep in memory two position: + // - previous: the position where we are comming from + // - next: the position where we are going + // At each step, find the connected pipes (there should be 2) and select the one which is different from the one at the previous position. + // Stop when the position of the animal is reached, meaning we went through the whole loop once. + mut res := [terrain.animal] + mut connected := terrain.get_connected(terrain.animal) + mut previous := connected[0] + mut next := connected[1] + for { + if next == terrain.animal { + return res + } + previous = res[res.len - 1] + res << next + connected = terrain.get_connected(next) + next = if connected[0] != previous { connected[0] } else { connected[1] } + } + return res +} + +// Returns the number of tiles inside the given loop. +fn (terrain &Terrain) count_inside_loop(loop []Coords) int { + // For this part we use the shoelace formula, which helps us compute the number of tiles inside the loop, including the loop itself. Then we use Pick's theorem to remove the loop. + // https://en.wikipedia.org/wiki/Shoelace_formula + // https://en.wikipedia.org/wiki/Pick%27s_theorem + mut area := 0 + for i in 0 .. loop.len { + current := loop[i] + previous := if i == 0 { loop[loop.len - 1] } else { loop[i - 1] } + next := if i == loop.len - 1 { loop[0] } else { loop[i + 1] } + area += current.x * (previous.y - next.y) + } + return area / 2 - loop.len / 2 + 1 +} diff --git a/2023/11/brayevalerien/part1.v b/2023/11/brayevalerien/part1.v new file mode 100644 index 0000000..b6a4755 --- /dev/null +++ b/2023/11/brayevalerien/part1.v @@ -0,0 +1,113 @@ +module main + +import os +import math + +fn main() { + input_path := 'stargazing.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + mut cosmos := Cosmos.new(lines) + cosmos.expand() + cosmos.locate_galaxies() + println('Final result: ${cosmos.sum_of_distances()}') +} + +// 0 -----> x +// | +// | +// v +// y +struct Coords { + x int + y int +} + +struct Cosmos { +mut: + field [][]string + galaxies []Coords +} + +fn (coords &Coords) str() string { + return '(${coords.x}, ${coords.y})' +} + +fn Cosmos.new(lines []string) Cosmos { + mut field := [][]string{} + for line in lines { + field << line.split('') + } + return Cosmos{field, []Coords{}} +} + +fn (cosmos Cosmos) str() string { + mut res := '' + for line in cosmos.field[0..cosmos.field.len - 1] { + res += line.join('') + '\n' + } + res += cosmos.field[cosmos.field.len - 1].join('') + return res +} + +// Doubles the lines and columns of cosmos.field where there is no galaxy. +fn (mut cosmos Cosmos) expand() { + // Idea: + // 1. expand lines + // 2. transpose + // 3. expand lines (old columns) + // 4. transpose again + cosmos.field = transpose_field(expand_field_lines(transpose_field(expand_field_lines(cosmos.field)))) +} + +fn expand_field_lines(field [][]string) [][]string { + mut res := [][]string{} + for line in field { + res << line + if '#' !in line { // no galaxy in line, add it twice + res << line + } + } + return res +} + +// Given a field, returns the same field but where lines and columns have been swapped. +fn transpose_field(field [][]string) [][]string { + mut res := [][]string{len: field[0].len, init: []string{len: field.len}} + for y in 0 .. field.len { + for x in 0 .. field[y].len { + res[x][y] = field[y][x] + } + } + return res +} + +// Fills the 'galaxies' field of cosmos with the coordinates of the galaxies ('#') in cosmos.field. +fn (mut cosmos Cosmos) locate_galaxies() { + mut galaxies := []Coords{} + for y, line in cosmos.field { + for x, object in line { + if object == '#' { + galaxies << Coords{x, y} + } + } + } + cosmos.galaxies = galaxies +} + +// Finds the sum of all distances between pairs of galaxies (each pair is used only once). +fn (cosmos Cosmos) sum_of_distances() int { + mut res := 0 + // 1. Construct the array of pairs of galaxies + // 2. For each pair, compute the Manhattan distance and add it to the result + for i in 0 .. cosmos.galaxies.len - 1 { + for j in (i + 1) .. cosmos.galaxies.len { + res += dist_manhattan(cosmos.galaxies[i], cosmos.galaxies[j]) + } + } + return res +} + +fn dist_manhattan(pos1 Coords, pos2 Coords) int { + return math.abs(pos2.x - pos1.x) + math.abs(pos2.y - pos1.y) +} diff --git a/2023/11/brayevalerien/part2.v b/2023/11/brayevalerien/part2.v new file mode 100644 index 0000000..22de594 --- /dev/null +++ b/2023/11/brayevalerien/part2.v @@ -0,0 +1,148 @@ +module main + +import os +import math + +fn main() { + input_path := 'stargazing.input' + + lines := os.read_lines(input_path) or { panic('Could not read input file.') } + mut cosmos := Cosmos.new(lines) + expansion_factor := 1000000 // number of times the empty lines and columns are duplicated + cosmos.expand(expansion_factor) + println(cosmos) + cosmos.locate_galaxies() + println('Final result: ${cosmos.sum_of_distances()}') +} + +// 0 -----> x +// | +// | +// v +// y +struct Coords { + x i64 + y i64 +} + +struct Cosmos { +mut: + field [][]string + galaxies []Coords + expanded_lines []i64 + expanded_columns []i64 + expansion_factor i64 +} + +fn (coords &Coords) str() string { + return '(${coords.x}, ${coords.y})' +} + +fn Cosmos.new(lines []string) Cosmos { + mut field := [][]string{} + for line in lines { + field << line.split('') + } + return Cosmos{field, []Coords{}, []i64{}, []i64{}, 1} +} + +fn (cosmos Cosmos) str() string { + mut res := 'expanded lines: ${cosmos.expanded_lines} | expanded columns: ${cosmos.expanded_columns}\n' + for line in cosmos.field[0..cosmos.field.len - 1] { + res += line.join('') + '\n' + } + res += cosmos.field[cosmos.field.len - 1].join('') + return res +} + +// Doubles the lines and columns of cosmos.field where there is no galaxy. +fn (mut cosmos Cosmos) expand(factor i64) { + // Idea: + // 1. store expanded lines in cosmos.expanded_lines + // 2. transpose + // 3. store expand lines (old columns) in cosmos.expanded_columns + // 4. transpose again + cosmos.expanded_lines = expand_field_lines(cosmos.field) + transposed_field := transpose_field(cosmos.field) + cosmos.expanded_columns = expand_field_lines(transposed_field) + cosmos.expansion_factor = factor +} + +fn expand_field_lines(field [][]string) []i64 { + mut res := []i64{} + for i, line in field { + if '#' !in line { // no galaxy in line, add it twice + res << i + } + } + return res +} + +// Given a field, returns the same field but where lines and columns have been swapped. +fn transpose_field(field [][]string) [][]string { + mut res := [][]string{len: field[0].len, init: []string{len: field.len}} + for y in 0 .. field.len { + for x in 0 .. field[y].len { + res[x][y] = field[y][x] + } + } + return res +} + +// Fills the 'galaxies' field of cosmos with the coordinates of the galaxies ('#') in cosmos.field. +fn (mut cosmos Cosmos) locate_galaxies() { + mut galaxies := []Coords{} + for y, line in cosmos.field { + for x, object in line { + if object == '#' { + galaxies << Coords{x, y} + } + } + } + cosmos.galaxies = galaxies +} + +// Finds the sum of all distances between pairs of galaxies (each pair is used only once). +fn (cosmos Cosmos) sum_of_distances() i64 { + mut res := i64(0) + // 1. Construct the array of pairs of galaxies + // 2. For each pair, compute the Manhattan distance and add it to the result + for i in 0 .. cosmos.galaxies.len - 1 { + for j in (i + 1) .. cosmos.galaxies.len { + res += cosmos.dist_manhattan(cosmos.galaxies[i], cosmos.galaxies[j]) + } + } + return res +} + +fn (cosmos Cosmos) dist_manhattan(pos1 Coords, pos2 Coords) i64 { + return cosmos.dx(pos1, pos2) + cosmos.dy(pos1, pos2) +} + +fn (cosmos Cosmos) dx(pos1 Coords, pos2 Coords) i64 { + min_x := math.min(pos1.x, pos2.x) + max_x := math.max(pos1.x, pos2.x) + mut res := i64(0) + for x in min_x .. max_x { + if x in cosmos.expanded_columns { + res += cosmos.expansion_factor + } else { + res += 1 + } + } + return res +} + +fn (cosmos Cosmos) dy(pos1 Coords, pos2 Coords) i64 { + min_y := math.min(pos1.y, pos2.y) + max_y := math.max(pos1.y, pos2.y) + mut res := i64(0) + for y in min_y .. max_y { + if y in cosmos.expanded_lines { + res += cosmos.expansion_factor + } else { + res += 1 + } + } + return res +} From bb04d94eb515f06a93e9c116be361fe7d1c90219 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 1 Dec 2024 00:41:02 +0200 Subject: [PATCH 2/2] Fix path to input, make `v run verify.v brayevalerien` pass. --- 2023/01/brayevalerien/part1.v | 2 +- 2023/01/brayevalerien/part2.v | 2 +- 2023/02/brayevalerien/part1.v | 2 +- 2023/02/brayevalerien/part2.v | 2 +- 2023/03/brayevalerien/part1.v | 2 +- 2023/03/brayevalerien/part2.v | 2 +- 2023/04/brayevalerien/part1.v | 2 +- 2023/04/brayevalerien/part2.v | 2 +- 2023/08/brayevalerien/part1.v | 2 +- 2023/08/brayevalerien/part2.v | 2 +- 2023/09/brayevalerien/part1.v | 2 +- 2023/09/brayevalerien/part2.v | 2 +- 2023/10/brayevalerien/part1.v | 2 +- 2023/10/brayevalerien/part2.v | 2 +- 2023/11/brayevalerien/part1.v | 2 +- 2023/11/brayevalerien/part2.v | 2 +- known_outputs/2023/01/brayevalerien/part1.out | 1 + known_outputs/2023/01/brayevalerien/part2.out | 1 + known_outputs/2023/02/brayevalerien/part1.out | 1 + known_outputs/2023/02/brayevalerien/part2.out | 1 + known_outputs/2023/03/brayevalerien/part1.out | 1 + known_outputs/2023/03/brayevalerien/part2.out | 21 +++++++++++++++++++ known_outputs/2023/04/brayevalerien/part1.out | 1 + known_outputs/2023/04/brayevalerien/part2.out | 1 + known_outputs/2023/08/brayevalerien/part1.out | 1 + known_outputs/2023/08/brayevalerien/part2.out | 1 + known_outputs/2023/09/brayevalerien/part1.out | 1 + known_outputs/2023/09/brayevalerien/part2.out | 1 + known_outputs/2023/10/brayevalerien/part1.out | 1 + known_outputs/2023/10/brayevalerien/part2.out | 1 + known_outputs/2023/11/brayevalerien/part1.out | 1 + known_outputs/2023/11/brayevalerien/part2.out | 12 +++++++++++ 32 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 known_outputs/2023/01/brayevalerien/part1.out create mode 100644 known_outputs/2023/01/brayevalerien/part2.out create mode 100644 known_outputs/2023/02/brayevalerien/part1.out create mode 100644 known_outputs/2023/02/brayevalerien/part2.out create mode 100644 known_outputs/2023/03/brayevalerien/part1.out create mode 100644 known_outputs/2023/03/brayevalerien/part2.out create mode 100644 known_outputs/2023/04/brayevalerien/part1.out create mode 100644 known_outputs/2023/04/brayevalerien/part2.out create mode 100644 known_outputs/2023/08/brayevalerien/part1.out create mode 100644 known_outputs/2023/08/brayevalerien/part2.out create mode 100644 known_outputs/2023/09/brayevalerien/part1.out create mode 100644 known_outputs/2023/09/brayevalerien/part2.out create mode 100644 known_outputs/2023/10/brayevalerien/part1.out create mode 100644 known_outputs/2023/10/brayevalerien/part2.out create mode 100644 known_outputs/2023/11/brayevalerien/part1.out create mode 100644 known_outputs/2023/11/brayevalerien/part2.out diff --git a/2023/01/brayevalerien/part1.v b/2023/01/brayevalerien/part1.v index 875d66e..3844073 100644 --- a/2023/01/brayevalerien/part1.v +++ b/2023/01/brayevalerien/part1.v @@ -3,7 +3,7 @@ module main import os fn main() { - input_path := 'trebuchet-part1.input' + input_path := '../trebuchet-part1.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } mut sum := 0 // will contain the final result diff --git a/2023/01/brayevalerien/part2.v b/2023/01/brayevalerien/part2.v index 911480b..0e060f3 100644 --- a/2023/01/brayevalerien/part2.v +++ b/2023/01/brayevalerien/part2.v @@ -3,7 +3,7 @@ module main import os fn main() { - input_path := 'trebuchet-part2.input' + input_path := '../trebuchet-part2.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } mut sum := 0 // will contain the final result diff --git a/2023/02/brayevalerien/part1.v b/2023/02/brayevalerien/part1.v index 2b1b666..eea9045 100644 --- a/2023/02/brayevalerien/part1.v +++ b/2023/02/brayevalerien/part1.v @@ -53,7 +53,7 @@ fn is_valid(game Game) bool { } fn main() { - input_path := 'cube.input' + input_path := '../cube.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } mut result := 0 diff --git a/2023/02/brayevalerien/part2.v b/2023/02/brayevalerien/part2.v index 6990352..7779799 100644 --- a/2023/02/brayevalerien/part2.v +++ b/2023/02/brayevalerien/part2.v @@ -52,7 +52,7 @@ fn set_power(game Game) int { } fn main() { - input_path := 'cube.input' + input_path := '../cube.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } mut result := 0 diff --git a/2023/03/brayevalerien/part1.v b/2023/03/brayevalerien/part1.v index 58975a1..ada107d 100644 --- a/2023/03/brayevalerien/part1.v +++ b/2023/03/brayevalerien/part1.v @@ -188,7 +188,7 @@ fn (mut cursor Cursor) get_sum() int { } fn main() { - input_path := 'schematic.input' + input_path := '../schematic.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } schematic := Schematic.new(lines) diff --git a/2023/03/brayevalerien/part2.v b/2023/03/brayevalerien/part2.v index 1251439..1394d5b 100644 --- a/2023/03/brayevalerien/part2.v +++ b/2023/03/brayevalerien/part2.v @@ -214,7 +214,7 @@ fn (mut cursor Cursor) get_sum() int { } fn main() { - input_path := 'schematic.input' + input_path := '../schematic.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } schematic := Schematic.new(lines) diff --git a/2023/04/brayevalerien/part1.v b/2023/04/brayevalerien/part1.v index e22553f..e02605a 100644 --- a/2023/04/brayevalerien/part1.v +++ b/2023/04/brayevalerien/part1.v @@ -4,7 +4,7 @@ import os import math fn main() { - input_path := 'scratchcards.input' + input_path := '../scratchcards.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } mut sum := 0 // will contain the final result diff --git a/2023/04/brayevalerien/part2.v b/2023/04/brayevalerien/part2.v index 836931d..b98c25e 100644 --- a/2023/04/brayevalerien/part2.v +++ b/2023/04/brayevalerien/part2.v @@ -5,7 +5,7 @@ import arrays import math fn main() { - input_path := 'scratchcards.input' + input_path := '../scratchcards.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } mut copies := []int{len: lines.len, init: 1} // number of remaining copies per card diff --git a/2023/08/brayevalerien/part1.v b/2023/08/brayevalerien/part1.v index 4766789..46da9bb 100644 --- a/2023/08/brayevalerien/part1.v +++ b/2023/08/brayevalerien/part1.v @@ -3,7 +3,7 @@ module main import os fn main() { - input_path := 'haunted_wasteland-part1.input' + input_path := '../haunted_wasteland-part1.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } sequence := Sequence{lines[0].split(''), 0} diff --git a/2023/08/brayevalerien/part2.v b/2023/08/brayevalerien/part2.v index 4e764fc..4be0a84 100644 --- a/2023/08/brayevalerien/part2.v +++ b/2023/08/brayevalerien/part2.v @@ -4,7 +4,7 @@ import os import math fn main() { - input_path := 'haunted_wasteland-part2.input' + input_path := '../haunted_wasteland-part2.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } sequence := Sequence{lines[0].split(''), 0} diff --git a/2023/09/brayevalerien/part1.v b/2023/09/brayevalerien/part1.v index ea5fc34..ce0b763 100644 --- a/2023/09/brayevalerien/part1.v +++ b/2023/09/brayevalerien/part1.v @@ -5,7 +5,7 @@ import math import arrays fn main() { - input_path := 'mirage_maintenance.input' + input_path := '../mirage_maintenance.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } mut sum := 0 diff --git a/2023/09/brayevalerien/part2.v b/2023/09/brayevalerien/part2.v index 75c5f90..c93a17a 100644 --- a/2023/09/brayevalerien/part2.v +++ b/2023/09/brayevalerien/part2.v @@ -5,7 +5,7 @@ import math import arrays fn main() { - input_path := 'mirage_maintenance.input' + input_path := '../mirage_maintenance.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } mut sum := 0 diff --git a/2023/10/brayevalerien/part1.v b/2023/10/brayevalerien/part1.v index 708325b..5dfb919 100644 --- a/2023/10/brayevalerien/part1.v +++ b/2023/10/brayevalerien/part1.v @@ -3,7 +3,7 @@ module main import os fn main() { - input_path := 'pipe_maze-part1.input' + input_path := '../pipe_maze-part1.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } terrain := build_terrain(lines) diff --git a/2023/10/brayevalerien/part2.v b/2023/10/brayevalerien/part2.v index 9a26d97..d0b2b67 100644 --- a/2023/10/brayevalerien/part2.v +++ b/2023/10/brayevalerien/part2.v @@ -3,7 +3,7 @@ module main import os fn main() { - input_path := 'pipe_maze-part2.input' + input_path := '../pipe_maze-part2.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } terrain := build_terrain(lines) diff --git a/2023/11/brayevalerien/part1.v b/2023/11/brayevalerien/part1.v index b6a4755..3525a5f 100644 --- a/2023/11/brayevalerien/part1.v +++ b/2023/11/brayevalerien/part1.v @@ -4,7 +4,7 @@ import os import math fn main() { - input_path := 'stargazing.input' + input_path := '../stargazing.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } mut cosmos := Cosmos.new(lines) diff --git a/2023/11/brayevalerien/part2.v b/2023/11/brayevalerien/part2.v index 22de594..824c55a 100644 --- a/2023/11/brayevalerien/part2.v +++ b/2023/11/brayevalerien/part2.v @@ -4,7 +4,7 @@ import os import math fn main() { - input_path := 'stargazing.input' + input_path := '../stargazing.input' lines := os.read_lines(input_path) or { panic('Could not read input file.') } mut cosmos := Cosmos.new(lines) diff --git a/known_outputs/2023/01/brayevalerien/part1.out b/known_outputs/2023/01/brayevalerien/part1.out new file mode 100644 index 0000000..78db580 --- /dev/null +++ b/known_outputs/2023/01/brayevalerien/part1.out @@ -0,0 +1 @@ +Final result: 142 diff --git a/known_outputs/2023/01/brayevalerien/part2.out b/known_outputs/2023/01/brayevalerien/part2.out new file mode 100644 index 0000000..2255539 --- /dev/null +++ b/known_outputs/2023/01/brayevalerien/part2.out @@ -0,0 +1 @@ +Final result: 281 diff --git a/known_outputs/2023/02/brayevalerien/part1.out b/known_outputs/2023/02/brayevalerien/part1.out new file mode 100644 index 0000000..17fc7c3 --- /dev/null +++ b/known_outputs/2023/02/brayevalerien/part1.out @@ -0,0 +1 @@ +Final result: 8 diff --git a/known_outputs/2023/02/brayevalerien/part2.out b/known_outputs/2023/02/brayevalerien/part2.out new file mode 100644 index 0000000..81e11f5 --- /dev/null +++ b/known_outputs/2023/02/brayevalerien/part2.out @@ -0,0 +1 @@ +Final result: 2286 diff --git a/known_outputs/2023/03/brayevalerien/part1.out b/known_outputs/2023/03/brayevalerien/part1.out new file mode 100644 index 0000000..13861f2 --- /dev/null +++ b/known_outputs/2023/03/brayevalerien/part1.out @@ -0,0 +1 @@ +Final result: 4361 diff --git a/known_outputs/2023/03/brayevalerien/part2.out b/known_outputs/2023/03/brayevalerien/part2.out new file mode 100644 index 0000000..cfc2c99 --- /dev/null +++ b/known_outputs/2023/03/brayevalerien/part2.out @@ -0,0 +1,21 @@ +467..114.. +...*...... +..35..633. +......#... +617*...... +.....+.58. +..592..... +......755. +...$.*.... +.664.598.. + +gear_ratio >>> Found `*` at (1, 3). +gear_ratio >>> -> Numbers around at: [[0, 2], [2, 2]] +gear_ratio >>> -> Multiplying 467 and 35 +gear_ratio >>> Found `*` at (4, 3). +gear_ratio >>> -> Numbers around at: [[4, 2]] +gear_ratio >>> X not multiplying: digits at [[4, 2]] +gear_ratio >>> Found `*` at (8, 5). +gear_ratio >>> -> Numbers around at: [[7, 6], [9, 5]] +gear_ratio >>> -> Multiplying 755 and 598 +Final result: 467835 diff --git a/known_outputs/2023/04/brayevalerien/part1.out b/known_outputs/2023/04/brayevalerien/part1.out new file mode 100644 index 0000000..9a274cc --- /dev/null +++ b/known_outputs/2023/04/brayevalerien/part1.out @@ -0,0 +1 @@ +Final result: 13 diff --git a/known_outputs/2023/04/brayevalerien/part2.out b/known_outputs/2023/04/brayevalerien/part2.out new file mode 100644 index 0000000..815e3b5 --- /dev/null +++ b/known_outputs/2023/04/brayevalerien/part2.out @@ -0,0 +1 @@ +Final result: 30 diff --git a/known_outputs/2023/08/brayevalerien/part1.out b/known_outputs/2023/08/brayevalerien/part1.out new file mode 100644 index 0000000..59e59fc --- /dev/null +++ b/known_outputs/2023/08/brayevalerien/part1.out @@ -0,0 +1 @@ +Final result: 2 diff --git a/known_outputs/2023/08/brayevalerien/part2.out b/known_outputs/2023/08/brayevalerien/part2.out new file mode 100644 index 0000000..e3fce76 --- /dev/null +++ b/known_outputs/2023/08/brayevalerien/part2.out @@ -0,0 +1 @@ +Final result: 6 diff --git a/known_outputs/2023/09/brayevalerien/part1.out b/known_outputs/2023/09/brayevalerien/part1.out new file mode 100644 index 0000000..86d23ad --- /dev/null +++ b/known_outputs/2023/09/brayevalerien/part1.out @@ -0,0 +1 @@ +Final result: 114 diff --git a/known_outputs/2023/09/brayevalerien/part2.out b/known_outputs/2023/09/brayevalerien/part2.out new file mode 100644 index 0000000..59e59fc --- /dev/null +++ b/known_outputs/2023/09/brayevalerien/part2.out @@ -0,0 +1 @@ +Final result: 2 diff --git a/known_outputs/2023/10/brayevalerien/part1.out b/known_outputs/2023/10/brayevalerien/part1.out new file mode 100644 index 0000000..17fc7c3 --- /dev/null +++ b/known_outputs/2023/10/brayevalerien/part1.out @@ -0,0 +1 @@ +Final result: 8 diff --git a/known_outputs/2023/10/brayevalerien/part2.out b/known_outputs/2023/10/brayevalerien/part2.out new file mode 100644 index 0000000..443d4a5 --- /dev/null +++ b/known_outputs/2023/10/brayevalerien/part2.out @@ -0,0 +1 @@ +Final result: 10 diff --git a/known_outputs/2023/11/brayevalerien/part1.out b/known_outputs/2023/11/brayevalerien/part1.out new file mode 100644 index 0000000..ee37e0c --- /dev/null +++ b/known_outputs/2023/11/brayevalerien/part1.out @@ -0,0 +1 @@ +Final result: 374 diff --git a/known_outputs/2023/11/brayevalerien/part2.out b/known_outputs/2023/11/brayevalerien/part2.out new file mode 100644 index 0000000..9cca453 --- /dev/null +++ b/known_outputs/2023/11/brayevalerien/part2.out @@ -0,0 +1,12 @@ +expanded lines: [3, 7] | expanded columns: [2, 5, 8] +...#...... +.......#.. +#......... +.......... +......#... +.#........ +.........# +.......... +.......#.. +#...#..... +Final result: 82000210