Refactor 2015 days 01 to 10

Split parts into separate files and remove some unused files
This commit is contained in:
Patrick Auernig 2021-12-08 01:05:17 +01:00
parent 13d1191a82
commit 3c57921438
60 changed files with 655 additions and 722 deletions

4
.gitignore vendored
View File

@ -1 +1,5 @@
*.bak
# build artifacts
part_one
part_two

8
2015/day-01/common.rb Normal file
View 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

View File

@ -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
View 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
View 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
View 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]

View File

@ -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
View 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
View 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
View 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

View File

@ -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
View 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
View 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)

View File

@ -1 +0,0 @@
main

54
2015/day-04/common.rs Normal file
View 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
View 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
View 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(())
}

View File

@ -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
View 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

View File

@ -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
View 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
View 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

View File

@ -1 +0,0 @@
3.0.0

33
2015/day-06/common.rb Normal file
View 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

View File

@ -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
View 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
View 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

View File

@ -1 +0,0 @@
main

View File

@ -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
View 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
View 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(())
}

View File

@ -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(())
}

View File

@ -1 +0,0 @@
3.0.0

View File

@ -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
View 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
View 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

View File

@ -1,3 +1,3 @@
*.dot
*.png
main
dot

39
2015/day-09/common.rs Normal file
View 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
View 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(())
}

View File

@ -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(())
}

View File

@ -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(())
}

View File

@ -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(())
}

View File

@ -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)
}

View File

@ -1 +0,0 @@
3.0.0

22
2015/day-10/common.rb Normal file
View 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

View File

@ -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
View 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
View 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