Great effort keeping the instance together. I'm very impressed, and grateful for both your efforts. It's probably just going over my head, but what is the inspiration for the name "Teasmade"? Is it a play on Tea and Maid?
I've just transferred it over to you :)
Great. I've appointed you as mod and transferred the community.
Reach out if you have any issues!
I have created the community and thrown up a basic sidebar: !woodpecker_ci@programming.dev
If you're interested in moderating the community, make a post there and let me know here.
Hi @lanartri@programming.dev.
I have created your requested community: !memoristik@programming.dev. As this is your own project, I have not created sidebar descriptions or anything like that and will leave this for you to do.
Please make a post in the community and I will grant you mod status and transfer the community to you. I'd then ask that you get some rules thrown up in the sidebar, as a minimum.
Today's problems felt really refreshing after yesterday.
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.
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)));
}
}
:::</map>
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.
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.
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.
Fair enough. And I'm sure the people who volunteered were probably thrilled to be involved with the project, it really is a brilliant piece of work.
Absolutely loved this. Never heard of the artist before this (though clearly she is very popular!). She seemed to have a lot of fun making the video.
The only thing that disappointed me was learning that a bunch of people had to volunteer their time to make this. Surely this made lot of money for the artist and video producers, could it really be that the margins were too thin to compensate all the people working on this?
I think you may have misread the title (and certainly not watch the video! :P). This is about games made in Godot, an open source game engine (mostly written in C++) featuring a scripting language similar to Python (but far from identical).
That's so interesting. I'm a developer myself, but haven't ever tackled a making a framework. Having obviously dealt many times with assuming there must be a framework error after hours of debugging (usually to find out it was indeed a user error...), I can imagine the debacle of trying to figure that out while developing one!
Okay, great. I've sent you my account via DM.
What have been your biggest challenges as you've developed this?
I'd give it a go, and probably will at some point, but just don't have time at the moment. But having had a cursory glance, I'm very impressed with the documentation. The framework looks similar enough to Vue and svelte that I feel it would be easy enough for most frontend devs to pick this up quite quickly.
Like many others here, at the company I work for you get nothing.
I do one on-call shift as primary per week and one as secondary. I then also cover a week every six weeks or so.
If shit really hits the fan, them work is pretty cool about taking some time back, but we're far from micromanaged as it is, so we can just kind of make it work.
I'd say an incidency probably occurs on around half of my primary shifts (and I've yet to ever do anything as secondary), and nearly always it was something I could resolve within one hour.
Every dev at the company is on the rota once they've got a few month's experience.
Based in the UK.
Well TIL. Didn't think to search for it. I am in my thirties and have lived in the UK my whole life, and I have never encountered or heard of such a device until now!
Given that, I think it's really creative name to use for such a purpose 👍