They're not really blaming capitalism for anything though? They're just explaining how it works, and they're right. In a market driven economy, you are paid for having a skill or some knowledge based on the demand of that skill or knowledge and nothing else. In the same way as the quality of your house has little bearing on it's value when compared to it's location. Not a criticism of capitalism.
Today's problems felt really refreshing after yesterday.
Solution in Rust π¦
Code
use std::{
collections::HashSet,
env, fs,
io::{self, BufRead, BufReader, Read},
};
fn main() -> io::Result<()> {
let args: Vec = env::args().collect();
let filename = &args[1];
let file1 = fs::File::open(filename)?;
let file2 = fs::File::open(filename)?;
let reader1 = BufReader::new(file1);
let reader2 = BufReader::new(file2);
println!("Part one: {}", process_part_one(reader1));
println!("Part two: {}", process_part_two(reader2));
Ok(())
}
fn parse_data(reader: BufReader) -> Vec> {
let lines = reader.lines().flatten();
let data: Vec<_> = lines
.map(|line| {
line.split(':')
.last()
.expect("text after colon")
.split_whitespace()
.map(|s| s.parse::().expect("numbers"))
.collect::>()
})
.collect();
data
}
fn calculate_ways_to_win(time: u64, dist: u64) -> HashSet {
let mut wins = HashSet:new();
for t in 1..time {
let d = t * (time - t);
if d > dist {
wins.insert(t);
}
}
wins
}
fn process_part_one(reader: BufReader) -> u64 {
let data = parse_data(reader);
let results: Vec<_> = data[0].iter().zip(data[1].iter()).collect();
let mut win_method_qty: Vec = Vec::new();
for r in results {
win_method_qty.push(calculate_ways_to_win(*r.0, *r.1).len() as u64);
}
win_method_qty.iter().product()
}
fn process_part_two(reader: BufReader) -> u64 {
let data = parse_data(reader);
let joined_data: Vec<_> = data
.iter()
.map(|v| {
v.iter()
.map(|d| d.to_string())
.collect::>()
.join("")
.parse::()
.expect("all digits")
})
.collect();
calculate_ways_to_win(joined_data[0], joined_data[1]).len() as u64
}
#[cfg(test)]
mod tests {
use super::*;
const INPUT: &str = "Time: 7 15 30
Distance: 9 40 200";
#[test]
fn test_process_part_one() {
let input_bytes = INPUT.as_bytes();
assert_eq!(288, process_part_one(BufReader::new(input_bytes)));
}
#[test]
fn test_process_part_two() {
let input_bytes = INPUT.as_bytes();
assert_eq!(71503, process_part_two(BufReader::new(input_bytes)));
}
}
:::
Like many others, I really didn't enjoy this one. I particularly struggled with part 02, which ended up with me just brute forcing it and checking each seed. On my system it took over 15 minutes to run, which is truly awful. I'm open to pointers on how I could better have solved part two.
Solution in Rust π¦
Code
use std::{
env, fs,
io::{self, BufReader, Read},
};
fn main() -> io::Result<()> {
let args: Vec = env::args().collect();
let filename = &args[1];
let file1 = fs::File::open(filename)?;
let file2 = fs::File::open(filename)?;
let mut reader1 = BufReader::new(file1);
let mut reader2 = BufReader::new(file2);
println!("Part one: {}", process_part_one(&mut reader1));
println!("Part two: {}", process_part_two(&mut reader2));
Ok(())
}
#[derive(Debug)]
struct Map {
lines: Vec,
}
impl Map {
fn map_to_lines(&self, key: u32) -> u32 {
for line in &self.lines {
if line.in_range(key) {
return line.map(key);
}
}
key
}
}
#[derive(Debug)]
struct MapLine {
dest_range: u32,
source_range: u32,
range_length: u32,
}
impl MapLine {
fn map(&self, key: u32) -> u32 {
let diff = key - self.source_range;
if self.dest_range as i64 + diff as i64 > 0 {
return (self.dest_range as i64 + diff as i64) as u32;
}
key
}
fn in_range(&self, key: u32) -> bool {
self.source_range <= key
&& (key as i64) < self.source_range as i64 + self.range_length as i64
}
}
fn parse_input(reader: &mut BufReader) -> (Vec, Vec<map>) {
let mut almanac = String::new();
reader
.read_to_string(&mut almanac)
.expect("read successful");
let parts: Vec<&str> = almanac.split("\n\n").collect();
let (seeds, others) = parts.split_first().expect("at least one part");
let seeds: Vec<_> = seeds
.split(": ")
.last()
.expect("at least one")
.split_whitespace()
.map(|s| s.to_string())
.collect();
let maps: Vec<_> = others
.iter()
.map(|item| {
let lines_iter = item
.split(':')
.last()
.expect("exists")
.trim()
.split('\n')
.map(|nums| {
let nums_split = nums.split_whitespace().collect::>();
MapLine {
dest_range: nums_split[0].parse().expect("is digit"),
source_range: nums_split[1].parse().expect("is digit"),
range_length: nums_split[2].parse().expect("is digit"),
}
});
Map {
lines: lines_iter.collect(),
}
})
.collect();
(seeds, maps)
}
fn process_part_one(reader: &mut BufReader) -> u32 {
let (seeds, maps) = parse_input(reader);
let mut res = u32::MAX;
for seed in &seeds {
let mut val = seed.parse::().expect("is digits");
for map in &maps {
val = map.map_to_lines(val);
}
res = u32::min(res, val);
}
res
}
fn process_part_two(reader: &mut BufReader) -> u32 {
let (seeds, maps) = parse_input(reader);
let seed_chunks: Vec<_> = seeds.chunks(2).collect();
let mut res = u32::MAX;
for chunk in seed_chunks {
let range_start: u32 = chunk[0].parse().expect("is digits");
let range_length: u32 = chunk[1].parse().expect("is digits");
let range_end: u32 = range_start + range_length;
for seed in range_start..range_end {
let mut val = seed;
for map in &maps {
val = map.map_to_lines(val);
}
res = u32::min(res, val);
}
}
res
}
#[cfg(test)]
mod tests {
use super::*;
const INPUT: &str = "seeds: 79 14 55 13
seed-to-soil map:
50 98 2
52 50 48
soil-to-fertilizer map:
0 15 37
37 52 2
39 0 15
fertilizer-to-water map:
49 53 8
0 11 42
42 0 7
57 7 4
water-to-light map:
88 18 7
18 25 70
light-to-temperature map:
45 77 23
81 45 19
68 64 13
temperature-to-humidity map:
0 69 1
1 0 69
humidity-to-location map:
60 56 37
56 93 4";
#[test]
fn test_process_part_one() {
let input_bytes = INPUT.as_bytes();
assert_eq!(35, process_part_one(&mut BufReader::new(input_bytes)));
}
#[test]
fn test_process_part_two() {
let input_bytes = INPUT.as_bytes();
assert_eq!(46, process_part_two(&mut BufReader::new(input_bytes)));
}
}
Late as always (actually a day late by UK time).
My solution to this one runs slow, but it gets the job done. I didn't actually need the CardInfo struct by the time I was done, but couldn't be bothered to remove it. Previously, it held more than just count.
Day 04 in Rust π¦
use std::{
collections::BTreeMap,
env, fs,
io::{self, BufRead, BufReader, Read},
};
fn main() -> io::Result<()> {
let args: Vec = env::args().collect();
let filename = &args[1];
let file1 = fs::File::open(filename)?;
let file2 = fs::File::open(filename)?;
let reader1 = BufReader::new(file1);
let reader2 = BufReader::new(file2);
println!("Part one: {}", process_part_one(reader1));
println!("Part two: {}", process_part_two(reader2));
Ok(())
}
fn process_part_one(reader: BufReader) -> u32 {
let mut sum = 0;
for line in reader.lines().flatten() {
let card_data: Vec<_> = line.split(": ").collect();
let all_numbers = card_data[1];
let number_parts: Vec> = all_numbers
.split('|')
.map(|x| {
x.replace(" ", " ")
.split_whitespace()
.map(|val| val.to_string())
.collect()
})
.collect();
let (winning_nums, owned_nums) = (&number_parts[0], &number_parts[1]);
let matches = owned_nums
.iter()
.filter(|num| winning_nums.contains(num))
.count();
if matches > 0 {
sum += 2_u32.pow((matches - 1) as u32);
}
}
sum
}
#[derive(Debug)]
struct CardInfo {
count: u32,
}
fn process_part_two(reader: BufReader) -> u32 {
let mut cards: BTreeMap = BTreeMap::new();
for line in reader.lines().flatten() {
let card_data: Vec<_> = line.split(": ").collect();
let card_id: u32 = card_data[0]
.replace("Card", "")
.trim()
.parse()
.expect("is digit");
let all_numbers = card_data[1];
let number_parts: Vec> = all_numbers
.split('|')
.map(|x| {
x.replace(" ", " ")
.split_whitespace()
.map(|val| val.to_string())
.collect()
})
.collect();
let (winning_nums, owned_nums) = (&number_parts[0], &number_parts[1]);
let matches = owned_nums
.iter()
.filter(|num| winning_nums.contains(num))
.count();
let card_details = CardInfo { count: 1 };
if let Some(old_card_info) = cards.insert(card_id, card_details) {
let card_entry = cards.get_mut(&card_id);
card_entry.expect("card exists").count += old_card_info.count;
};
let current_card = cards.get(&card_id).expect("card exists");
if matches > 0 {
for _ in 0..current_card.count {
for i in (card_id + 1)..=(matches as u32) + card_id {
let new_card_info = CardInfo { count: 1 };
if let Some(old_card_info) = cards.insert(i, new_card_info) {
let card_entry = cards.get_mut(&i).expect("card exists");
card_entry.count += old_card_info.count;
}
}
}
}
}
let sum = cards.iter().fold(0, |acc, c| acc + c.1.count);
sum
}
#[cfg(test)]
mod tests {
use super::*;
const INPUT: &str = "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11";
#[test]
fn test_process_part_one() {
let input_bytes = INPUT.as_bytes();
assert_eq!(13, process_part_one(BufReader::new(input_bytes)));
}
#[test]
fn test_process_part_two() {
let input_bytes = INPUT.as_bytes();
assert_eq!(30, process_part_two(BufReader::new(input_bytes)));
}
}
Edit: Updated now with part 2.
Managed to have a crack at this a bit earlier today, I've only done Part 01 so far. I'll update with Part 02 later.
I tackled this with the personal challenge of not loading the entire puzzle input into memory, which would have made this a bit easier.
Solution in Rust π¦
use std::{
env, fs,
io::{self, BufRead, BufReader, Read},
};
fn main() -> io::Result<()> {
let args: Vec = env::args().collect();
let filename = &args[1];
let file1 = fs::File::open(filename)?;
let file2 = fs::File::open(filename)?;
let reader1 = BufReader::new(file1);
let reader2 = BufReader::new(file2);
println!("Part one: {}", process_part_one(reader1));
println!("Part two: {}", process_part_two(reader2));
Ok(())
}
fn process_part_one(reader: BufReader) -> u32 {
let mut lines = reader.lines().peekable();
let mut prev_line: Option = None;
let mut sum = 0;
while let Some(line) = lines.next() {
let current_line = line.expect("line exists");
let next_line = match lines.peek() {
Some(Ok(line)) => Some(line),
Some(Err(_)) => None,
None => None,
};
match (prev_line, next_line) {
(None, Some(next)) => {
let lines = vec![Β€t_line, next];
sum += parse_lines(lines, true);
}
(Some(prev), Some(next)) => {
let lines = vec![&prev, Β€t_line, next];
sum += parse_lines(lines, false);
}
(Some(prev), None) => {
let lines = vec![&prev, Β€t_line];
sum += parse_lines(lines, false);
}
(None, None) => {}
}
prev_line = Some(current_line);
}
sum
}
fn process_part_two(reader: BufReader) -> u32 {
let mut lines = reader.lines().peekable();
let mut prev_line: Option = None;
let mut sum = 0;
while let Some(line) = lines.next() {
let current_line = line.expect("line exists");
let next_line = match lines.peek() {
Some(Ok(line)) => Some(line),
Some(Err(_)) => None,
None => None,
};
match (prev_line, next_line) {
(None, Some(next)) => {
let lines = vec![Β€t_line, next];
sum += parse_lines_for_gears(lines, true);
}
(Some(prev), Some(next)) => {
let lines = vec![&prev, Β€t_line, next];
sum += parse_lines_for_gears(lines, false);
}
(Some(prev), None) => {
let lines = vec![&prev, Β€t_line];
sum += parse_lines_for_gears(lines, false);
}
(None, None) => {}
}
prev_line = Some(current_line);
}
sum
}
fn parse_lines(lines: Vec<&String>, first_line: bool) -> u32 {
let mut sum = 0;
let mut num = 0;
let mut valid = false;
let mut char_vec: Vec> = Vec::new();
for line in lines {
char_vec.push(line.chars().collect());
}
let chars = match first_line {
true => &char_vec[0],
false => &char_vec[1],
};
for i in 0..chars.len() {
if chars[i].is_digit(10) {
// Add the digit to the number
num = num * 10 + chars[i].to_digit(10).expect("is digit");
// Check the surrounding character for non-period symbols
for &x in &[-1, 0, 1] {
for chars in &char_vec {
if (i as isize + x).is_positive() && ((i as isize + x) as usize) < chars.len() {
let index = (i as isize + x) as usize;
if !chars[index].is_digit(10) && chars[index] != '.' {
valid = true;
}
}
}
}
} else {
if valid {
sum += num;
}
valid = false;
num = 0;
}
}
if valid {
sum += num;
}
sum
}
fn parse_lines_for_gears(lines: Vec<&String>, first_line: bool) -> u32 {
let mut sum = 0;
let mut char_vec: Vec> = Vec::new();
for line in &lines {
char_vec.push(line.chars().collect());
}
let chars = match first_line {
true => &char_vec[0],
false => &char_vec[1],
};
for i in 0..chars.len() {
if chars[i] == '*' {
let surrounding_nums = get_surrounding_numbers(&lines, i);
let product = match surrounding_nums.len() {
0 | 1 => 0,
_ => surrounding_nums.iter().product(),
};
sum += product;
}
}
sum
}
fn get_surrounding_numbers(lines: &Vec<&String>, gear_pos: usize) -> Vec {
let mut nums: Vec = Vec::new();
let mut num: u32 = 0;
let mut valid = false;
for line in lines {
for (i, char) in line.chars().enumerate() {
if char.is_digit(10) {
num = num * 10 + char.to_digit(10).expect("is digit");
if [gear_pos - 1, gear_pos, gear_pos + 1].contains(&i) {
valid = true;
}
} else if num > 0 && valid {
nums.push(num);
num = 0;
valid = false;
} else {
num = 0;
valid = false;
}
}
if num > 0 && valid {
nums.push(num);
}
num = 0;
valid = false;
}
nums
}
#[cfg(test)]
mod tests {
use super::*;
const INPUT: &str = "467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..";
#[test]
fn test_process_part_one() {
let input_bytes = INPUT.as_bytes();
assert_eq!(4361, process_part_one(BufReader::new(input_bytes)));
}
#[test]
fn test_process_part_two() {
let input_bytes = INPUT.as_bytes();
assert_eq!(467835, process_part_two(BufReader::new(input_bytes)));
}
}
Late as always, as I'm on UK time and can't work on these until late evening.
Part 01 and Part 02 in Rust π¦ :
use std::{
env, fs,
io::{self, BufRead, BufReader},
};
#[derive(Debug)]
struct Sample {
r: u32,
g: u32,
b: u32,
}
fn split_cube_set(set: &[&str], colour: &str) -> Option {
match set.iter().find(|x| x.ends_with(colour)) {
Some(item) => item
.trim()
.split(' ')
.next()
.expect("Found item is present")
.parse::()
.ok(),
None => None,
}
}
fn main() -> io::Result<()> {
let args: Vec = env::args().collect();
let filename = &args[1];
let file = fs::File::open(filename)?;
let reader = BufReader::new(file);
let mut valid_game_ids_sum = 0;
let mut game_power_sum = 0;
let max_r = 12;
let max_g = 13;
let max_b = 14;
for line_result in reader.lines() {
let mut valid_game = true;
let line = line_result.unwrap();
let line_split: Vec<_> = line.split(':').collect();
let game_id = line_split[0]
.split(' ')
.collect::>()
.last()
.expect("item exists")
.parse::()
.expect("is a number");
let rest = line_split[1];
let cube_sets = rest.split(';');
let samples: Vec = cube_sets
.map(|set| {
let set_split: Vec<_> = set.split(',').collect();
let r = split_cube_set(&set_split, "red").unwrap_or(0);
let g = split_cube_set(&set_split, "green").unwrap_or(0);
let b = split_cube_set(&set_split, "blue").unwrap_or(0);
Sample { r, g, b }
})
.collect();
let mut highest_r = 0;
let mut highest_g = 0;
let mut highest_b = 0;
for sample in &samples {
if !(sample.r <= max_r && sample.g <= max_g && sample.b <= max_b) {
valid_game = false;
}
highest_r = u32::max(highest_r, sample.r);
highest_g = u32::max(highest_g, sample.g);
highest_b = u32::max(highest_b, sample.b);
}
if valid_game {
valid_game_ids_sum += game_id;
}
game_power_sum += highest_r * highest_g * highest_b;
}
println!("Sum of game ids: {valid_game_ids_sum}");
println!("Sum of game powers: {game_power_sum}");
Ok(())
}
Part 02 in Rust π¦ :
use std::{
collections::HashMap,
env, fs,
io::{self, BufRead, BufReader},
};
fn main() -> io::Result<()> {
let args: Vec = env::args().collect();
let filename = &args[1];
let file = fs::File::open(filename)?;
let reader = BufReader::new(file);
let number_map = HashMap::from([
("one", "1"),
("two", "2"),
("three", "3"),
("four", "4"),
("five", "5"),
("six", "6"),
("seven", "7"),
("eight", "8"),
("nine", "9"),
]);
let mut total = 0;
for _line in reader.lines() {
let digits = get_text_numbers(_line.unwrap(), &number_map);
if !digits.is_empty() {
let digit_first = digits.first().unwrap();
let digit_last = digits.last().unwrap();
let mut cat = String::new();
cat.push(*digit_first);
cat.push(*digit_last);
let cat: i32 = cat.parse().unwrap();
total += cat;
}
}
println!("{total}");
Ok(())
}
fn get_text_numbers(text: String, number_map: &HashMap<&str, &str>) -> Vec {
let mut digits: Vec = Vec::new();
if text.is_empty() {
return digits;
}
let mut sample = String::new();
let chars: Vec = text.chars().collect();
let mut ptr1: usize = 0;
let mut ptr2: usize;
while ptr1 < chars.len() {
sample.clear();
ptr2 = ptr1 + 1;
if chars[ptr1].is_digit(10) {
digits.push(chars[ptr1]);
sample.clear();
ptr1 += 1;
continue;
}
sample.push(chars[ptr1]);
while ptr2 < chars.len() {
if chars[ptr2].is_digit(10) {
sample.clear();
break;
}
sample.push(chars[ptr2]);
if number_map.contains_key(&sample.as_str()) {
let str_digit: char = number_map.get(&sample.as_str()).unwrap().parse().unwrap();
digits.push(str_digit);
sample.clear();
break;
}
ptr2 += 1;
}
ptr1 += 1;
}
digits
}
I am planning to use Rust this year to refresh my knowledge after having not used it for six months or so. I'm contemplating doing some solution visualisation this year, as I'm always impressed by that when others do it - but very much time availability dependent.
My biggest bugbear is between this and unadjustable font sizes within mobile apps.
I feel like it is; it's a story of a dreadful (woeful even) UX story highlighting a plethora of accessibility crimes. Of course, the subject of the story is not directly stated to have any kind of accessibility issues, but I think it highlights something many of us face regularly. There is a car park in my town that nearly always has its only two pay points out of order, leaving a similarly awful app as the only option, and I certainly felt this blog post resonate.
I'd throw another one in for being against word filters. I think it's condescending, and ultimately words are really only bad in context. What if I want to quote someone who used the word? Or what about if a bread making community takes off, where the word is pretty appropriate.
Certainly it's acceptable to remove posts, etc, when some words are used in certain ways, but I think this should be left to the discretion of the moderators.
Ultimately, anyone here who wants to ignore the rules and use those words will get around the filters anyway.