Refactor 2015 days 01 to 10
Split parts into separate files and remove some unused files
This commit is contained in:
parent
13d1191a82
commit
3c57921438
4
.gitignore
vendored
4
.gitignore
vendored
@ -1 +1,5 @@
|
||||
*.bak
|
||||
|
||||
# build artifacts
|
||||
part_one
|
||||
part_two
|
||||
|
8
2015/day-01/common.rb
Normal file
8
2015/day-01/common.rb
Normal file
@ -0,0 +1,8 @@
|
||||
INPUT = File.readlines(ARGV.first, chomp: true).first.chars
|
||||
|
||||
def match_paren(char)
|
||||
case char
|
||||
when "(" then 1
|
||||
when ")" then -1
|
||||
end
|
||||
end
|
@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "pathname"
|
||||
|
||||
INPUTS = ["inputs/sample.txt", "inputs/puzzle.txt"].map { |x| Pathname(x) }
|
||||
|
||||
def match_paren(char)
|
||||
case char
|
||||
when "(" then 1
|
||||
when ")" then -1
|
||||
end
|
||||
end
|
||||
|
||||
def solve_part_1(content)
|
||||
content.chars.sum { |x| match_paren(x) }
|
||||
end
|
||||
|
||||
def solve_part_2(content)
|
||||
content.chars.each.with_index(1).reduce(0) do |acc, (char, index)|
|
||||
acc += match_paren(char)
|
||||
return index if acc == -1
|
||||
acc
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def main(files)
|
||||
files.each do |file|
|
||||
puts "File: #{file}"
|
||||
file.read.lines do |line|
|
||||
next if line.empty?
|
||||
result = solve_part_1(line.chomp)
|
||||
puts " Floor: #{result}"
|
||||
result = solve_part_2(line.chomp)
|
||||
puts " Basement Floor Position: #{result}"
|
||||
puts " -" * 15
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
main(INPUTS)
|
10
2015/day-01/part_one.rb
Executable file
10
2015/day-01/part_one.rb
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require_relative "common"
|
||||
|
||||
def part_one(chars)
|
||||
chars.sum { |x| match_paren(x) }
|
||||
end
|
||||
|
||||
result = part_one(INPUT)
|
||||
puts result
|
15
2015/day-01/part_two.rb
Executable file
15
2015/day-01/part_two.rb
Executable file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require_relative "common"
|
||||
|
||||
def part_two(chars)
|
||||
chars.each.with_index(1).reduce(0) do |acc, (char, index)|
|
||||
acc += match_paren(char)
|
||||
return index if acc == -1
|
||||
acc
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
result = part_two(INPUT)
|
||||
puts result
|
6
2015/day-02/common.rb
Normal file
6
2015/day-02/common.rb
Normal file
@ -0,0 +1,6 @@
|
||||
INPUT = File.readlines(ARGV.first, chomp: true).lazy.map do |num|
|
||||
dims = num.split("x").map(&:to_i)
|
||||
[sides(*dims), *dims]
|
||||
end
|
||||
|
||||
def sides(l, w, h) = [l * w, w * h, h * l]
|
@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "pathname"
|
||||
|
||||
INPUTS = [
|
||||
Pathname("inputs/test.txt"),
|
||||
Pathname("inputs/puzzle.txt"),
|
||||
].freeze
|
||||
|
||||
def parse_input(data)
|
||||
data.chomp.split("x").map(&:to_i)
|
||||
end
|
||||
|
||||
def sides(length, width, height)
|
||||
[length * width, width * height, height * length]
|
||||
end
|
||||
|
||||
def solve_part_1(input)
|
||||
input.lines.sum do |line|
|
||||
sides = sides(*parse_input(line))
|
||||
smallest = sides.min
|
||||
sides.sum { |x| x * 2 } + smallest
|
||||
end
|
||||
end
|
||||
|
||||
def solve_part_2(input)
|
||||
input.lines.sum do |line|
|
||||
length, width, height = parse_input(line)
|
||||
sides = sides(length, width, height)
|
||||
circumference =
|
||||
case sides.map.with_index.min.last
|
||||
when 0 then length * 2 + width * 2
|
||||
when 1 then width * 2 + height * 2
|
||||
when 2 then height * 2 + length * 2
|
||||
end
|
||||
circumference + length * width * height
|
||||
end
|
||||
end
|
||||
|
||||
def main(files)
|
||||
files.each do |file|
|
||||
puts "File: #{file}"
|
||||
content = file.read
|
||||
|
||||
result = solve_part_1(content.chomp)
|
||||
puts " Wrapping Paper: #{result}"
|
||||
puts " -" * 15
|
||||
|
||||
result = solve_part_2(content.chomp)
|
||||
puts " Ribbon Length: #{result}"
|
||||
puts " -" * 15
|
||||
end
|
||||
end
|
||||
|
||||
main(INPUTS)
|
12
2015/day-02/part_one.rb
Executable file
12
2015/day-02/part_one.rb
Executable file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require_relative "common"
|
||||
|
||||
def part_one(dims)
|
||||
dims.sum do |sides, *|
|
||||
smallest = sides.min
|
||||
sides.sum { |x| x * 2 } + smallest
|
||||
end
|
||||
end
|
||||
|
||||
puts part_one(INPUT)
|
17
2015/day-02/part_two.rb
Executable file
17
2015/day-02/part_two.rb
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require_relative "common"
|
||||
|
||||
def part_two(dims)
|
||||
dims.sum do |sides, length, width, height|
|
||||
circumference =
|
||||
case sides.map.with_index.min.last
|
||||
when 0 then length * 2 + width * 2
|
||||
when 1 then width * 2 + height * 2
|
||||
when 2 then height * 2 + length * 2
|
||||
end
|
||||
circumference + length * width * height
|
||||
end
|
||||
end
|
||||
|
||||
puts part_two(INPUT)
|
18
2015/day-03/common.rb
Normal file
18
2015/day-03/common.rb
Normal file
@ -0,0 +1,18 @@
|
||||
INPUT = File
|
||||
.readlines(ARGV.first, chomp: true)
|
||||
.lazy.map do |line|
|
||||
line.each_char.map { |d| position_modifier(d) }
|
||||
end
|
||||
|
||||
def position_modifier(direction)
|
||||
case direction
|
||||
when ">" then [1, 0]
|
||||
when "<" then [-1, 0]
|
||||
when "^" then [0, 1]
|
||||
when "v" then [0, -1]
|
||||
end
|
||||
end
|
||||
|
||||
def apply_direction(position, change)
|
||||
position.map.with_index { |v, i| v + change[i] }
|
||||
end
|
@ -1,78 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "pathname"
|
||||
require "set"
|
||||
|
||||
INPUTS = [
|
||||
Pathname("inputs/test.txt"),
|
||||
Pathname("inputs/puzzle.txt"),
|
||||
].freeze
|
||||
|
||||
def position_modifier(direction)
|
||||
case direction
|
||||
when ">" then [1, 0]
|
||||
when "<" then [-1, 0]
|
||||
when "^" then [0, 1]
|
||||
when "v" then [0, -1]
|
||||
end
|
||||
end
|
||||
|
||||
def apply_direction(position, change)
|
||||
position.map.with_index { |v, i| v + change[i] }
|
||||
end
|
||||
|
||||
def next_position(position, direction)
|
||||
apply_direction(position, position_modifier(direction))
|
||||
end
|
||||
|
||||
def solve_part_1(input)
|
||||
input.lines.map do |line|
|
||||
puts " " + "-" * 10
|
||||
|
||||
position = [0, 0]
|
||||
|
||||
houses =
|
||||
line.chomp.each_char.each_with_object(Set[position]) do |direction, visited|
|
||||
next_pos = next_position(position, direction)
|
||||
visited.add(next_pos)
|
||||
position = next_pos
|
||||
end
|
||||
|
||||
puts " Houses: #{houses.size}"
|
||||
end
|
||||
end
|
||||
|
||||
def solve_part_2(input)
|
||||
input.lines.map do |line|
|
||||
puts " " + "-" * 10
|
||||
|
||||
position_s = [0, 0]
|
||||
position_r = position_s.dup
|
||||
|
||||
houses =
|
||||
line.chomp.each_char.each_with_object(Set[position_s.dup]).with_index(1) do |(direction, visited), index|
|
||||
if index.even?
|
||||
next_pos = next_position(position_s, direction)
|
||||
visited.add(next_pos)
|
||||
position_s = next_pos
|
||||
else
|
||||
next_pos = next_position(position_r, direction)
|
||||
visited.add(next_pos)
|
||||
position_r = next_pos
|
||||
end
|
||||
end
|
||||
|
||||
puts " Houses: #{houses.size}"
|
||||
end
|
||||
end
|
||||
|
||||
def main(files)
|
||||
files.each do |file|
|
||||
puts "File: #{file}"
|
||||
solve_part_1(file.read.chomp)
|
||||
puts "=" * 15
|
||||
solve_part_2(file.read.chomp)
|
||||
end
|
||||
end
|
||||
|
||||
main(INPUTS)
|
17
2015/day-03/part_one.rb
Executable file
17
2015/day-03/part_one.rb
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "set"
|
||||
require_relative "common"
|
||||
|
||||
def part_one(chars)
|
||||
position = [0, 0]
|
||||
chars
|
||||
.each_with_object(Set[position]) do |direction, visited|
|
||||
next_pos = apply_direction(position, direction)
|
||||
visited.add(next_pos)
|
||||
position = next_pos
|
||||
end
|
||||
.size
|
||||
end
|
||||
|
||||
puts part_one(INPUT.first)
|
26
2015/day-03/part_two.rb
Executable file
26
2015/day-03/part_two.rb
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "set"
|
||||
require_relative "common"
|
||||
|
||||
def part_two(chars)
|
||||
position_s = [0, 0]
|
||||
position_r = position_s.dup
|
||||
|
||||
chars
|
||||
.each_with_object(Set[position_s.dup])
|
||||
.with_index(1) do |(direction, visited), index|
|
||||
if index.even?
|
||||
next_pos = apply_direction(position_s, direction)
|
||||
visited.add(next_pos)
|
||||
position_s = next_pos
|
||||
else
|
||||
next_pos = apply_direction(position_r, direction)
|
||||
visited.add(next_pos)
|
||||
position_r = next_pos
|
||||
end
|
||||
end
|
||||
.size
|
||||
end
|
||||
|
||||
puts part_two(INPUT.first)
|
1
2015/day-04/.gitignore
vendored
1
2015/day-04/.gitignore
vendored
@ -1 +0,0 @@
|
||||
main
|
54
2015/day-04/common.rs
Normal file
54
2015/day-04/common.rs
Normal file
@ -0,0 +1,54 @@
|
||||
mod md5;
|
||||
mod threadpool;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||
pub type ThreadPool = threadpool::ThreadPool<Vec<usize>>;
|
||||
|
||||
pub fn extract_result(pool: &ThreadPool) -> Option<usize> {
|
||||
let mut smallest = None;
|
||||
while let Some(result) = pool.next_result().unwrap() {
|
||||
let result = result.into_iter().min();
|
||||
if !result.is_none() && (smallest.is_none() || smallest > result) {
|
||||
smallest = result;
|
||||
}
|
||||
}
|
||||
smallest
|
||||
}
|
||||
|
||||
pub fn find_suffix_with_zeroes(input: &str, amount: usize) -> usize {
|
||||
let pool = ThreadPool::with_threads(8);
|
||||
|
||||
let mut start = 1;
|
||||
let mut tries = 8;
|
||||
|
||||
let test_string = "0".repeat(amount);
|
||||
|
||||
loop {
|
||||
if tries == 0 {
|
||||
match extract_result(&pool) {
|
||||
None => tries = 7,
|
||||
Some(v) => return v,
|
||||
}
|
||||
}
|
||||
|
||||
let test_string = test_string.clone();
|
||||
let input = input.to_owned();
|
||||
pool.add_task(move || {
|
||||
(start..)
|
||||
.into_iter()
|
||||
.take(20_000)
|
||||
.filter_map(|i| {
|
||||
let secret = format!("{}{}", input, i);
|
||||
let hexdigest = md5::hexdigest(&secret);
|
||||
if hexdigest.starts_with(&test_string) {
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
tries -= 1;
|
||||
start += 20_000;
|
||||
}
|
||||
}
|
18
2015/day-04/part_one.rs
Normal file
18
2015/day-04/part_one.rs
Normal file
@ -0,0 +1,18 @@
|
||||
mod common;
|
||||
|
||||
use std::env;
|
||||
use std::fs::read_to_string;
|
||||
|
||||
use common::{find_suffix_with_zeroes, Result};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let path = env::args().skip(1).next().unwrap();
|
||||
let input = read_to_string(path)?;
|
||||
let input = input.lines().next().unwrap().to_owned();
|
||||
|
||||
let result = find_suffix_with_zeroes(&input, 5);
|
||||
|
||||
println!("{}", result);
|
||||
|
||||
Ok(())
|
||||
}
|
18
2015/day-04/part_two.rs
Normal file
18
2015/day-04/part_two.rs
Normal file
@ -0,0 +1,18 @@
|
||||
mod common;
|
||||
|
||||
use std::env;
|
||||
use std::fs::read_to_string;
|
||||
|
||||
use common::{find_suffix_with_zeroes, Result};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let path = env::args().skip(1).next().unwrap();
|
||||
let input = read_to_string(path)?;
|
||||
let input = input.lines().next().unwrap().to_owned();
|
||||
|
||||
let result = find_suffix_with_zeroes(&input, 6);
|
||||
|
||||
println!("{}", result);
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
mod md5;
|
||||
mod threadpool;
|
||||
|
||||
use threadpool::ThreadPool;
|
||||
|
||||
macro_rules! infile {
|
||||
[$path:literal] => { ($path, include_str!($path)) }
|
||||
}
|
||||
|
||||
const INPUTS: &[(&str, &str)] = &[
|
||||
infile!("../inputs/test.txt"),
|
||||
infile!("../inputs/puzzle.txt"),
|
||||
];
|
||||
|
||||
fn extract_result(pool: &ThreadPool<Vec<usize>>) -> Option<usize> {
|
||||
let mut smallest = None;
|
||||
while let Some(result) = pool.next_result().unwrap() {
|
||||
let result = result.into_iter().min();
|
||||
if !result.is_none() && (smallest.is_none() || smallest > result) {
|
||||
smallest = result;
|
||||
}
|
||||
}
|
||||
smallest
|
||||
}
|
||||
|
||||
fn solve_part_1(pool: &ThreadPool<Vec<usize>>, input: &str) -> usize {
|
||||
let mut start = 1;
|
||||
let mut tries = 7;
|
||||
|
||||
loop {
|
||||
if tries == 0 {
|
||||
match extract_result(&pool) {
|
||||
None =>{ tries = 7 },
|
||||
Some(v) => return v,
|
||||
}
|
||||
}
|
||||
|
||||
let input = input.to_string();
|
||||
pool.add_task(move || {
|
||||
(start..).into_iter()
|
||||
.take(20_000)
|
||||
.filter_map(|i| {
|
||||
let secret = format!("{}{}", input, i);
|
||||
let hexdigest = md5::hexdigest(&secret);
|
||||
if hexdigest.starts_with("00000") { Some(i) } else { None }
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
tries -= 1;
|
||||
start += 20_000;
|
||||
}
|
||||
}
|
||||
|
||||
fn solve_part_2(pool: &ThreadPool<Vec<usize>>, input: &str) -> usize {
|
||||
let mut start = 1;
|
||||
let mut tries = 8;
|
||||
|
||||
loop {
|
||||
if tries == 0 {
|
||||
match extract_result(&pool) {
|
||||
None =>{ tries = 7 },
|
||||
Some(v) => return v,
|
||||
}
|
||||
}
|
||||
|
||||
let input = input.to_string();
|
||||
pool.add_task(move || {
|
||||
(start..).into_iter()
|
||||
.take(20_000)
|
||||
.filter_map(|i| {
|
||||
let secret = format!("{}{}", input, i);
|
||||
let hexdigest = md5::hexdigest(&secret);
|
||||
if hexdigest.starts_with("000000") { Some(i) } else { None }
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
tries -= 1;
|
||||
start += 20_000;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let pool = ThreadPool::with_threads(8);
|
||||
|
||||
for (path, input) in INPUTS {
|
||||
println!("File: {}", path);
|
||||
|
||||
for line in input.split('\n') {
|
||||
if line.is_empty() { continue }
|
||||
|
||||
println!("\tInput: {}", line);
|
||||
|
||||
let solution = solve_part_1(&pool, &line);
|
||||
println!("\t\tFive leading zeroes: {}", solution);
|
||||
|
||||
let solution = solve_part_2(&pool, &line);
|
||||
println!("\t\tSix leading zeroes: {}", solution);
|
||||
}
|
||||
}
|
||||
}
|
9
2015/day-05/common.rb
Normal file
9
2015/day-05/common.rb
Normal file
@ -0,0 +1,9 @@
|
||||
LINES = File.each_line(ARGV.first, chomp: true)
|
||||
|
||||
def count_nice(lines)
|
||||
LINES.sum do |line|
|
||||
# line = line.chomp # chomp: true keyword argument emits warnings
|
||||
next 0 if line.empty?
|
||||
yield(line) ? 1 : 0
|
||||
end
|
||||
end
|
@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env -S ruby
|
||||
|
||||
require "pathname"
|
||||
require "set"
|
||||
|
||||
INPUTS = [
|
||||
Pathname("inputs/test.txt"),
|
||||
Pathname("inputs/puzzle.txt"),
|
||||
].freeze
|
||||
VOWELS = %[a e i o u].freeze
|
||||
|
||||
def vowel_count(input)
|
||||
input.each_char.sum do |char|
|
||||
VOWELS.include?(char) ? 1 : 0
|
||||
end
|
||||
end
|
||||
|
||||
def twin_letters(input)
|
||||
input.each_char.each_cons(2).sum do |(char, next_char)|
|
||||
char == next_char ? 1 : 0
|
||||
end
|
||||
end
|
||||
|
||||
def letter_pairs_count(input)
|
||||
characters = input
|
||||
.each_char
|
||||
.each_cons(4)
|
||||
.reject { |(a, b, c, d)| (a == b && b == c || b == c && c == d ) && a != d }
|
||||
|
||||
last_index = characters.size
|
||||
pairs = characters
|
||||
.flat_map
|
||||
.with_index(1) { |(a, b, c, d), i| i == last_index ? [a+b, b+c, c+d] : [a+b] }
|
||||
|
||||
pairs.tally.values.max || 0
|
||||
end
|
||||
|
||||
def surrounded_letter_count(input)
|
||||
input
|
||||
.each_char
|
||||
.each_cons(3)
|
||||
.count { |(a, _, b)| a == b }
|
||||
end
|
||||
|
||||
def is_nice?(input)
|
||||
return false if input.match?(/ab|cd|pq|xy/)
|
||||
return false unless vowel_count(input) >= 3
|
||||
return false unless twin_letters(input) >= 1
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def is_really_nice?(input)
|
||||
return false if letter_pairs_count(input) < 2
|
||||
return false if surrounded_letter_count(input).zero?
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def count_nice(file)
|
||||
file.each_line.sum do |line|
|
||||
line = line.chomp # chomp: true keyword argument emits warnings
|
||||
next 0 if line.empty?
|
||||
yield(line) ? 1 : 0
|
||||
end
|
||||
end
|
||||
|
||||
def solve_part_1(file)
|
||||
nice_lines = count_nice(file) { |l| is_nice?(l) }
|
||||
puts "\tThere are #{nice_lines} nice lines"
|
||||
end
|
||||
|
||||
def solve_part_2(file)
|
||||
nice_lines = count_nice(file) { |l| is_really_nice?(l) }
|
||||
puts "\tThere are #{nice_lines} really nice lines"
|
||||
end
|
||||
|
||||
def main(files)
|
||||
files.each do |file|
|
||||
puts "File: #{file}"
|
||||
solve_part_1(file)
|
||||
solve_part_2(file)
|
||||
end
|
||||
end
|
||||
|
||||
main(INPUTS)
|
30
2015/day-05/part_one.rb
Executable file
30
2015/day-05/part_one.rb
Executable file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
VOWELS = %[a e i o u].freeze
|
||||
|
||||
def vowel_count(input)
|
||||
input.each_char.sum do |char|
|
||||
VOWELS.include?(char) ? 1 : 0
|
||||
end
|
||||
end
|
||||
|
||||
def twin_letters(input)
|
||||
input.each_char.each_cons(2).sum do |(char, next_char)|
|
||||
char == next_char ? 1 : 0
|
||||
end
|
||||
end
|
||||
|
||||
def is_nice?(input)
|
||||
return false if input.empty?
|
||||
return false if input.match?(/ab|cd|pq|xy/)
|
||||
return false unless vowel_count(input) >= 3
|
||||
return false unless twin_letters(input) >= 1
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
result = File.readlines(ARGV.first, chomp: true).sum do |line|
|
||||
is_nice?(line) ? 1 : 0
|
||||
end
|
||||
|
||||
puts result
|
36
2015/day-05/part_two.rb
Executable file
36
2015/day-05/part_two.rb
Executable file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
def letter_pairs_count(input)
|
||||
characters = input
|
||||
.each_char
|
||||
.each_cons(4)
|
||||
.reject { |(a, b, c, d)| (a == b && b == c || b == c && c == d ) && a != d }
|
||||
|
||||
last_index = characters.size
|
||||
pairs = characters
|
||||
.flat_map
|
||||
.with_index(1) { |(a, b, c, d), i| i == last_index ? [a+b, b+c, c+d] : [a+b] }
|
||||
|
||||
pairs.tally.values.max || 0
|
||||
end
|
||||
|
||||
def surrounded_letter_count(input)
|
||||
input
|
||||
.each_char
|
||||
.each_cons(3)
|
||||
.count { |(a, _, b)| a == b }
|
||||
end
|
||||
|
||||
def is_really_nice?(input)
|
||||
return false if input.empty?
|
||||
return false if letter_pairs_count(input) < 2
|
||||
return false if surrounded_letter_count(input).zero?
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
result = File.readlines(ARGV.first, chomp: true).sum do |line|
|
||||
is_really_nice?(line) ? 1 : 0
|
||||
end
|
||||
|
||||
puts result
|
@ -1 +0,0 @@
|
||||
3.0.0
|
33
2015/day-06/common.rb
Normal file
33
2015/day-06/common.rb
Normal file
@ -0,0 +1,33 @@
|
||||
class Grid
|
||||
def initialize
|
||||
@area = Array.new(1000) { Array.new(1000) { 0 } }
|
||||
@commands = {
|
||||
turn_on: ->(area) { 1 },
|
||||
toggle: ->(area) { area ^ 0x1 },
|
||||
turn_off: -> (area) { 0 }
|
||||
}
|
||||
end
|
||||
|
||||
def set_command(name, callback) = @commands[name.to_sym] = callback
|
||||
|
||||
def apply(cmd, coords)
|
||||
coords => [[x, y], [w, h]]
|
||||
(y..h).each do |i|
|
||||
(x..w).each do |j|
|
||||
@area[i][j] = @commands[cmd.to_sym].(@area[i][j])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def lights_on = @area.sum { |r| r.count { |x| x != 0 }}
|
||||
def brightness = @area.sum { |r| r.sum }
|
||||
end
|
||||
|
||||
def parse_instruction(input)
|
||||
left, right = input.split("through")
|
||||
cmd, _, start_coords = left.strip.rpartition(" ").map(&:strip)
|
||||
start_coords = start_coords.split(",").map(&:to_i)
|
||||
end_coords = right.strip.split(",").map(&:to_i)
|
||||
|
||||
[cmd.tr(" ", "_"), [start_coords, end_coords]]
|
||||
end
|
@ -1,75 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "pathname"
|
||||
|
||||
INPUTS = [
|
||||
Pathname("inputs/test.txt"),
|
||||
Pathname("inputs/puzzle.txt"),
|
||||
].freeze
|
||||
|
||||
class Grid
|
||||
def initialize = @area = Array.new(1000) { Array.new(1000) { 0 } }
|
||||
|
||||
def apply(cmd, coords)
|
||||
coords => [[x, y], [w, h]]
|
||||
(y..h).each do |i|
|
||||
(x..w).each do |j|
|
||||
@area[i][j] = send(cmd, @area[i][j])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def lights_on = @area.sum { |r| r.count { |x| x != 0 }}
|
||||
def brightness = @area.sum { |r| r.sum }
|
||||
|
||||
private
|
||||
|
||||
def turn_on(area) = 1
|
||||
def toggle(area) = area ^ 0x1
|
||||
def turn_off(area) = 0
|
||||
|
||||
def turn_on2(area) = area + 1
|
||||
def turn_off2(area) = [area - 1, 0].max()
|
||||
def toggle2(area) = area + 2
|
||||
end
|
||||
|
||||
def parse_instruction(input)
|
||||
input = input.chomp
|
||||
left, right = input.split("through")
|
||||
cmd, _, start_coords = left.strip.rpartition(" ").map(&:strip)
|
||||
start_coords = start_coords.split(",").map(&:to_i)
|
||||
end_coords = right.strip.split(",").map(&:to_i)
|
||||
|
||||
[cmd.tr(" ", "_"), [start_coords, end_coords]]
|
||||
end
|
||||
|
||||
def solve_part_1(file)
|
||||
grid = Grid.new()
|
||||
|
||||
file.each_line do |line|
|
||||
grid.apply(*parse_instruction(line))
|
||||
end
|
||||
|
||||
puts "\tLights on: #{grid.lights_on}"
|
||||
end
|
||||
|
||||
def solve_part_2(file)
|
||||
grid = Grid.new()
|
||||
|
||||
file.each_line do |line|
|
||||
parse_instruction(line) => [cmd, coords]
|
||||
grid.apply("#{cmd}2", coords)
|
||||
end
|
||||
|
||||
puts "\tBrightness: #{grid.brightness}"
|
||||
end
|
||||
|
||||
def main(files)
|
||||
files.each do |file|
|
||||
puts "File: #{file}"
|
||||
solve_part_1(file)
|
||||
solve_part_2(file)
|
||||
end
|
||||
end
|
||||
|
||||
main(INPUTS)
|
11
2015/day-06/part_one.rb
Executable file
11
2015/day-06/part_one.rb
Executable file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require_relative "common"
|
||||
|
||||
grid = Grid.new()
|
||||
|
||||
File.readlines(ARGV.first, chomp: true).each do |line|
|
||||
grid.apply(*parse_instruction(line))
|
||||
end
|
||||
|
||||
puts grid.lights_on
|
15
2015/day-06/part_two.rb
Executable file
15
2015/day-06/part_two.rb
Executable file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require_relative "common"
|
||||
|
||||
grid = Grid.new()
|
||||
|
||||
grid.set_command(:turn_on, ->(area) { area + 1 })
|
||||
grid.set_command(:turn_off, ->(area) { [area - 1, 0].max() })
|
||||
grid.set_command(:toggle, ->(area) { area + 2 })
|
||||
|
||||
File.readlines(ARGV.first, chomp: true).each do |line|
|
||||
grid.apply(*parse_instruction(line))
|
||||
end
|
||||
|
||||
puts grid.brightness
|
1
2015/day-07/.gitignore
vendored
1
2015/day-07/.gitignore
vendored
@ -1 +0,0 @@
|
||||
main
|
@ -40,8 +40,8 @@ pub struct Circuit {
|
||||
}
|
||||
|
||||
impl Circuit {
|
||||
pub fn add_instruction(&mut self, instruction: &str) {
|
||||
let tokens = tokenize(instruction);
|
||||
pub fn add_instruction(&mut self, input: &str) {
|
||||
let tokens = tokenize(input);
|
||||
self.instructions.push_front(tokens);
|
||||
}
|
||||
|
||||
@ -52,8 +52,8 @@ impl Circuit {
|
||||
}
|
||||
|
||||
fn execute_one(&mut self, tokens: Vec<Token>) {
|
||||
use self::Token::*;
|
||||
use self::Op::*;
|
||||
use self::Token::*;
|
||||
|
||||
self.instruction_len = tokens.len();
|
||||
|
||||
@ -62,7 +62,7 @@ impl Circuit {
|
||||
Ident(_) | Value(_) => {
|
||||
self.stack.push(token.clone());
|
||||
continue;
|
||||
},
|
||||
}
|
||||
Op(Assign) => self.op_assign(),
|
||||
Op(Not) => self.op_not(),
|
||||
Op(And) => self.binop(|a, b| a & b),
|
||||
@ -75,26 +75,29 @@ impl Circuit {
|
||||
Err(Error::VariableNotFound) => {
|
||||
self.instructions.push_back(tokens);
|
||||
return;
|
||||
},
|
||||
}
|
||||
Err(e) => panic!("{:?}", e),
|
||||
_ => ()
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn variable_override<N>(&mut self, name: N, value: Value)
|
||||
where N: Into<Ident>
|
||||
where
|
||||
N: Into<Ident>,
|
||||
{
|
||||
self.overrides.insert(name.into(), value);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn variables(&self) -> &HashMap<Ident,Value> {
|
||||
pub fn variables(&self) -> &HashMap<Ident, Value> {
|
||||
&self.variables
|
||||
}
|
||||
|
||||
pub fn variable<N>(&self, name: N) -> Option<&Value>
|
||||
where N: AsRef<str>
|
||||
where
|
||||
N: AsRef<str>,
|
||||
{
|
||||
self.variables.get(name.as_ref())
|
||||
}
|
||||
@ -107,16 +110,26 @@ impl Circuit {
|
||||
let rhs = self.stack.pop().unwrap();
|
||||
|
||||
if let Ident(id) = lhs {
|
||||
let replace = if self.instruction_len == 3 { self.overrides.get(&id) } else { None };
|
||||
let replace = if self.instruction_len == 3 {
|
||||
self.overrides.get(&id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
match (rhs, replace) {
|
||||
(Value(_), Some(val)) => { self.variables.insert(id, *val); },
|
||||
(Value(val), None) => { self.variables.insert(id, val); },
|
||||
(Value(_), Some(val)) => {
|
||||
self.variables.insert(id, *val);
|
||||
}
|
||||
(Value(val), None) => {
|
||||
self.variables.insert(id, val);
|
||||
}
|
||||
(Ident(id2), _) => {
|
||||
let tmp = *self.variables.get(&id2)
|
||||
let tmp = *self
|
||||
.variables
|
||||
.get(&id2)
|
||||
.ok_or_else(|| Error::VariableNotFound)?;
|
||||
self.variables.insert(id, tmp);
|
||||
},
|
||||
_ => return Err(Error::InvalidStack)
|
||||
}
|
||||
_ => return Err(Error::InvalidStack),
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,20 +145,23 @@ impl Circuit {
|
||||
match token {
|
||||
Value(val) => {
|
||||
self.stack.push(Value(!val));
|
||||
},
|
||||
}
|
||||
Ident(id) => {
|
||||
let val = self.variables.get(&id)
|
||||
let val = self
|
||||
.variables
|
||||
.get(&id)
|
||||
.ok_or_else(|| Error::VariableNotFound)?;
|
||||
self.stack.push(Value(!val));
|
||||
},
|
||||
_ => return Err(Error::InvalidStack)
|
||||
}
|
||||
_ => return Err(Error::InvalidStack),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn binop<F>(&mut self, fun: F) -> Result<()>
|
||||
where F: Fn(Value, Value) -> Value
|
||||
where
|
||||
F: Fn(Value, Value) -> Value,
|
||||
{
|
||||
use self::Token::*;
|
||||
|
||||
@ -155,23 +171,31 @@ impl Circuit {
|
||||
|
||||
match (lhs, rhs) {
|
||||
(Ident(id1), Value(val)) => {
|
||||
let a = *self.variables.get(&id1)
|
||||
let a = *self
|
||||
.variables
|
||||
.get(&id1)
|
||||
.ok_or_else(|| Error::VariableNotFound)?;
|
||||
self.stack.push(Value(fun(a, val)));
|
||||
},
|
||||
}
|
||||
(Ident(id1), Ident(id2)) => {
|
||||
let a = *self.variables.get(&id1)
|
||||
let a = *self
|
||||
.variables
|
||||
.get(&id1)
|
||||
.ok_or_else(|| Error::VariableNotFound)?;
|
||||
let b = *self.variables.get(&id2)
|
||||
let b = *self
|
||||
.variables
|
||||
.get(&id2)
|
||||
.ok_or_else(|| Error::VariableNotFound)?;
|
||||
self.stack.push(Value(fun(a, b)));
|
||||
},
|
||||
}
|
||||
(Value(val), Ident(id2)) => {
|
||||
let b = *self.variables.get(&id2)
|
||||
let b = *self
|
||||
.variables
|
||||
.get(&id2)
|
||||
.ok_or_else(|| Error::VariableNotFound)?;
|
||||
self.stack.push(Value(fun(val, b)));
|
||||
},
|
||||
_ => return Err(Error::InvalidStack)
|
||||
}
|
||||
_ => return Err(Error::InvalidStack),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -193,13 +217,13 @@ fn tokenize(input: &str) -> Vec<Token> {
|
||||
otherwise => match otherwise.parse::<Value>() {
|
||||
Ok(val) => Token::Value(val),
|
||||
Err(_) => Token::Ident(word.to_string()),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
match token {
|
||||
Token::Op(_) => {
|
||||
operator = Some(token);
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
tokens.push(token);
|
||||
if let Some(op) = operator.take() {
|
23
2015/day-07/part_one.rs
Normal file
23
2015/day-07/part_one.rs
Normal file
@ -0,0 +1,23 @@
|
||||
mod common;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead};
|
||||
|
||||
use common::Circuit;
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let path = env::args().nth(1).unwrap();
|
||||
let file = io::BufReader::new(File::open(path)?);
|
||||
|
||||
let mut circuit = Circuit::default();
|
||||
for line in file.lines() {
|
||||
circuit.add_instruction(&line?);
|
||||
}
|
||||
circuit.execute();
|
||||
let output = circuit.variable("a").unwrap();
|
||||
|
||||
println!("{}", output);
|
||||
|
||||
Ok(())
|
||||
}
|
32
2015/day-07/part_two.rs
Normal file
32
2015/day-07/part_two.rs
Normal file
@ -0,0 +1,32 @@
|
||||
mod common;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead};
|
||||
|
||||
use common::Circuit;
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let path = env::args().nth(1).unwrap();
|
||||
let file = io::BufReader::new(File::open(path)?);
|
||||
let instructions = file.lines().map(|l| l.unwrap()).collect::<Vec<_>>();
|
||||
|
||||
let mut circuit = Circuit::default();
|
||||
for tokens in &instructions {
|
||||
circuit.add_instruction(tokens);
|
||||
}
|
||||
circuit.execute();
|
||||
let output = circuit.variable("a").copied().unwrap();
|
||||
|
||||
circuit = Circuit::default();
|
||||
for tokens in &instructions {
|
||||
circuit.add_instruction(tokens);
|
||||
}
|
||||
circuit.variable_override("b", output);
|
||||
circuit.execute();
|
||||
let output = circuit.variable("a").unwrap();
|
||||
|
||||
println!("{}", output);
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
mod circuit;
|
||||
|
||||
use std::{
|
||||
io::{self, BufRead},
|
||||
fs::File,
|
||||
};
|
||||
use circuit::Circuit;
|
||||
|
||||
const FILE_PATHS: &[&str] = &[
|
||||
"inputs/test.txt",
|
||||
"inputs/puzzle.txt",
|
||||
];
|
||||
|
||||
fn solve_part_1(file_path: &str) -> io::Result<u16>{
|
||||
let mut circuit = Circuit::default();
|
||||
|
||||
let file = io::BufReader::new(File::open(file_path)?);
|
||||
for line in file.lines() {
|
||||
circuit.add_instruction(&line?);
|
||||
}
|
||||
circuit.execute();
|
||||
let a = circuit.variable("a").copied().unwrap_or_default();
|
||||
println!("\ta: {:?}", a);
|
||||
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
fn solve_part_2(file_path: &str, a: u16) -> io::Result<()> {
|
||||
let mut circuit = Circuit::default();
|
||||
|
||||
let file = io::BufReader::new(File::open(file_path)?);
|
||||
for line in file.lines() {
|
||||
circuit.add_instruction(&line?);
|
||||
}
|
||||
circuit.variable_override("b", a);
|
||||
circuit.execute();
|
||||
|
||||
if let Some(a) = circuit.variable("a") {
|
||||
println!("\ta: {:?}", a);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
for file_path in FILE_PATHS {
|
||||
println!("File: {}", file_path);
|
||||
|
||||
let a = solve_part_1(file_path)?;
|
||||
solve_part_2(file_path, a)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1 +0,0 @@
|
||||
3.0.0
|
@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "pathname"
|
||||
|
||||
INPUTS = [
|
||||
Pathname("inputs/test.txt"),
|
||||
Pathname("inputs/puzzle.txt"),
|
||||
].freeze
|
||||
|
||||
def solve_part_1(file)
|
||||
size = file.each_line.sum do |line|
|
||||
line = line.chomp
|
||||
line.size - line.undump.size
|
||||
end
|
||||
|
||||
puts "\tSize 1: #{size}"
|
||||
end
|
||||
|
||||
def solve_part_2(file)
|
||||
size = file.each_line.sum do |line|
|
||||
line = line.chomp
|
||||
line.dump.size - line.size
|
||||
end
|
||||
|
||||
puts "\tSize 2: #{size}"
|
||||
end
|
||||
|
||||
def main(files)
|
||||
files.each do |file|
|
||||
puts "File: #{file}"
|
||||
|
||||
solve_part_1(file)
|
||||
solve_part_2(file)
|
||||
end
|
||||
end
|
||||
|
||||
main(INPUTS)
|
7
2015/day-08/part_one.rb
Executable file
7
2015/day-08/part_one.rb
Executable file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
size = File.readlines(ARGV.first, chomp: true).sum do |line|
|
||||
line.size - line.undump.size
|
||||
end
|
||||
|
||||
puts size
|
7
2015/day-08/part_two.rb
Executable file
7
2015/day-08/part_two.rb
Executable file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
size = File.readlines(ARGV.first, chomp: true).sum do |line|
|
||||
line.dump.size - line.size
|
||||
end
|
||||
|
||||
puts size
|
2
2015/day-09/.gitignore
vendored
2
2015/day-09/.gitignore
vendored
@ -1,3 +1,3 @@
|
||||
*.dot
|
||||
*.png
|
||||
main
|
||||
dot
|
||||
|
39
2015/day-09/common.rs
Normal file
39
2015/day-09/common.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use std::collections::HashSet;
|
||||
use std::error::Error as StdError;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead, BufReader};
|
||||
use std::path::Path;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use crate::graph;
|
||||
|
||||
pub type Location = String;
|
||||
pub type Distance = u64;
|
||||
pub type Graph = graph::Graph<Location, Distance>;
|
||||
pub type Node = graph::Node<Location, Distance>;
|
||||
pub type NodeSet = HashSet<Node>;
|
||||
|
||||
pub type Error = Box<dyn StdError>;
|
||||
pub type Result<T, E = Error> = StdResult<T, E>;
|
||||
|
||||
pub fn parse_file<P>(path: P) -> io::Result<Graph>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let file = BufReader::new(File::open(path)?);
|
||||
let mut directions = Graph::default();
|
||||
|
||||
for line in file.lines() {
|
||||
let line = line?;
|
||||
let words = line.split(' ').collect::<Vec<_>>();
|
||||
let location = (
|
||||
words[0].to_string(),
|
||||
words[2].to_string(),
|
||||
words[4].parse::<Distance>().unwrap(),
|
||||
);
|
||||
|
||||
directions.add_edge(&location);
|
||||
}
|
||||
|
||||
Ok(directions)
|
||||
}
|
53
2015/day-09/dot.rs
Normal file
53
2015/day-09/dot.rs
Normal file
@ -0,0 +1,53 @@
|
||||
mod common;
|
||||
mod graph;
|
||||
|
||||
use std::env;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use common::{Graph, Result};
|
||||
|
||||
fn generate_dot(graph: &Graph, name: &str) -> Result<String> {
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut buf = String::new();
|
||||
|
||||
writeln!(buf, "strict graph {} {{", name)?;
|
||||
for node in &graph.nodes {
|
||||
for (outgoing, weight) in &node.borrow().outgoing {
|
||||
writeln!(
|
||||
buf,
|
||||
"{} -- {} [ label = \"{}\" ];",
|
||||
node.borrow().data,
|
||||
outgoing.borrow().data,
|
||||
weight
|
||||
)?;
|
||||
}
|
||||
}
|
||||
writeln!(buf, "}}")?;
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let path = PathBuf::from(env::args().nth(1).unwrap());
|
||||
let graph = common::parse_file(&path)?;
|
||||
|
||||
let mut dot_path = path.clone();
|
||||
dot_path.set_extension("dot");
|
||||
let graph_name = dot_path.file_stem().unwrap().to_str().unwrap();
|
||||
|
||||
let dot = generate_dot(&graph, &graph_name)?;
|
||||
|
||||
let out_path = dot_path.file_name().unwrap();
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.open(out_path)?;
|
||||
|
||||
write!(file, "{}", dot)?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,7 +1,11 @@
|
||||
use std::collections::HashSet;
|
||||
use crate::{Distance, Graph, Node};
|
||||
mod common;
|
||||
pub mod graph;
|
||||
mod utils;
|
||||
|
||||
type NodeSet = HashSet<Node>;
|
||||
use std::cmp::min;
|
||||
use std::env;
|
||||
|
||||
use crate::common::{Distance, Node, NodeSet, Result};
|
||||
|
||||
// Only works with complete graphs (where each node leads to each other node)
|
||||
fn visit_all_shortest(start_node: &Node) -> Vec<(Node, Distance)> {
|
||||
@ -45,9 +49,9 @@ fn visit_all_shortest(start_node: &Node) -> Vec<(Node, Distance)> {
|
||||
path
|
||||
}
|
||||
|
||||
/// Find shortest path that connects two points and visits all locations.
|
||||
pub fn solve(directions: &Graph) -> Distance {
|
||||
use std::cmp::min;
|
||||
fn main() -> Result<()> {
|
||||
let path = env::args().nth(1).unwrap();
|
||||
let directions = common::parse_file(path)?;
|
||||
|
||||
let mut shortest_distance = None;
|
||||
|
||||
@ -58,12 +62,14 @@ pub fn solve(directions: &Graph) -> Distance {
|
||||
continue;
|
||||
}
|
||||
|
||||
let distance = path.iter().map(|p| p.1).sum();
|
||||
let distance: Distance = path.iter().map(|p| p.1).sum();
|
||||
shortest_distance = match shortest_distance {
|
||||
None => Some(distance),
|
||||
Some(d) => Some(min(distance, d)),
|
||||
};
|
||||
}
|
||||
|
||||
shortest_distance.unwrap_or_default()
|
||||
println!("{}", shortest_distance.unwrap_or_default());
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,7 +1,11 @@
|
||||
use std::collections::HashSet;
|
||||
use crate::{Distance, Graph, Node};
|
||||
mod common;
|
||||
pub mod graph;
|
||||
mod utils;
|
||||
|
||||
type NodeSet = HashSet<Node>;
|
||||
use std::cmp::max;
|
||||
use std::env;
|
||||
|
||||
use crate::common::{Distance, Node, NodeSet, Result};
|
||||
|
||||
struct Memo<'a> {
|
||||
max_dist: &'a mut Distance,
|
||||
@ -18,11 +22,14 @@ fn depth_first_search(node: &Node, memo: Memo<'_>) {
|
||||
if !memo.visited.contains(&out_node) {
|
||||
distance = memo.prev_dist + out_weight;
|
||||
|
||||
depth_first_search(&out_node, Memo {
|
||||
max_dist: &mut *memo.max_dist,
|
||||
prev_dist: distance,
|
||||
visited: &mut *memo.visited,
|
||||
});
|
||||
depth_first_search(
|
||||
&out_node,
|
||||
Memo {
|
||||
max_dist: &mut *memo.max_dist,
|
||||
prev_dist: distance,
|
||||
visited: &mut *memo.visited,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if *memo.max_dist < distance {
|
||||
@ -38,17 +45,22 @@ fn visit_all_longest(start_node: &Node) -> Distance {
|
||||
|
||||
let mut distance = 0;
|
||||
|
||||
depth_first_search(&start_node, Memo {
|
||||
max_dist: &mut distance,
|
||||
prev_dist: 0,
|
||||
visited: &mut visited,
|
||||
});
|
||||
depth_first_search(
|
||||
&start_node,
|
||||
Memo {
|
||||
max_dist: &mut distance,
|
||||
prev_dist: 0,
|
||||
visited: &mut visited,
|
||||
},
|
||||
);
|
||||
|
||||
distance
|
||||
}
|
||||
|
||||
pub fn solve(directions: &Graph) -> Distance {
|
||||
use std::cmp::max;
|
||||
fn main() -> Result<()> {
|
||||
let path = env::args().nth(1).unwrap();
|
||||
|
||||
let directions = common::parse_file(path)?;
|
||||
|
||||
let mut longest_distance = 0;
|
||||
|
||||
@ -57,5 +69,7 @@ pub fn solve(directions: &Graph) -> Distance {
|
||||
longest_distance = max(distance, longest_distance);
|
||||
}
|
||||
|
||||
longest_distance
|
||||
println!("{}", longest_distance);
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
mod graph;
|
||||
mod utils;
|
||||
mod part1;
|
||||
mod part2;
|
||||
|
||||
type Graph = graph::Graph<Location, Distance>;
|
||||
type Node = graph::Node<Location, Distance>;
|
||||
|
||||
type Location = String;
|
||||
type Distance = u64;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
use std::env;
|
||||
|
||||
let file_paths = env::args().skip(1).collect::<Vec<_>>();
|
||||
|
||||
for file_path in &file_paths {
|
||||
let locations = utils::parse_file(file_path)?;
|
||||
|
||||
let mut directions = Graph::default();
|
||||
|
||||
for location in &locations {
|
||||
directions.add_edge(location);
|
||||
}
|
||||
|
||||
utils::write_dot(&directions, &file_path)?;
|
||||
|
||||
println!("{} / solution 1 = {:?}", file_path, part1::solve(&directions));
|
||||
println!("{} / solution 2 = {:?}", file_path, part2::solve(&directions));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,58 +0,0 @@
|
||||
use std::{
|
||||
io::{self, BufRead},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use crate::{Location, Distance, Graph};
|
||||
|
||||
pub fn generate_dot(graph: &Graph, name: &str) -> Result<String, Box<dyn std::error::Error>> {
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut buf = String::new();
|
||||
|
||||
writeln!(buf, "strict graph {} {{", name)?;
|
||||
for node in &graph.nodes {
|
||||
for (outgoing, weight) in &node.borrow().outgoing {
|
||||
writeln!(buf, "{} -- {} [ label = \"{}\" ];",
|
||||
node.borrow().data,
|
||||
outgoing.borrow().data,
|
||||
weight
|
||||
)?;
|
||||
}
|
||||
}
|
||||
writeln!(buf, "}}")?;
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
pub fn write_dot<P>(graph: &Graph, path: P) -> Result<(), Box<dyn std::error::Error>>
|
||||
where P: Into<PathBuf>
|
||||
{
|
||||
use std::{fs::OpenOptions, io::Write};
|
||||
|
||||
let mut dot_path = path.into();
|
||||
dot_path.set_extension("dot");
|
||||
let graph_name = dot_path.file_stem().unwrap().to_str().unwrap();
|
||||
let dot = generate_dot(&graph, &graph_name)?;
|
||||
let out_path = dot_path.file_name().unwrap();
|
||||
let mut file = OpenOptions::new().write(true).truncate(true).create(true).open(out_path)?;
|
||||
|
||||
write!(file, "{}", dot)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn parse_file<P>(path: P) -> io::Result<Vec<(Location, Location, Distance)>>
|
||||
where P: AsRef<Path>
|
||||
{
|
||||
use std::{fs::File, io::BufReader};
|
||||
|
||||
let file = BufReader::new(File::open(path)?);
|
||||
let dests = file.lines()
|
||||
.map(|l| {
|
||||
let line = l.unwrap();
|
||||
let words = line.split(' ').collect::<Vec<_>>();
|
||||
(words[0].to_string(), words[2].to_string(), words[4].parse::<Distance>().unwrap())
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(dests)
|
||||
}
|
@ -1 +0,0 @@
|
||||
3.0.0
|
22
2015/day-10/common.rb
Normal file
22
2015/day-10/common.rb
Normal file
@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "stringio"
|
||||
|
||||
def look_and_say(input)
|
||||
counter = 1
|
||||
|
||||
output = input.each_char.map(&:to_i).chain([nil]).each_cons(2).reduce(StringIO.new) do |acc, (val1, val2)|
|
||||
if val1 == val2
|
||||
counter += 1
|
||||
else
|
||||
acc.write counter
|
||||
acc.write val1
|
||||
counter = 1
|
||||
end
|
||||
|
||||
acc
|
||||
end
|
||||
|
||||
output.rewind
|
||||
output
|
||||
end
|
@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "pathname"
|
||||
require "stringio"
|
||||
|
||||
def look_and_say(input)
|
||||
counter = 1
|
||||
|
||||
output = input.each_char.map(&:to_i).chain([nil]).each_cons(2).reduce(StringIO.new) do |acc, (val1, val2)|
|
||||
if val1 == val2
|
||||
counter += 1
|
||||
else
|
||||
acc.write counter
|
||||
acc.write val1
|
||||
counter = 1
|
||||
end
|
||||
|
||||
acc
|
||||
end
|
||||
|
||||
output.rewind
|
||||
output
|
||||
end
|
||||
|
||||
def solve_part1(content)
|
||||
puts 40.times.reduce(StringIO.new(content)) { |acc| look_and_say(acc) }.size
|
||||
end
|
||||
|
||||
def solve_part2(content)
|
||||
puts 50.times.reduce(StringIO.new(content)) { |acc, _| look_and_say(acc) }.size
|
||||
end
|
||||
|
||||
def main(files)
|
||||
files.each do |file|
|
||||
content = file.read.chomp
|
||||
solve_part1(content)
|
||||
solve_part2(content)
|
||||
end
|
||||
end
|
||||
|
||||
main(ARGV.map { |x| Pathname(x) })
|
8
2015/day-10/part_one.rb
Executable file
8
2015/day-10/part_one.rb
Executable file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "stringio"
|
||||
require_relative "common"
|
||||
|
||||
content = File.read(ARGV.first).chomp
|
||||
puts 40.times.reduce(StringIO.new(content)) { |acc| look_and_say(acc) }.size
|
8
2015/day-10/part_two.rb
Executable file
8
2015/day-10/part_two.rb
Executable file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "stringio"
|
||||
require_relative "common"
|
||||
|
||||
content = File.read(ARGV.first).chomp
|
||||
puts 50.times.reduce(StringIO.new(content)) { |acc| look_and_say(acc) }.size
|
Loading…
Reference in New Issue
Block a user