GithubHelp home page GithubHelp logo

advent-of-code's People

Contributors

burntsushi avatar schubart avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

advent-of-code's Issues

aoc10: 80x80 canvas?

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?

for _ in 0..1_000_000 {
points.step();
let (w, h) = points.dimensions();
if w <= 80 && h <= 80 {
writeln!(io::stdout(), "seconds: {}", points.seconds)?;
writeln!(io::stdout(), "{}", points.grid_string().trim())?;
writeln!(io::stdout(), "{}", "~".repeat(79))?;
}
}

AOC01: Using std::io::Result instead?

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.

Solution for day 20 seems to be wrong

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.

Day 5: Comparing char and char::ToUppercase

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?

Day 4 / Borrowing in loop

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?

  1. I try to have a HashMap (key: String [guard_id], value: Guard [values I want to store]) that contains my guards data.
  2. In every loop, I get the guard (or insert a new one if I never found it before).
  3. Then I mutate its values.

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 ๐Ÿ‘

Walk through your thought process for day 10

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.

Day 2: returning tuples

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.

Thanks!

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!

Day 8: HashMap.copy is expensive

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:

  1. I copy the HashMap
  2. I add a new metadata to the node vector (mutating)
  3. If all metadatas are present for this node, I process the value
  4. I then need the global HashMap to retrieve children node values (reading)

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

Can you help me understand? - day1 / part2

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

::std?

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.

Why read from stdin ?

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.

Day 5: advancing or 'skipping' an iterator

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

Day12: Slowlyness from usage of string?

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 :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.