Inlägg

Inlägg som Sajox har skrivit i forumet
Av Sajox

Dag: 17
Språk: Rust

use std::{iter, str::FromStr}; #[derive(Debug, Default, Clone, Copy)] pub struct Point(i64, i64); #[derive(Debug, Default, Clone, Copy)] pub struct Area { start: Point, end: Point, } #[derive(Debug, Default, Clone, Copy)] pub struct Probe { pos: Point, velocity: Point, area: Area, } impl FromStr for Area { type Err = String; fn from_str(s: &str) -> Result<Self, Self::Err> { let r: Vec<_> = s .split(",") .flat_map(|s| s.split("..")) .map(|s| { s.chars() .filter(|&c| matches!(c, '0'..='9' | '-')) .collect::<String>() }) .filter_map(|s| s.parse::<i64>().ok()) .collect(); Ok(Self { start: Point(r[0], r[2]), end: Point(r[1], r[3]), }) } } impl Area { pub fn check(&self, point: Point) -> bool { let min_x = self.start.0.min(self.end.0); let max_x = self.start.0.max(self.end.0); let min_y = self.start.1.min(self.end.1); let max_y = self.start.1.max(self.end.1); min_x <= point.0 && max_x >= point.0 && min_y <= point.1 && max_y >= point.1 } pub fn range(&self) -> impl Iterator<Item = (i64, i64)> { let max_x = self.start.0.max(self.end.0); let min_y = self.start.1.min(self.end.1); (1..=max_x).flat_map(move |r| iter::repeat(r).zip(min_y..=min_y.abs())) } } impl Probe { pub fn new(area: Area, velocity: Point) -> Self { Self { pos: Default::default(), velocity, area, } } pub fn step(&mut self) { self.pos.0 += self.velocity.0; self.pos.1 += self.velocity.1; self.velocity.0 -= self.velocity.0.signum(); self.velocity.1 -= 1; } pub fn within_target(&self) -> bool { self.area.check(self.pos) } pub fn bounds_check(&self) -> bool { let max_x = self.area.start.0.max(self.area.end.0); let min_y = self.area.start.1.min(self.area.end.1); max_x < self.pos.0 || min_y > self.pos.1 } pub fn simulate(&mut self) -> Option<i64> { let mut max = 0; while !self.bounds_check() { max = self.pos.1.max(max); self.step(); if self.within_target() { return Some(max); } } None } } pub fn solve(area: Area, mut func: impl FnMut(i64) -> bool) { for (x, y) in area.range() { if let Some(value) = Probe::new(area, Point(x, y)).simulate() { if !func(value) { return; } } } } fn main() { let area = std::fs::read_to_string("input.txt") .unwrap() .lines() .filter_map(|s| s.parse::<Area>().ok()) .next() .unwrap(); let mut max = 0; solve(area, |value| { max = max.max(value); if max > value { return false; } true }); println!("Part 1: {}", max); // 2850 let mut count = 0; solve(area, |_| { count += 1; true }); println!("Part 2: {}", count); // 1117 }

Dold text
Av Sajox

Dag: 15
Språk: Rust

use std::{ cmp::Reverse, collections::{BinaryHeap, HashMap}, ops::Div, }; type RiskLevel = HashMap<(usize, usize), usize>; struct Chiton { map: RiskLevel, costs: RiskLevel, width: usize, height: usize, } impl Chiton { pub fn new(input: &str) -> Self { let mut map = HashMap::new(); let mut costs = HashMap::new(); let mut height = 0; let mut width = 0; for (y, line) in input.lines().enumerate() { for (x, c) in line.chars().enumerate() { map.insert((x, y), c.to_digit(10).unwrap() as usize); costs.insert((x, y), usize::MAX); height = y; width = x; } } Self { map, costs, width: width + 1, height: height + 1, } } pub fn solve_part1(&self) -> usize { self.solve(self.width, self.height, &self.map, self.costs.clone()) } pub fn solve_part2(&self) -> usize { let mut new_map = HashMap::new(); let mut costs = HashMap::new(); for tile_y in 0..5 { for tile_x in 0..5 { for y in 0..self.height { for x in 0..self.width { let pos = (x + tile_x * self.width, y + tile_y * self.height); let value = self.map[&(x, y)] + tile_x + tile_y; new_map.insert(pos, value % 10 + value.div(10)); costs.insert(pos, usize::MAX); } } } } self.solve(self.width * 5, self.height * 5, &new_map, costs) } fn solve(&self, width: usize, height: usize, map: &RiskLevel, mut costs: RiskLevel) -> usize { let mut visit = BinaryHeap::new(); visit.push((Reverse(0), (0, 0))); while let Some(args) = visit.pop() { let (Reverse(cost), pos) = args; if cost < costs[&pos] { costs.insert(pos, cost); for p in self.adjacent(width, height, pos) { visit.push((Reverse(cost + map[&p]), p)); } } } costs[&(width - 1, height - 1)] } fn adjacent(&self, width: usize, height: usize, (x, y): (usize, usize)) -> Vec<(usize, usize)> { [(0, -1), (-1, 0), (1, 0), (0, 1)] .iter() .filter(|&(dx, dy)| { let x = x as i64; let y = y as i64; x + dx >= 0 && y + dy >= 0 && x + dx < width as i64 && y + dy < height as i64 }) .map(|&(dx, dy)| ((x as i64 + dx) as usize, (y as i64 + dy) as usize)) .collect() } } fn main() { let input = std::fs::read_to_string("input.txt").unwrap(); let chiton = Chiton::new(&input); println!("Part 1: {}", chiton.solve_part1()); // 720 println!("Part 2: {}", chiton.solve_part2()); // 3025 }

Dold text
Av Sajox

Dag: 13
Språk: Rust

#![feature(hash_drain_filter)] use std::collections::HashMap; const MARK: char = '█'; fn main() { let input = std::fs::read_to_string("input.txt").unwrap(); let mut lines = input.lines(); let mut map: HashMap<(u32, u32), char> = HashMap::new(); let mut instructions = Vec::new(); while let Some(f) = lines.next() { if f.is_empty() { break; } let (x, y) = f.split_once(',').unwrap(); map.insert((x.parse::<u32>().unwrap(), y.parse::<u32>().unwrap()), MARK); } while let Some(f) = lines.next() { instructions.push( f.split(' ') .last() .unwrap() .split_once('=') .map(|p| (p.0, p.1.parse::<u32>().unwrap())) .unwrap(), ); } run(&mut map, &instructions[..1]); println!("Part 1: {}", map.len()); // 710 run(&mut map, &instructions[1..]); print(&mut map); // Part 2: EPLGRULR } fn run(data: &mut HashMap<(u32, u32), char>, instructions: &[(&str, u32)]) { for instruction in instructions { match instruction.0 { "y" => fold_y(data, instruction.1), _ => fold_x(data, instruction.1), } } } fn print(data: &mut HashMap<(u32, u32), char>) -> Option<()> { let y_max = data.keys().map(|(_, y)| *y).max()?; let y_min = data.keys().map(|(_, y)| *y).min()?; let x_max = data.keys().map(|(x, _)| *x).max()?; let x_min = data.keys().map(|(x, _)| *x).min()?; for y in y_min..=y_max { for x in x_min..=x_max { print!("{}", data.get(&(x, y)).unwrap_or(&' ')); } println!(); } Some(()) } fn fold_y(data: &mut HashMap<(u32, u32), char>, y: u32) { let height = y * 2; let drained: Vec<_> = data.drain_filter(|(_, y1), _| y1 > &y).collect(); for ((x, y), _) in drained { data.insert((x, height - y), MARK); } } fn fold_x(data: &mut HashMap<(u32, u32), char>, x: u32) { let width = x * 2; let drained: Vec<_> = data.drain_filter(|(x1, _), _| x1 > &x).collect(); for ((x2, y), _) in drained { data.insert((width - x2, y), MARK); } }

Dold text
Av Sajox

Dag: 12
Språk: Rust

use std::collections::{HashMap, HashSet}; fn main() { let input = std::fs::read_to_string("input.txt").unwrap(); let data: Vec<_> = input.lines().filter_map(|s| s.split_once('-')).collect(); let mut map: HashMap<_, HashSet<_>> = HashMap::new(); for (begin, end) in data { map.entry(begin).or_default().insert(end); map.entry(end).or_default().insert(begin); } println!("Part 1: {}", paths(&map, HashSet::new(), "start", false)); // 3230 println!("Part 2: {}", paths(&map, HashSet::new(), "start", true)); // 83475 } fn paths<'a>( data: &HashMap<&str, HashSet<&'a str>>, mut visited: HashSet<&'a str>, id: &'a str, mut twice: bool, ) -> u32 { if id == "end" { return 1; } if is_small(id) { if visited.contains(id) { if twice && id != "start" { twice = false; } else { return 0; } } else { visited.insert(id); } } match data.get(id) { Some(ids) => ids .iter() .fold(0, |acc, v| acc + paths(data, visited.clone(), v, twice)), _ => 0, } } pub fn is_small(s: &str) -> bool { s.chars().all(char::is_lowercase) }

Dold text
Av Sajox

Dag: 11
Språk: Rust

const ADJACENT: &[(i64, i64); 8] = &[ (-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1), ]; const WIDTH: i64 = 10; const HEIGHT: i64 = 10; macro_rules! check_and_update { ($input:expr, $arr:expr, $count:expr, $x:expr, $y:expr) => {{ $input[$y][$x] += 1; if $input[$y][$x] > 9 { $input[$y][$x] = 0; $count += 1; $arr.push(($x, $y)) } }}; } fn main() { let mut input: Vec<Vec<_>> = std::fs::read_to_string("input.txt") .unwrap() .lines() .map(|s| { s.chars() .filter_map(|c| c.to_digit(10).map(|d| d as usize)) .collect() }) .collect(); println!("{}", part1(&mut input.clone())); // 1665 println!("{}", part2(&mut input)); // 235 } fn part1(input: &mut Vec<Vec<usize>>) -> u64 { (0..100).fold(0, |acc, _| acc + calc_step(input)) } fn part2(input: &mut Vec<Vec<usize>>) -> u64 { let mut steps = 0; while !input.iter().all(|a| a.iter().all(|&b| b == 0)) { calc_step(input); steps += 1; } steps } fn calc_step(input: &mut Vec<Vec<usize>>) -> u64 { let mut flashes = vec![]; let mut count = 0; for y in 0..input.len() { for x in 0..input[0].len() { check_and_update!(input, flashes, count, x, y); } } while !flashes.is_empty() { for flash in flashes.drain(0..).collect::<Vec<_>>() { flashes.append( &mut adjacent(flash).iter().fold(vec![], |mut acc, &(x, y)| { if input[y][x] != 0 { check_and_update!(input, acc, count, x, y); } acc }), ) } } count } fn adjacent((x, y): (usize, usize)) -> Vec<(usize, usize)> { ADJACENT .iter() .filter(|&(dx, dy)| { let x = x as i64; let y = y as i64; x + dx >= 0 && y + dy >= 0 && x + dx < WIDTH && y + dy < HEIGHT }) .map(|&(dx, dy)| ((x as i64 + dx) as usize, (y as i64 + dy) as usize)) .collect() }

Dold text
Av Sajox
Skrivet av Thomas:

Dag: 8
Språk: TypeScript (med Node.js)
Lösning: https://github.com/exscape/AOC2021/blob/main/src/day8.ts

"Löste" äntligen dag 8 del 2. Känner dock fortfarande inte att jag löst den, eftersom jag i slutändan körde brute force för att testa alla möjliga mappings mellan de olika kopplingarna (5040 st per rad att testa).

Nu när jag fått fram rätt svar känns det dock mer OK att kolla in andras lösningar. Vill lösa alla själv utan hints, men denna gången fick det bli en fullösning för att jag inte skulle ge upp helt.
Har helt fastnat i mitt tänk och kanske kommer få en "men för fan..."-upplevelse när jag ser andras lösningar.

Renskrev precis min dag 8 del 2 lösning. Kanske går att förstå bättre nu. Ingen brute force lösning.

Av Sajox

Dag: 10
Språk: Rust

const PAIR: &[char; 4] = &['<', '(', '[', '{']; const SCORE: &[u32; 4] = &[25137, 3, 57, 1197]; const SCORE2: &[usize; 4] = &[4, 1, 2, 3]; fn main() { let input = std::fs::read_to_string("input.txt").unwrap(); println!( "Part 1: {}", input .lines() .filter_map(|f| process(f, false).err()) .map(|chr| match_with(chr, SCORE)) .sum::<u32>() ); // 411471 let mut scores = input .lines() .filter(|f| process(f, false).is_ok()) .filter_map(|f| process(f, true).ok()) .map(|f| { f.iter() .fold(0usize, |acc, &chr| acc * 5 + match_with(chr, SCORE2)) }) .collect::<Vec<_>>(); scores.sort_unstable(); println!("Part 2: {}", scores[scores.len() / 2]); // 3122628974 } fn process(s: &str, repair: bool) -> Result<Vec<char>, char> { let mut acc = vec![]; for value in s.chars() { if matches!(value, '>' | ')' | ']' | '}') { if repair { acc.pop(); } else { acc.pop() .filter(|&p| p == match_with(value, PAIR)) .ok_or(value)?; } } else { acc.push(value) } } acc.reverse(); Ok(acc) } fn match_with<T: Copy>(chr: char, m: &[T; 4]) -> T { match chr { '>' | '<' => m[0], ')' | '(' => m[1], ']' | '[' => m[2], '}' | '{' => m[3], _ => panic!("brunsås"), } }

Dold text
Av Sajox

Dag: 9
Språk: Rust

type ColData<'a> = (Option<&'a usize>, Option<&'a usize>, Option<&'a usize>); type RowData<'a> = (ColData<'a>, ColData<'a>, ColData<'a>); fn main() { let input: Vec<Vec<_>> = std::fs::read_to_string("input.txt") .unwrap() .lines() .map(|s| { s.chars() .filter_map(|c| c.to_digit(10).map(|d| d as usize)) .collect() }) .collect(); println!( "{}", part1(&input).iter().map(|(r, _, _)| r + 1).sum::<usize>() ); println!("{}", part2(&input).unwrap()); } fn part1(data: &[Vec<usize>]) -> Vec<(usize, usize, usize)> { let mut hits = Vec::new(); for row in 0..data.len() { for col in 0..data[0].len() { if let Some(&risk_lvl) = part1_matcher(fetch_row(data, row, col)) { hits.push((risk_lvl, row, col)); } } } hits } fn part2(data: &[Vec<usize>]) -> Option<usize> { let lows = part1(data); let mut sizes = vec![]; for (_, row, col) in lows { sizes.push(solve(data, &mut vec![], row, col)); } sizes.sort_unstable(); Some(sizes.pop()? * sizes.pop()? * sizes.pop()?) } fn solve(data: &[Vec<usize>], res: &mut Vec<(usize, usize)>, row: usize, col: usize) -> usize { if res.contains(&(row, col)) { return 0; } let mut accum = 1; let (up, mid, down) = fetch_row(data, row, col); res.push((row, col)); for i in [ (up.1, if row > 0 { row - 1 } else { 0 }, col), (mid.0, row, if col > 0 { col - 1 } else { 0 }), (mid.2, row, col + 1), (down.1, row + 1, col), ] { accum += match i { (Some(&x), row, col) if x != 9 => solve(data, res, row, col), _ => 0, }; } accum } fn part1_matcher((up, mid, down): RowData) -> Option<&usize> { match (up.1, mid, down.1) { (None, (None, x @ Some(a), Some(b)), Some(c)) if [b, c].iter().all(|v| a < v) => x, (None, (Some(d), x @ Some(a), Some(b)), Some(c)) if a < b && a < c && a < d => x, (None, (Some(b), x @ Some(a), None), Some(c)) if [b, c].iter().all(|v| a < v) => x, (Some(c), (None, x @ Some(a), Some(b)), Some(d)) if [b, c, d].iter().all(|v| a < v) => x, (Some(c), (Some(d), x @ Some(a), Some(b)), Some(e)) if [b, c, d, e].iter().all(|v| a < v) => { x } (Some(c), (Some(b), x @ Some(a), None), Some(d)) if [b, c, d].iter().all(|v| a < v) => x, (Some(c), (None, x @ Some(a), Some(b)), None) if [b, c].iter().all(|v| a < v) => x, (Some(c), (Some(d), x @ Some(a), Some(b)), None) if [b, c, d].iter().all(|v| a < v) => x, (Some(c), (Some(b), x @ Some(a), None), None) if [b, c].iter().all(|v| a < v) => x, _ => None, } } fn fetch_row(input: &[Vec<usize>], row: usize, col: usize) -> RowData { ( row.checked_sub(1) .map(|v| fetch_col(input.get(v), col)) .unwrap_or((None, None, None)), fetch_col(input.get(row), col), row.checked_add(1) .map(|v| fetch_col(input.get(v), col)) .unwrap_or((None, None, None)), ) } fn fetch_col(row: Option<&Vec<usize>>, col: usize) -> ColData { if let Some(value) = row { ( col.checked_sub(1).map(|v| value.get(v)).flatten(), value.get(col), col.checked_add(1).map(|v| value.get(v)).flatten(), ) } else { (None, None, None) } }

Dold text
Av Sajox

Dag: 8
Språk: Rust

Lagomt segt med denna efter jobbet.
Min hjärna kändes halvgrillad innan jag såg mönstret på del 2.

Dold text

use std::collections::HashMap; const ZERO: &[usize; 6] = &[0, 1, 2, 4, 5, 6]; const ONE: &[usize; 2] = &[2, 5]; const TWO: &[usize; 5] = &[0, 2, 3, 4, 6]; const THREE: &[usize; 5] = &[0, 2, 3, 5, 6]; const FOUR: &[usize; 4] = &[1, 2, 3, 5]; const FIVE: &[usize; 5] = &[0, 1, 3, 5, 6]; const SIX: &[usize; 6] = &[0, 1, 3, 4, 5, 6]; const SEVEN: &[usize; 3] = &[0, 2, 5]; const EIGHT: &[usize; 7] = &[0, 1, 2, 3, 4, 5, 6]; const NINE: &[usize; 6] = &[0, 1, 2, 3, 5, 6]; fn main() { let input = std::fs::read_to_string("input.txt").unwrap(); let input: Vec<_> = input .lines() .map(|s| s.split_once('|').unwrap()) .map(|t| { let mut a = t.0.split_whitespace().collect::<Vec<_>>(); let b = t.1.split_whitespace().collect::<Vec<_>>(); a.sort_by(|a, b| a.len().cmp(&b.len())); (a, b) }) .collect(); println!("part 1: {}", part1(&input)); // 318 println!("part 2: {}", part2(&input).unwrap()); // 996280 } fn part1(data: &[(Vec<&str>, Vec<&str>)]) -> usize { data.iter() .flat_map(|p| &p.1) .filter(|p| matches!(p.len(), 2 | 3 | 4 | 7)) .count() } fn part2(data: &[(Vec<&str>, Vec<&str>)]) -> Option<usize> { let mut total = 0; for (usp, out) in data.iter() { let segment_occurences: HashMap<char, u8> = usp.iter() .flat_map(|s| s.chars()) .fold(HashMap::new(), |mut map, v| { *map.entry(v).or_default() += 1; map }); let mut segments = [' '; 7]; for code in usp.iter() { // Samlar ihop så vi slipper dubbelborrow let only_new_segments: Vec<_> = code .chars() .filter(|c| !segments.contains(c)) .map(|c| (c, segment_occurences.get(&c).unwrap())) .collect(); for (chr, occurences) in only_new_segments { match (code.len(), occurences) { (2, 8) => segments[2] = chr, (2, _) => segments[5] = chr, (3, _) => segments[0] = chr, (4, 6) => segments[1] = chr, (4, _) => segments[3] = chr, (7, 4) => segments[4] = chr, (7, _) => segments[6] = chr, _ => (), } } } // Bygg ihop talet total += out .iter() .fold(0usize, |acc, digit| acc * 10 + get_digit(&segments, digit)); } Some(total) } fn get_digit(segments: &[char; 7], digit: &str) -> usize { match digit.len() { 6 if chk_chrs(ZERO.iter().map(|&f| segments[f]), digit) => 0, 2 if chk_chrs(ONE.iter().map(|&f| segments[f]), digit) => 1, 5 if chk_chrs(TWO.iter().map(|&f| segments[f]), digit) => 2, 5 if chk_chrs(THREE.iter().map(|&f| segments[f]), digit) => 3, 4 if chk_chrs(FOUR.iter().map(|&f| segments[f]), digit) => 4, 5 if chk_chrs(FIVE.iter().map(|&f| segments[f]), digit) => 5, 6 if chk_chrs(SIX.iter().map(|&f| segments[f]), digit) => 6, 3 if chk_chrs(SEVEN.iter().map(|&f| segments[f]), digit) => 7, 7 if chk_chrs(EIGHT.iter().map(|&f| segments[f]), digit) => 8, 6 if chk_chrs(NINE.iter().map(|&f| segments[f]), digit) => 9, _ => panic!("brunsås"), } } fn chk_chrs<'a>(mut segments: impl Iterator<Item = char>, digit: &str) -> bool { segments.all(|chr| digit.contains(chr)) }

Dold text
Av Sajox

Dag: 7
Språk: Rust

#![feature(int_abs_diff)] fn main() { let input: Vec<u32> = std::fs::read_to_string("input.txt") .unwrap() .split(',') .filter_map(|s| s.parse().ok()) .collect(); fuel(&input, |a, b| a.abs_diff(b)); // 355592 fuel(&input, |a, b| (0..=a.abs_diff(b)).sum::<u32>()); // 101618069 } fn fuel(data: &[u32], func: fn(u32, u32) -> u32) { let mut fuel = u32::MAX; for v in 1.. { let curr_fuel = data.iter().map(|&a| func(a, v)).sum::<u32>(); fuel = curr_fuel.min(fuel); if fuel < curr_fuel { break; } } println!("{}", fuel); }

Dold text
Av Sajox

Dag: 6
Språk: Rust

fn main() { let index: Vec<usize> = std::fs::read_to_string("input.txt") .unwrap() .split(',') .filter_map(|s| s.parse().ok()) .collect(); count_fish(&index, 80); // 352872 count_fish(&index, 256); // 1604361182149 } fn count_fish(data: &[usize], days: usize) { let mut window = [0usize; 9]; for i in data.iter() { window[*i] += 1; } for _ in 0..days { window.rotate_left(1); window[6] += window[8]; } println!("{:?}", window.iter().sum::<usize>()); }

Dold text
Av Sajox

Day: 5
Språk: Rust

use std::{collections::HashMap, str::FromStr}; type Point = (u32, u32); #[derive(Debug, Clone)] struct Line { p1: Point, p2: Point, } impl Line { pub fn points(&self) -> Vec<Point> { let mut points = vec![self.p1]; let mut step_x = self.p1.0; let mut step_y = self.p1.1; while (step_x, step_y) != self.p2 { step_x = Line::step(step_x, self.p2.0); step_y = Line::step(step_y, self.p2.1); points.push((step_x, step_y)); } points } fn step(a: u32, b: u32) -> u32 { match (a, b) { (a, b) if a < b => a + 1, (a, b) if a > b => a - 1, _ => a, } } } impl FromStr for Line { type Err = String; fn from_str(s: &str) -> Result<Self, Self::Err> { let parts: Vec<&str> = s.split_whitespace().collect(); let parts: Vec<u32> = parts .iter() .flat_map(|s| s.split(',').collect::<Vec<_>>()) .filter_map(|s| s.parse().ok()) .collect(); Ok(Self { p1: (parts[0], parts[1]), p2: (parts[2], parts[3]), }) } } fn count_overlap(lines: &[Line], filter: impl Fn(&Line) -> bool) -> usize { let mut map: HashMap<Point, u32> = HashMap::new(); for line in lines.iter().filter(|line| filter(line)) { for point in line.points() { let pos = map.entry(point).or_default(); *pos += 1; } } map.iter().filter(|p| p.1 >= &2).count() } fn main() { let input = std::fs::read_to_string("input.txt").unwrap(); let lines: Vec<Line> = input.lines().filter_map(|s| s.parse().ok()).collect(); println!( "Part 1: {}", count_overlap(&lines, |line| line.p1.0 == line.p2.0 || line.p1.1 == line.p2.1) ); // 4655 println!("Part 2: {}", count_overlap(&lines, |_| true)); // 20500 }

Dold text
Av Sajox
Skrivet av Thomas:

... och Lisp antar jag? Ser ut att vara huvudsaklingen från relaterade språk.

Kollade upp hur Lisp ser ut och jag kan bara hålla med.

Av Sajox
Skrivet av Bryal:

Dag: 4
Språk: Carth

Utnyttjade sortering på ett ställe för att lösa denna uppgiften, så nu har man fått lägga till en Merge Sort i standardbiblioteket

(import std) (define main (do io/bind (<- input (io/map unwrap! (read-file "inputs/day4.txt"))) (let (([draws boards] (map-two parse-draws parse-boards (unwrap! (string/split-first-line input)))) (scores (play draws (array/iter boards))))) (display (str-append "Part 1: first to win = " (show-int (array/first! scores)))) (display (str-append "Part 1: last to win = " (show-int (array/last! scores)))))) (define (parse-draws s) (map (<o unwrap! parse-int) (string/splits-on "," s))) (define: (parse-boards s) (Fun Str (Array (Array (Maybe Int)))) (array/collect (map (fun (c) (array/collect (map parse-int (flat-map words (skip (to-nat 1) (array/iter c)))))) (iter/chunks (to-nat 6) (lines s))))) (define (play draws boards) (car (foldl (fun ([scores1 boards] draw) (let1 [scores2 boards'] (mark-boards draw boards) [(array/append scores1 scores2) (list/iter boards')])) [array/nil boards] draws))) (define (mark-boards x bs) (map-car (<o (merge-sort int/cmp) array/collect-list) (partition-eithers (map (mark-board x) bs)))) (define (mark-board x b) (match (array/find-by (maybe/= = (Some x)) b) (case (Some i) (let1 b' (array/set! i None b) (if (bingo? (to-int i) b') (Left (score x b')) (Right b')))) (case None (Right b)))) (define (bingo? i b) (define (winning-line? is) (all none? (map (fun (i) (array/lookup! (to-nat i) b)) is))) (let ((row-is (take 5 (range-from (* (/ i 5) 5)))) (col-is (map (fun (j) (+ (rem i 5) (* 5 j))) (range 0 4)))) (or (winning-line? row-is) (winning-line? col-is)))) (define (score x b) (* x (sum (cat-maybes (array/iter b)))))

Dold text

Jag får lite vibbar av Objective-C och Rust ihopsmetat då jag försöker förstå syntaxen.

Av Sajox

Dag: 4
Språk: Rust

#[derive(Debug, Clone)] struct Board { board: Vec<(u32, bool)>, size: usize, bingo: bool, last_number: Option<u32>, } impl Board { pub fn new(board: &[u32], size: usize) -> Self { let board = board.iter().map(|v| (*v, false)).collect(); Self { board, size, bingo: false, last_number: None, } } pub fn mark(&mut self, number: u32) { if self .board .iter_mut() .find(|p| p.0 == number) .map(|p| p.1 = true) .is_some() { self.last_number = Some(number); } } pub fn row_bingo(&self) -> bool { let size = self.size; self.board .chunks(size) .any(|chunk| chunk.iter().all(|item| item.1)) } pub fn column_bingo(&self) -> bool { let size = self.size; 'outer: for col in 0..size { for row in 0..size { if !self.board[row * size + col].1 { continue 'outer; } } return true; } false } // Immutable för att kunna filtrera pub fn bingo(&self) -> bool { self.bingo } pub fn bingo_mut(&mut self) -> bool { if !self.bingo { self.bingo = self.row_bingo() || self.column_bingo() } self.bingo } pub fn score(&self) -> u32 { let sum: u32 = self.board.iter().filter(|p| !p.1).map(|p| p.0).sum(); sum * self.last_number.unwrap_or(0) } } fn create_game() -> Option<(Vec<Board>, Vec<u32>)> { let input = std::fs::read_to_string("input.txt").ok()?; let mut lines = input.lines(); let numbers_to_draw: Vec<_> = lines .next()? .split(',') .filter_map(|s| s.parse::<u32>().ok()) .collect(); let mut temp = vec![]; let mut boards = vec![]; for line in lines.filter(|p| !p.is_empty()) { let mut line = line .split(' ') .filter_map(|s| s.parse::<u32>().ok()) .collect::<Vec<_>>(); let size = line.len(); temp.append(&mut line); if temp.len() == size.pow(2) { boards.push(Board::new(&temp, size)); temp.clear(); } } Some((boards, numbers_to_draw)) } fn play(mut filter: impl FnMut(&mut Board) -> bool) { let (mut boards, nums) = create_game().unwrap(); 'outer: for num in nums { for board in boards.iter_mut().filter(|board| !board.bingo()) { board.mark(num); if filter(board) { break 'outer; } } } } fn main() { // Part 1 score 89001 play(|board: &mut Board| { if board.bingo_mut() { println!("{}", board.score()); return true; } false }); // Part 2 score 7296 let mut board_snapshot = None; play(|board: &mut Board| { if board.bingo_mut() { board_snapshot = Some(board.clone()); } false }); if let Some(board) = board_snapshot { println!("{}", board.score()); } }

Dold text
Av Sajox

Dag: 3
Språk: Rust

const SIZE: usize = 12; fn main() { let input = std::fs::read_to_string("input.txt").unwrap(); let input: Vec<_> = input.lines().collect(); part1(&input); part2(input); } fn part1(data: &[&str]) { let gamma_rate = calc_gamma_rate(data); let epsilon_rate = gamma_rate ^ usize::from_str_radix(&"1".repeat(SIZE), 2).unwrap(); println!("Part 1: {}", gamma_rate * epsilon_rate); //2595824 } fn part2(data: Vec<&str>) { let oxygen_generator_rating = life_support(data.clone(), |lines, index, len| { let counted = count_ones(lines); if counted[index] >= len - counted[index] { '1' } else { '0' } }); let scrubber_rating = life_support(data.clone(), |lines, index, len| { let counted = count_ones(lines); if counted[index] >= len - counted[index] { '0' } else { '1' } }); println!("Part 2: {}", oxygen_generator_rating * scrubber_rating); //2135254 } fn count_ones(data: &[&str]) -> Vec<usize> { data.iter().fold(vec![0usize; SIZE], |mut acc, value| { for (i, s) in value.chars().enumerate() { acc[i] += matches!(s, '1') as usize; } acc }) } fn calc_gamma_rate(data: &[&str]) -> usize { count_ones(data).iter().fold(0usize, |mut acc, &s| { acc <<= 1; acc + (s > data.len() - s) as usize }) } fn life_support(mut lines: Vec<&str>, f: impl Fn(&[&str], usize, usize) -> char) -> usize { for i in 0.. { let keep_token = f(&lines, i, lines.len()); lines = lines .into_iter() .filter(|&c| c.chars().nth(i).unwrap() == keep_token) .collect(); if lines.len() == 1 { break; } } usize::from_str_radix(lines[0], 2).unwrap() }

Dold text
Av Sajox

Dag: 2
Språk: Rust

use std::str::FromStr; pub enum Command { Forward(u32), Down(u32), Up(u32), } impl FromStr for Command { type Err = String; fn from_str(s: &str) -> Result<Self, Self::Err> { let (command, value) = s.split_once(' ').ok_or("Felaktig instruktion")?; let value: u32 = value.parse().map_err(|_| "Felaktig siffra")?; match command { "forward" => Ok(Command::Forward(value)), "down" => Ok(Command::Down(value)), "up" => Ok(Command::Up(value)), _ => Err("Okänt kommando".into()), } } } #[derive(Debug, Default)] struct Submarine { horizontal: u32, depth: u32, aim: u32, } impl Submarine { pub fn instruction(&mut self, command: &Command) { match command { Command::Forward(value) => self.horizontal += value, Command::Down(value) => self.depth += value, Command::Up(value) => self.depth -= value, } } pub fn instruction_with_aim(&mut self, command: &Command) { match command { Command::Forward(value) => { self.horizontal += value; self.depth += self.aim * value; } Command::Down(value) => self.aim += value, Command::Up(value) => self.aim -= value, } } pub fn product(&self) -> u32 { self.horizontal * self.depth } } fn main() { let input = std::fs::read_to_string("input.txt"); let commands: Vec<Command> = input .unwrap() .lines() .filter_map(|s| s.parse::<_>().ok()) .collect(); // Part 1 let mut submarine = Submarine::default(); for command in commands.iter() { submarine.instruction(command); } println!("{}", submarine.product()); // 2027977 //Part 2 let mut submarine = Submarine::default(); for command in commands.iter() { submarine.instruction_with_aim(command); } println!("{}", submarine.product()); // 1903644897 }

Dold text
Av Sajox

Dag: 1
Språk: Rust

fn main() { let input = std::fs::read_to_string("input.txt"); let res: Vec<_> = input .unwrap() .lines() .filter_map(|s| s.parse::<_>().ok()) .collect(); calc(&res); // 1342 calc(&res.windows(3).map(|s| s.iter().sum()).collect::<Vec<_>>()); // 1378 } fn calc(data: &[i32]) { let acc = data.iter().fold((-1, 0), |mut acc, &v| { acc.0 += (v > acc.1) as i32; (acc.0, v) }); println!("Svar: {}", acc.0); }

Dold text
Av Sajox
Skrivet av xobust:

Jag och flickvännen började göra advent of code för några datar sedan,
vi har mest löst uppgifterna i python hitils.
Bestämde mig för att testa Rust för första gången för lucka 13.
Ni får gärna ge mig tips på hur man använder rust mer effeiktivt.
Standard IO var inte lika lätt som IO stream I C++. Det lilla jag
testat av streams och optional hantering var väldigt anvdvändbart.
Dokumentationen var också väldigt användbar med exempel.

use std::io;

fn main() {
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Expected input");
let startTime : u32 = input.trim().parse::<u32>()
.expect("Needs to be a number");
println!("{}",startTime);

io::stdin().read_line(&mut input).expect("Expected another line");
let busses : Vec<u32> = input.split("\n").skip(1)
.next().expect("Needs input").split(',')
.filter_map(|x| match x.parse::<u32>() {
Ok(i) => Some(i),
Err(_) => None,
}
).collect();

println!("{:?}", buss);

let mut results =
busses.iter().map(|bus| match buss % startTime {
0 => (0, bus),
_ =>
((bus * (startTime/bus + 1))-startTime,
bus)
}).collect::<Vec<(u32,&u32)>>();

results.sort_by_key(|b| b.0);

println!("First Buss {} at time {}", results[0].1, results[0].0);
println!("Answer {}", results[0].0 * results[0].1);
}

Dold text

Det är absoult inget fel på detta. Själv har jag mest läst från filer.
En variant skulle kunna se ut så här med lite småjusteringar. Hoppas det hjälpte.

use std::io::{self, BufRead}; fn main() -> std::result::Result<(), Box<dyn std::error::Error>> { // Lås stdin och hämta ett buffrat handtag let stdin = io::stdin(); let handtag = stdin.lock(); // Hämta ut 2 rader let data = handtag .lines() .take(2) .filter_map(|s| s.ok()) .collect::<Vec<_>>(); // Ingen trim behövs. Nyrad redan borttagen från .lines() let start_time = data[0].parse::<u32>()?; println!("Start: {}", start_time); let busses = data[1] .split(',') // Använd .ok() som konverterar direkt till en Option från ett result. .filter_map(|x| x.parse::<u32>().ok()) .collect::<Vec<u32>>(); println!("{:?}", busses); let mut results = busses .iter() .map(|bus| match bus % start_time { 0 => (0, bus), _ => ((bus * (start_time / bus + 1)) - start_time, bus), }) .collect::<Vec<(u32, &u32)>>(); results.sort_by_key(|b| b.0); println!("First Buss {} at time {}", results[0].1, results[0].0); println!("Answer {}", results[0].0 * results[0].1); Ok(()) }

Dold text
Av Sajox
Skrivet av Yoshman:

Skillnaden ligger i hur din kod beräknar loop_size jämfört med t.ex. min Swift-lösning (som använder reduce).

Med fold/reduce blir det dyrare att beräkna resultatet ju högre värde man har på loop_size och framförallt blir resultatet O(N^2) om man anropar transform() vid beräkningen av loop_size

Min Swift-lösning översatt till Rust blir ungefär detta (med din indata)

type Num = u64; const CARD_PUB_KEY: Num = 8458505; const DOOR_PUB_KEY: Num = 16050997; const DIVISOR: Num = 20201227; fn transform(subject_number: Num, loop_size: Num) -> Num { (0..loop_size).fold(1, |value, _| (value * subject_number) % DIVISOR) } fn loop_size_calc(pub_key: Num) -> Num { let subject_number: Num = 7; let mut loop_size: Num = 0; let mut value: Num = 1; while value != pub_key { loop_size += 1; value = (value * subject_number) % DIVISOR; } return loop_size; } fn day25(card_pub_key: Num, door_pub_key: Num) { let card_loop_size = loop_size_calc(card_pub_key); let secret_key = transform(door_pub_key, card_loop_size); println!("🌟 Part 1 : {}", secret_key) } fn bench(f: &dyn Fn()) { let start = std::time::Instant::now(); f(); println!("⌚ Took : {}ms", start.elapsed().as_millis()); } fn main() { bench(&||day25(CARD_PUB_KEY, DOOR_PUB_KEY)); }

Denna lösning hittar loop_size på linjär tid mot storleken på loop_size. På min 3900X tar ovan 84 ms för att slutföra beräkningen.

Ändrar man den markerade delen till detta blir det i stället O(N^2), orkade inte vänta till beräkningen blev klar då (tar mer än en minut).

fn loop_size_calc(pub_key: Num) -> Num { let subject_number: Num = 7; let mut loop_size: Num = 0; while transform(subject_number, loop_size) != pub_key { loop_size += 1 } return loop_size; }

Implementerar man istället transform() på det sätet du gjort blir den funktionen tillräckligt effektiv för att använda transform() även i loop_size_calc(), men ändå långsammare än den iterativa lösningen där man inte alls använder transform().

På min dator tar din Rust-lösning 1,4s.

Dold text

Jag tror kodblindheten slagit till. (Eller glöggen) Att börja om summeringen för varje "varv" i iteratorn låter väldigt O(N^2). Det är nu man undrar varför man inte ens såg det. Det är inte ens mycket kod.

Tack Yoshman.