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
|
*.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 {
|
impl Circuit {
|
||||||
pub fn add_instruction(&mut self, instruction: &str) {
|
pub fn add_instruction(&mut self, input: &str) {
|
||||||
let tokens = tokenize(instruction);
|
let tokens = tokenize(input);
|
||||||
self.instructions.push_front(tokens);
|
self.instructions.push_front(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,8 +52,8 @@ impl Circuit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn execute_one(&mut self, tokens: Vec<Token>) {
|
fn execute_one(&mut self, tokens: Vec<Token>) {
|
||||||
use self::Token::*;
|
|
||||||
use self::Op::*;
|
use self::Op::*;
|
||||||
|
use self::Token::*;
|
||||||
|
|
||||||
self.instruction_len = tokens.len();
|
self.instruction_len = tokens.len();
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ impl Circuit {
|
|||||||
Ident(_) | Value(_) => {
|
Ident(_) | Value(_) => {
|
||||||
self.stack.push(token.clone());
|
self.stack.push(token.clone());
|
||||||
continue;
|
continue;
|
||||||
},
|
}
|
||||||
Op(Assign) => self.op_assign(),
|
Op(Assign) => self.op_assign(),
|
||||||
Op(Not) => self.op_not(),
|
Op(Not) => self.op_not(),
|
||||||
Op(And) => self.binop(|a, b| a & b),
|
Op(And) => self.binop(|a, b| a & b),
|
||||||
@ -75,26 +75,29 @@ impl Circuit {
|
|||||||
Err(Error::VariableNotFound) => {
|
Err(Error::VariableNotFound) => {
|
||||||
self.instructions.push_back(tokens);
|
self.instructions.push_back(tokens);
|
||||||
return;
|
return;
|
||||||
},
|
}
|
||||||
Err(e) => panic!("{:?}", e),
|
Err(e) => panic!("{:?}", e),
|
||||||
_ => ()
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn variable_override<N>(&mut self, name: N, value: Value)
|
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);
|
self.overrides.insert(name.into(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn variables(&self) -> &HashMap<Ident,Value> {
|
pub fn variables(&self) -> &HashMap<Ident, Value> {
|
||||||
&self.variables
|
&self.variables
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn variable<N>(&self, name: N) -> Option<&Value>
|
pub fn variable<N>(&self, name: N) -> Option<&Value>
|
||||||
where N: AsRef<str>
|
where
|
||||||
|
N: AsRef<str>,
|
||||||
{
|
{
|
||||||
self.variables.get(name.as_ref())
|
self.variables.get(name.as_ref())
|
||||||
}
|
}
|
||||||
@ -107,16 +110,26 @@ impl Circuit {
|
|||||||
let rhs = self.stack.pop().unwrap();
|
let rhs = self.stack.pop().unwrap();
|
||||||
|
|
||||||
if let Ident(id) = lhs {
|
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) {
|
match (rhs, replace) {
|
||||||
(Value(_), Some(val)) => { self.variables.insert(id, *val); },
|
(Value(_), Some(val)) => {
|
||||||
(Value(val), None) => { self.variables.insert(id, val); },
|
self.variables.insert(id, *val);
|
||||||
|
}
|
||||||
|
(Value(val), None) => {
|
||||||
|
self.variables.insert(id, val);
|
||||||
|
}
|
||||||
(Ident(id2), _) => {
|
(Ident(id2), _) => {
|
||||||
let tmp = *self.variables.get(&id2)
|
let tmp = *self
|
||||||
|
.variables
|
||||||
|
.get(&id2)
|
||||||
.ok_or_else(|| Error::VariableNotFound)?;
|
.ok_or_else(|| Error::VariableNotFound)?;
|
||||||
self.variables.insert(id, tmp);
|
self.variables.insert(id, tmp);
|
||||||
},
|
}
|
||||||
_ => return Err(Error::InvalidStack)
|
_ => return Err(Error::InvalidStack),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,20 +145,23 @@ impl Circuit {
|
|||||||
match token {
|
match token {
|
||||||
Value(val) => {
|
Value(val) => {
|
||||||
self.stack.push(Value(!val));
|
self.stack.push(Value(!val));
|
||||||
},
|
}
|
||||||
Ident(id) => {
|
Ident(id) => {
|
||||||
let val = self.variables.get(&id)
|
let val = self
|
||||||
|
.variables
|
||||||
|
.get(&id)
|
||||||
.ok_or_else(|| Error::VariableNotFound)?;
|
.ok_or_else(|| Error::VariableNotFound)?;
|
||||||
self.stack.push(Value(!val));
|
self.stack.push(Value(!val));
|
||||||
},
|
}
|
||||||
_ => return Err(Error::InvalidStack)
|
_ => return Err(Error::InvalidStack),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn binop<F>(&mut self, fun: F) -> Result<()>
|
fn binop<F>(&mut self, fun: F) -> Result<()>
|
||||||
where F: Fn(Value, Value) -> Value
|
where
|
||||||
|
F: Fn(Value, Value) -> Value,
|
||||||
{
|
{
|
||||||
use self::Token::*;
|
use self::Token::*;
|
||||||
|
|
||||||
@ -155,23 +171,31 @@ impl Circuit {
|
|||||||
|
|
||||||
match (lhs, rhs) {
|
match (lhs, rhs) {
|
||||||
(Ident(id1), Value(val)) => {
|
(Ident(id1), Value(val)) => {
|
||||||
let a = *self.variables.get(&id1)
|
let a = *self
|
||||||
|
.variables
|
||||||
|
.get(&id1)
|
||||||
.ok_or_else(|| Error::VariableNotFound)?;
|
.ok_or_else(|| Error::VariableNotFound)?;
|
||||||
self.stack.push(Value(fun(a, val)));
|
self.stack.push(Value(fun(a, val)));
|
||||||
},
|
}
|
||||||
(Ident(id1), Ident(id2)) => {
|
(Ident(id1), Ident(id2)) => {
|
||||||
let a = *self.variables.get(&id1)
|
let a = *self
|
||||||
|
.variables
|
||||||
|
.get(&id1)
|
||||||
.ok_or_else(|| Error::VariableNotFound)?;
|
.ok_or_else(|| Error::VariableNotFound)?;
|
||||||
let b = *self.variables.get(&id2)
|
let b = *self
|
||||||
|
.variables
|
||||||
|
.get(&id2)
|
||||||
.ok_or_else(|| Error::VariableNotFound)?;
|
.ok_or_else(|| Error::VariableNotFound)?;
|
||||||
self.stack.push(Value(fun(a, b)));
|
self.stack.push(Value(fun(a, b)));
|
||||||
},
|
}
|
||||||
(Value(val), Ident(id2)) => {
|
(Value(val), Ident(id2)) => {
|
||||||
let b = *self.variables.get(&id2)
|
let b = *self
|
||||||
|
.variables
|
||||||
|
.get(&id2)
|
||||||
.ok_or_else(|| Error::VariableNotFound)?;
|
.ok_or_else(|| Error::VariableNotFound)?;
|
||||||
self.stack.push(Value(fun(val, b)));
|
self.stack.push(Value(fun(val, b)));
|
||||||
},
|
}
|
||||||
_ => return Err(Error::InvalidStack)
|
_ => return Err(Error::InvalidStack),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -193,13 +217,13 @@ fn tokenize(input: &str) -> Vec<Token> {
|
|||||||
otherwise => match otherwise.parse::<Value>() {
|
otherwise => match otherwise.parse::<Value>() {
|
||||||
Ok(val) => Token::Value(val),
|
Ok(val) => Token::Value(val),
|
||||||
Err(_) => Token::Ident(word.to_string()),
|
Err(_) => Token::Ident(word.to_string()),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
match token {
|
match token {
|
||||||
Token::Op(_) => {
|
Token::Op(_) => {
|
||||||
operator = Some(token);
|
operator = Some(token);
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
if let Some(op) = operator.take() {
|
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
|
*.dot
|
||||||
*.png
|
*.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;
|
mod common;
|
||||||
use crate::{Distance, Graph, Node};
|
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)
|
// Only works with complete graphs (where each node leads to each other node)
|
||||||
fn visit_all_shortest(start_node: &Node) -> Vec<(Node, Distance)> {
|
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
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find shortest path that connects two points and visits all locations.
|
fn main() -> Result<()> {
|
||||||
pub fn solve(directions: &Graph) -> Distance {
|
let path = env::args().nth(1).unwrap();
|
||||||
use std::cmp::min;
|
let directions = common::parse_file(path)?;
|
||||||
|
|
||||||
let mut shortest_distance = None;
|
let mut shortest_distance = None;
|
||||||
|
|
||||||
@ -58,12 +62,14 @@ pub fn solve(directions: &Graph) -> Distance {
|
|||||||
continue;
|
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 {
|
shortest_distance = match shortest_distance {
|
||||||
None => Some(distance),
|
None => Some(distance),
|
||||||
Some(d) => Some(min(distance, d)),
|
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;
|
mod common;
|
||||||
use crate::{Distance, Graph, Node};
|
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> {
|
struct Memo<'a> {
|
||||||
max_dist: &'a mut Distance,
|
max_dist: &'a mut Distance,
|
||||||
@ -18,11 +22,14 @@ fn depth_first_search(node: &Node, memo: Memo<'_>) {
|
|||||||
if !memo.visited.contains(&out_node) {
|
if !memo.visited.contains(&out_node) {
|
||||||
distance = memo.prev_dist + out_weight;
|
distance = memo.prev_dist + out_weight;
|
||||||
|
|
||||||
depth_first_search(&out_node, Memo {
|
depth_first_search(
|
||||||
|
&out_node,
|
||||||
|
Memo {
|
||||||
max_dist: &mut *memo.max_dist,
|
max_dist: &mut *memo.max_dist,
|
||||||
prev_dist: distance,
|
prev_dist: distance,
|
||||||
visited: &mut *memo.visited,
|
visited: &mut *memo.visited,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if *memo.max_dist < distance {
|
if *memo.max_dist < distance {
|
||||||
@ -38,17 +45,22 @@ fn visit_all_longest(start_node: &Node) -> Distance {
|
|||||||
|
|
||||||
let mut distance = 0;
|
let mut distance = 0;
|
||||||
|
|
||||||
depth_first_search(&start_node, Memo {
|
depth_first_search(
|
||||||
|
&start_node,
|
||||||
|
Memo {
|
||||||
max_dist: &mut distance,
|
max_dist: &mut distance,
|
||||||
prev_dist: 0,
|
prev_dist: 0,
|
||||||
visited: &mut visited,
|
visited: &mut visited,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
distance
|
distance
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn solve(directions: &Graph) -> Distance {
|
fn main() -> Result<()> {
|
||||||
use std::cmp::max;
|
let path = env::args().nth(1).unwrap();
|
||||||
|
|
||||||
|
let directions = common::parse_file(path)?;
|
||||||
|
|
||||||
let mut longest_distance = 0;
|
let mut longest_distance = 0;
|
||||||
|
|
||||||
@ -57,5 +69,7 @@ pub fn solve(directions: &Graph) -> Distance {
|
|||||||
longest_distance = max(distance, longest_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