burntsushi / advent-of-code Goto Github PK
View Code? Open in Web Editor NEWRust solutions to AoC 2018
License: The Unlicense
Rust solutions to AoC 2018
License: The Unlicense
I was wondering how you determined from the instructions that the points would all fit within an 80x80 canvas when the message was being displayed?
advent-of-code/aoc10/src/main.rs
Lines 29 to 37 in 6285296
Thanks for generously publishing your code, @BurntSushi, it's really helping me learn idiomatic Rust.
In your solution for Day 1, you specify a type alias (is that the right term?) like this:
type Result<T> = ::std::result::Result<T, Box<::std::error::Error>>;
Which seems awfully similar to the Result that gets provided by std::io::Result
, which looks like this:
pub type Result<T> = result::Result<T, Error>;
My question is that why not just do use std::io::Result
instead of rolling your own type? I've used that Result in my own solution, and it seemed to work.
The solutions your code prints for day 20 seem to be wrong. So how did you make it to the second part without solving the first one? ๐
I think there are two major problems with the approach of the solution. For one, the code doesn't seem to build the product set of concatenated alternations, e.g. (N|S)(E|W)
isn't expanded to NE
, NW
, SE
, SW
. And second, the code only follows paths given in the regular expression, while the shortest way to the most distant room might be a different one, e.g. for the regex NESW
, the code would evaluate that the most distant room is three doors a way, while the correct answer is only two doors.
Assuming that I haven't missed anything, it may be helpful for other people looking at the code to add a comment that the solution does not work โ I at least was pretty confused when trying to figure out how the solution works, since I assumed it produces the correct solution.
Ok. This is going to be one of those issues that I'll look back on and be embarrassed about, I can just feel it. But why on earth can't I do this?
fn react (input: &str) -> String {
let polymer: Vec<char> = input.chars().collect();
let mut stable: Vec<char> = vec![];
let mut c = polymer.iter().peekable();
match c.next() {
Some(first) => {
match c.peek() {
Some(second) => {
if first.is_uppercase()
&& second.is_lowercase()
&& *first != second.to_uppercase() {
stable.push(*first);
stable.push(**second);
}
},
None => stable.push(*first),
}
},
None => {},
}
stable.into_iter().collect()
}
Hopefully it's obvious this isn't my final logic ๐ I'm just playing with iterators to try to understand more about them. But because std::char::ToUppercase
doesn't implement PartialEq
for char
, I can't compare the two. What's the correct approach for checking a char
against its uppercase equivalent?
Hi @BurntSushi,
Thank again for your work!
Do you have time to explain why my code is bad, and how I can achieve this in a better way?
HashMap
(key: String [guard_id], value: Guard [values I want to store]) that contains my guards data.But I am stuck with this error and I dunno how I can do this kind of code in a better way
--> src/main.rs:75:29
|
75 | current_guard = guards.entry(guard_id.clone()).or_insert(G
uard {
| ^^^^^^ mutable borrow starts here in previ
ous iteration of loop
...
112 | }
| - mutable borrow ends here
source code (https://github.com/fabienjuif/advent-of-code-2018/blob/borrow/day4/src/main.rs
):
extern crate chrono;
extern crate regex;
use std::fs;
use regex::Regex;
use std::collections::HashMap;
use chrono::prelude::*;
const FILE_NAME: &str = "./input.txt";
struct Record {
guard_id: String,
date_time: DateTime<Utc>,
fulldate: String,
// date: String,
// hour: String,
minute: String,
description: String,
}
struct Guard {
id: String,
minutes: Vec<(i64, i64)>,
most_minute_asleep: (i64, i64),
total_asleep: i64,
}
fn get_guards() {
let content = fs::read_to_string(FILE_NAME).unwrap();
let re = Regex::new(r"\[(\d+-\d+-\d+) (\d+):(\d+)\]( Guard #)?(\d+)? (.*)").unwrap();
let mut records: Vec<Record> = Vec::new();
for line in content.lines() {
match re.captures(line) {
Some(captures) => {
let date = captures.get(1).unwrap().as_str().to_string();
let hour = captures.get(2).unwrap().as_str().to_string();
let minute = captures.get(3).unwrap().as_str().to_string();
let fulldate = format!("{}T{}:{}:00Z", date, hour, minute);
let description = captures.get(6).unwrap().as_str().to_string();
let guard_id = match captures.get(5) {
Some(matcher) => matcher.as_str().to_string(),
None => String::from("")
};
let date_time = fulldate.parse::<DateTime<Utc>>().unwrap();
records.push(Record {
guard_id,
date_time,
fulldate,
// date,
// hour,
minute,
description,
});
},
None => {}
}
}
records.sort_by(|record_a, record_b| record_a.date_time.cmp(&record_b.date_time));
println!("records size: {}", records.len());
let mut guards: HashMap<String, Guard> = HashMap::new();
let mut current_guard: &mut Guard = &mut Guard { id: String::from(""), minutes: Vec::new(), most_minute_asleep: (0, 0), total_asleep: 0 };
let mut last_date_time = Utc::now();
let mut last_minute = String::from("");
let mut minutes: HashMap<i64, i64> = HashMap::new();
let mut sorted_minutes = Vec::new();
for record in records {
let Record { guard_id, date_time, description, minute, .. } = record;
if !guard_id.is_empty() { // empty guard_id means we are changing guard
current_guard = guards.entry(guard_id.clone()).or_insert(Guard {
id: guard_id.clone(),
minutes: Vec::new(),
most_minute_asleep: (0, 0),
total_asleep: 0,
});
for (minute, times) in minutes.iter() {
sorted_minutes.push((minute.clone(), times.clone()));
}
sorted_minutes.sort_by(|minute_a, minute_b| minute_b.1.cmp(&minute_a.1));
current_guard.minutes = sorted_minutes.drain(..).collect();
current_guard.most_minute_asleep = match current_guard.minutes.get(0) {
Some(value) => value.clone(),
None => (0, 0)
};
minutes.clear();
sorted_minutes.clear();
}
if description == "falls asleep" {
last_date_time = date_time.clone();
last_minute = minute.clone();
}
if description == "wakes up" {
current_guard.total_asleep += (date_time - last_date_time).num_minutes() + 1;
let num_minutes = (date_time - last_date_time).num_minutes();
for min in 0..num_minutes {
*minutes.entry(last_minute.parse::<i64>().unwrap() + min).or_insert(0) += 1;
}
}
}
}
fn main() {
get_guards();
}
Thank you ๐
First of all, thank you for taking the time to complete these puzzles; it shows how someone with experience in Rust would approach these problems, given that we all get the same instructions. That being said, I struggle with grasping how one would reach this point, since all I see is the final result.
What's your process to developing a solution? Do you just sit down and write the code like this with an idea already in mind of how to solve it, or does it take a few tries? If you ran into problems with your first attempt, how did you fix those? What gave you the idea only drawing grids smaller than (or equal to) 80 x 80? I also find it interesting that you adjust the bounds as you keep drawing the grid.
Is this code "good enough", or do you feel that you could improve it?
Thanks again for doing this.
Edit: I just saw issue #7.
Hello again! I finished this when I had time, then refactored after reviewing your solution. I saw your comment on Twitter saying to ask questions, so I'm piling on ๐
I managed to make my first effort a little more efficient, but I had questions which I guess are mostly about idiom. The task seems to lend itself to one function returning a tuple (indicating positives for sequences of two chars and sequences of three chars). So I went for this:
use std::collections::HashSet;
pub fn count_dups (id: &str) -> (u32, u32) {
let (mut twos, mut threes) = (false, false);
let mut unique_chars = HashSet::new();
for c in id.chars() {
let unique = unique_chars.insert(c);
if !unique {
let count = id.matches(c).count();
if count == 2 {
twos = true;
}
if count == 3 {
threes = true;
}
}
}
(twos as u32, threes as u32)
}
#[aoc(day2, part1)]
pub fn find_checksum (ids: &str) -> u32 {
let (twos, threes) = ids
.lines()
.fold((0, 0), |counts, id| {
let dups = count_dups(id);
(counts.0 + dups.0, counts.1 + dups.1)
});
twos * threes
}
This clocks in at about 350 ยตs without the file read (yours is 50-ish). TBH I should probably stop playing bench golf and focus on readability, but it's addictive. Anyway, is returning the tuple a reasonably idiomatic approach? And is there a cleaner way of obtaining the totals? The fold as written seems a little untidy.
As others, I've greatly enjoyed being able to compare your solutions to my own as I (slowly) go through AoC 2018. Thank you!
Is there a reason that you seem to prefer writeln!(io::stdout())
to println!
?
No issues, just wanted to say thank you for making this public. It's really helpful to be able to look at a solution from a more idiomatic point of view than I am able to muster. Cheers!
Hi @BurntSushi ๐
I think I struggle with borrow checker and copying my collections.
Right know, everytime I had a "borrow check" hit, I just cloned my collection (eg: my_map.clone()
).
But with day8 this is so expensive!
My programs runs in more than 1sec, where yours runs in < 0.01s! ๐จ
For me, the big problem is with this code:
// ...
let mut nodes = HashMap::<i32, Node>::new();
// ... loop {
let ref_nodes = nodes.clone();
nodes.entry(current_id)
.and_modify(|node| node.add_metadata(value, ref_nodes));
// ... }
That reference this:
#[derive(Debug, Clone)]
struct Node {
id: i32,
parent: Option<i32>,
value: i32,
childs_count: i32,
childs: Vec<i32>,
metadatas_count: i32,
metadatas: Vec<i32>,
}
impl Node {
fn has_metadatas (&self) -> bool {
self.metadatas_count == self.metadatas.len() as i32
}
fn add_metadata (&mut self, metadata: i32, nodes: HashMap<i32, Node>) {
self.metadatas.push(metadata);
if self.has_metadatas() {
self.value = self.metadatas.iter().fold(
0,
|acc, curr| {
if self.childs_count == 0 {
return acc + curr;
}
return acc + match self.childs.get(*curr as usize - 1) {
None => 0,
Some(child_id) => nodes.get(&child_id).unwrap().clone().value,
}
}
);
}
}
}
As you can see:
More over I do this a lot of time (but, there this is my algorithm that is ... shitty, so don't worry about this :) )
Here is my question (finally!): How do you handle that!
I thank I understood what you said here: #6 but I still struggle :(
Tank you!
PR containing my code is here: https://github.com/fabienjuif/advent-of-code-2018/pull/2/files
Source code is here: https://github.com/fabienjuif/advent-of-code-2018/tree/day7/day8
Hi @BurntSushi , thank you for making your code public.
I try to learn Rust from yesterday, and I have a pain to understand why my code doesn't work, it looks a lot like yours, but it wont work :(
I am sorry to asking you that, but can you help me understand what I do wrong?
fn main() {
let content = fs::read_to_string(FILE_NAME)
.expect("Error while reading the input file");
let mut previous = HashSet::new();
let mut count = 0;
previous.insert(count);
for line in content.lines() {
if !line.is_empty() {
let current: i32 = line.parse()
.expect("Error while parsing line");
count += current;
if previous.contains(&count) { // I NEVER PASS THIS CONDITION, WHY? :(
println!("here! {}", count);
break;
}
previous.insert(count);
}
}
println!("{}", count);
}
Since rust no longer requires ::create_name
as of 1.30.0, is it still idiomatic to use it for ::std
like in this repo?
I guess it's still the most unambiguous form? I'm asking as a near-beginner in Rust.
Sorry if this is a dumb question, but why do you read from stdin instead of opening and reading the input file directly within the rust code?
Still, thank you for making your solutions public.
So I was playing with peekable iterators today. I noticed that I wanted to be able to advance an iterator to skip one char inside a while let
, which I suspect isn't possible. Is there an idiomatic equivalent? This just seems ugly:
fn reaction (polymer: &Vec<char>) -> Vec<char> {
let mut stable: Vec<char> = vec![];
let mut c = polymer.iter().peekable();
let mut skip_char = false;
while let Some(first) = c.next() {
if skip_char {
skip_char = false;
continue;
}
match c.peek() {
Some(second) => {
if will_react(first, second) {
skip_char = true;
} else {
stable.push(*first);
}
},
None => stable.push(*first),
}
}
stable
}
https://github.com/richchurcher/advent-of-code-2018/blob/master/src/day5.rs
advent-of-code/aoc03/src/main.rs
Line 26 in d5e3fe3
Hi @BurntSushi
I see you use enums, structs, etc in day12.
I used strings: https://github.com/fabienjuif/advent-of-code-2018/blob/master/day12/src/main.rs
Do you think this is because of string usage that my code is 10x slower than yours?
Or do you think this is because of this? https://github.com/fabienjuif/advent-of-code-2018/blob/master/day12/src/main.rs#L24
(creating a new string each loop, I try other way to construct that string, so I guess this is not HOW this new string is built, but maybe just that the fact I build a new string each loop?)
I don't ask you to debug my code!
I just want to know if you can quickly see an issue there, if you have time!
Thank a lot, once again :)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.