15 votes

Day 2: Password Philosophy

Today's problem description: https://adventofcode.com/2020/day/2


Join the Tildes private leaderboard! You can do that on this page, by entering join code 730956-de85ce0c.

Please post your solutions in your own top-level comment. Here's a template you can copy-paste into your comment to format it nicely, with the code collapsed by default inside an expandable section with syntax highlighting (you can replace python with any of the "short names" listed in this page of supported languages):

<details>
<summary>Part 1</summary>

```python
Your code here.
```

</details>

28 comments

  1. PapaNachos
    (edited )
    Link
    Using Python and I got a chance to use some regular expressions too! Day 2 A For this one I wrote a regular expression and broke up each line into it's component pieces. From there it was a matter...
    • Exemplary

    Using Python and I got a chance to use some regular expressions too!

    Day 2 A For this one I wrote a regular expression and broke up each line into it's component pieces. From there it was a matter of using each of the pieces to check if the password fit.

    Regular expressions are somewhat cryptic, and if you haven't learned how to deal with them, I highly recommend https://regexone.com/
    It does take a bit of work to learn how to use them, but they are excellent at solving certain types of problems

    import re
    test_data = '''1-3 a: abcde
    1-3 b: cdefg
    2-9 c: ccccccccc'''
    
    passwords = test_data.split('\n')
    
    pattern = re.compile(r'(\d+)-(\d+)\s(\w):\s(\w+)')
    
    valid_passwords = 0
    for password in passwords:
        match = pattern.match(password)
        min_val = int(match.group(1))
        max_val = int(match.group(2))
        important_char = match.group(3)
        passphrase = match.group(4)
        count = passphrase.count(important_char)
        if count >= min_val and count <= max_val:
            valid_passwords = valid_passwords + 1
            #print(f"{password} meets the criteria")
        else:
            pass
            #print(f"{password} does not meet the criteria")
    print(f'A total of {valid_passwords} passwords were valid')
    
    Day 2 B For this one my regex solution was robust enough that I didn't have to rework it. Though I did rename some of the variables for added readability. I did need to redo the password validation check though.

    This is basically an XOR logic gate, where it will be true if exactly one of the parameters is TRUE and FALSE if neither or both of the parameters are TRUE. I put a quick one together by starting with 0. Each of the criteria that was TRUE would add +1. If the value at the end was exactly 1, I knew my result was valid. If it was anything other than 1, I knew the result was invalid.

    In the code below I separated out 0 and 2 as results for my own debugging purposes, but really the only check you need to make is if it equals 1.

    import re
    test_data = '''1-3 a: abcde
    1-3 b: cdefg
    2-9 c: ccccccccc'''
    
    passwords = test_data.split('\n')
    
    pattern = re.compile(r'(\d+)-(\d+)\s(\w):\s(\w+)')
    
    valid_passwords = 0
    for password in passwords:
        match = pattern.match(password)
        first_val = int(match.group(1))
        second_val = int(match.group(2))
        important_char = match.group(3)
        passphrase = match.group(4)
        first_char = passphrase[first_val - 1]
        second_char = passphrase[second_val - 1]
        check = 0
        if first_char == important_char:
            check = check + 1
        if second_char == important_char:
            check = check + 1
        if check == 0:
            pass
            #print('Invalid: No characters matched')
        elif check == 1:
            valid_passwords = valid_passwords + 1
            #print('Valid: Exactly 1 character matched')
        else:
            pass
            #print('Invalid: Both characters matched')
    
    
    Tips for Beginners
    • Don't worry about the competition part, just compete against yourself.

    • Don't be afraid to ask for help. But if you ask for help, try to understand what people are telling you, rather than just copying it

    • These will get substantially harder as time goes on. The later ones especially will be a challenge even for experienced programmers. Don't beat yourself up if you can't figure them out

    • Sometimes the hardest part is just getting started. Even getting to the point where you can even start working on the problem takes some effort. Before you even start trying to solve it you need 1)Somewhere to run the code. 2)The problem data in a place you can work with it. I recommend downloading Anaconda and using Jupyter Notebook. Then you can just copy-paste the data into a variable (but you'll need to reformat it or use triple quotes like in my examples

    5 votes
  2. blitz
    Link
    I noticed Day 2 is easily parallelizable so I took a multithreaded approach to the problem:. The trick was to use crossbeam's scope function to create a scope that borrowed lines, so that the...

    I noticed Day 2 is easily parallelizable so I took a multithreaded approach to the problem:. The trick was to use crossbeam's scope function to create a scope that borrowed lines, so that the borrow checker would be satisfied that its lifetime was long enough to be borrowed into the threads instead of moved. I managed to do this with no dynamic allocations except for the initial reading of the file, which I wasn't sure I would be able to do, so I'm really happy about that.

    Day 2
    extern crate crossbeam;
    
    use std::io;
    use std::io::prelude::*;
    
    use crossbeam::channel::{unbounded};
    use crossbeam::thread;
    
    fn find_range(range_str: &str) -> (usize, usize) {
        let mut range_iter = range_str.split('-');
    
        let lower = range_iter.next().unwrap().parse::<usize>().unwrap();
        let upper = range_iter.next().unwrap().parse::<usize>().unwrap();
    
        (lower, upper)
    }
    
    fn part1_test(range: (usize, usize), test_char: char, test_password: &str) -> bool {
        let mut test_char_count = 0;
    
        for c in test_password.chars() {
            if c == test_char {
                test_char_count += 1;
            }
        }
    
        range.0 <= test_char_count && test_char_count <= range.1
    }
    
    fn part2_test(indices: (usize, usize), test_char: char, test_password: &str) -> bool {
        let pw_bytes = test_password.as_bytes();
        let mut num_pos = 0;
    
        if pw_bytes[indices.0 - 1] as char == test_char {
            num_pos += 1;
        }
    
        if pw_bytes[indices.1 - 1] as char == test_char {
            num_pos += 1;
        }
    
        num_pos == 1
    }
    
    fn process_line(line: &str) ->  (bool, bool) {
        let mut line_iter = line.split_whitespace();
        let range = find_range(line_iter.next().unwrap());
        let subject_char = line_iter.next().unwrap().chars().next().unwrap();
        let subject_password = line_iter.next().unwrap();
    
        (
            part1_test(range, subject_char, subject_password),
            part2_test(range, subject_char, subject_password),
        )
    }
    
    fn main() {
        let stdin = io::stdin();
        let num_threads = 2;
    
    
        let lines: Vec<String> = stdin.lock().lines().collect::<Result<_, _>>().unwrap();
        let mut p1_matches = 0;
        let mut p2_matches = 0;
    
        let (line_send, line_recv) = unbounded();
        let (result_send, result_recv) = unbounded();
    
        thread::scope(|s| {
            for l in lines.iter() {
                line_send.send(l).unwrap();
            }
    
            drop(line_send);
    
            for _ in 0..num_threads {
                let r_clone = line_recv.clone();
                let s_clone = result_send.clone();
    
                s.spawn(move |_| {
                    let mut p1_valid_count = 0;
                    let mut p2_valid_count = 0;
    
                    while let Ok(line) = r_clone.recv() {
                        let (p1_valid, p2_valid) = process_line(line);
    
                        if p1_valid {
                            p1_valid_count += 1;
                        }
                        if p2_valid {
                            p2_valid_count += 1;
                        }
                    }
    
                    s_clone.send((p1_valid_count, p2_valid_count)).unwrap();
                    drop(s_clone);
                });
            }
    
        }).unwrap();
    
        drop(line_recv);
        drop(result_send);
    
        while let Ok((p1_valid_count, p2_valid_count)) = result_recv.recv() {
            p1_matches += p1_valid_count;
            p2_matches += p2_valid_count;
        }
    
        println!("Part1: {}", p1_matches);
        println!("Part2: {}", p2_matches);
    }
    
    5 votes
  3. wycy
    (edited )
    Link
    Solutions in Rust: Solution use std::env; use std::io::{self, prelude::*, BufReader}; use std::fs::File; extern crate regex; use regex::Regex; #[derive(Clone, Hash)] struct PasswordReqPair { pub...

    Solutions in Rust:

    Solution
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    
    extern crate regex;
    use regex::Regex;
    
    #[derive(Clone, Hash)]
    struct PasswordReqPair {
        pub char_min: usize,
        pub char_max: usize,
        pub letter: char,
        pub password: String,
    }
    impl PasswordReqPair {
    
        pub fn from_string(s: &str) -> PasswordReqPair {
            // 3-12 x: pxxxxxqkxhdqk
            let re = Regex::new(r"(\d+)-(\d+) (\w): (\w+)").unwrap();
            let matches = re.captures(s).unwrap();
            PasswordReqPair {
                char_min: matches[1].parse().unwrap(),
                char_max: matches[2].parse().unwrap(),
                letter:   matches[3].parse().unwrap(),
                password: matches[4].parse().unwrap(),
            }
        }
        pub fn is_valid_part1(&self) -> bool {
            let count = self.password.chars().filter(|x| x == &self.letter).count();
            count >= self.char_min && count <= self.char_max
        }
        pub fn is_valid_part2(&self) -> bool {
            let chars = self.password.chars().collect::<Vec<char>>();
            let first = chars[self.char_min-1] == self.letter;
            let second = chars[self.char_max-1] == self.letter;
            first ^ second // xor: (first && !second) || (!first && second)
        }
    }
    
    fn day02(input: &str) -> io::Result<()> {
        let file = File::open(input).expect("Input file not found.");
        let reader = BufReader::new(file);
    
        let input: Vec<String> = match reader.lines().collect() {
            Err(err) => panic!("Unknown error reading input: {}", err),
            Ok(result) => result,
        };
    
        let pass_data = input
            .iter()
            .map(|x| PasswordReqPair::from_string(x))
            .collect::<Vec<PasswordReqPair>>();
    
        // Part 1
        let part1 = pass_data.iter().filter(|x| x.is_valid_part1()).count();
        println!("Part 1: {}", part1); // 469
    
        // Part 2
        let part2 = pass_data.iter().filter(|x| x.is_valid_part2()).count();
        println!("Part 2: {}", part2); // 267
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        day02(&filename).unwrap();
    }
    
    5 votes
  4. spit-evil-olive-tips
    (edited )
    Link
    Playing around with using a regex to parse the input lines and a shiny new Python dataclass for the parsed representation. Part 1 from dataclasses import dataclass import re INPUT_REGEX =...

    Playing around with using a regex to parse the input lines and a shiny new Python dataclass for the parsed representation.

    Part 1
    from dataclasses import dataclass
    import re
    
    INPUT_REGEX = re.compile("([\d]+)-([\d]+) ([\w]): (\w+)")
    
    @dataclass
    class PasswordPolicy:
        minimum: int
        maximum: int
        character: str
        password: str
    
    
    def read_policies():
        policies = []
        with open('002.txt') as input_file:
            for line in input_file:
                match = INPUT_REGEX.search(line)
                if match:
                    minimum, maximum, character, password = match.groups()
                    policy = PasswordPolicy(int(minimum), int(maximum), character, password)
                    policies.append(policy)
                else:
                    print(f'failed line: {line}')
        return policies
    
    
    def main():
        policies = read_policies()
    
        successful_count = 0
        for policy in policies:
            count = policy.password.count(policy.character)
            success = (policy.minimum <= count <= policy.maximum)
    
            if success:
                print(f'success: {policy}')
                successful_count += 1
            else:
                print(f'failure: {policy}')
    
        print(successful_count)
    
    
    if __name__ == '__main__':
        main()
    

    Part 2 had only a small change to how I calculate success, and renaming some fields in the dataclass.

    Part 2
    --- 002-1.py    2020-12-02 16:58:52.711679879 -0800
    +++ 002-2.py    2020-12-02 16:58:44.842650228 -0800
    @@ -5,8 +5,8 @@
     
     @dataclass
     class PasswordPolicy:
    -    minimum: int
    -    maximum: int
    +    first: int
    +    second: int
         character: str
         password: str
     
    @@ -30,8 +30,9 @@
     
         successful_count = 0
         for policy in policies:
    -        count = policy.password.count(policy.character)
    -        success = (policy.minimum <= count <= policy.maximum)
    +        first_match = policy.password[policy.first - 1] == policy.character
    +        second_match = policy.password[policy.second - 1] == policy.character
    +        success = first_match != second_match
     
             if success:
                 print(f'success: {policy}')
    
    4 votes
  5. Crestwave
    Link
    Took me a second to remember how to do this in pure AWK, but I got there. Part 1 #!/usr/bin/awk -f { split($0, arr, /( |: |-)/) i = gsub(arr[3], arr[3], arr[4]) if (i >= arr[1] && i <= arr[2]) j...

    Took me a second to remember how to do this in pure AWK, but I got there.

    Part 1
    #!/usr/bin/awk -f
    {
            split($0, arr, /( |: |-)/)
            i = gsub(arr[3], arr[3], arr[4])
            if (i >= arr[1] && i <= arr[2])
                    j += 1
    }
    
    END { print j }
    
    Part 2
    #!/usr/bin/awk -f
    {
            j = 0
            split($0, arr, /( |: |-)/)
            for (i = 1; i <= 2; ++i)
                    if (substr(arr[4], arr[i], 1) == arr[3])
                            j += 1
            if (j == 1)
                    k += 1
    }
    
    END { print k }
    
    4 votes
  6. Gyrfalcon
    (edited )
    Link
    Stealing great formatting ideas from @Bauke and @spit-evil-olive-tips to present my solution. Language: Julia Repository Part 1 function main() lines = [] # Initialize array and read in input...

    Stealing great formatting ideas from @Bauke and @spit-evil-olive-tips to present my solution.

    Part 1
    function main()
    
        lines = []
        # Initialize array and read in input
        open("Day2/input.txt") do fp
            lines = readlines(fp)
        end
    
        # Split out everything appropriately
        lines = map(x -> split(x, ' '), lines)
        ranges = map(x -> split(x[1], '-'), lines)
        ranges = map(x -> [parse(Int, x[1]), parse(Int, x[2])], ranges)
        letters = map(x -> x[2][1], lines)
        passwords = map(x -> x[3], lines)
        # Define function for checking passwords
        function check_pw1(range, letter, pw)
            matches = count(x -> (x == letter), pw)
            return range[1] <= matches <= range[2]
        end
        # Run the check and display result
        result1 = count(map(check_pw1, ranges, letters, passwords))
        println("Part 1 result is ", result1)
    
    end
    
    main()
    
    Part 2 diff
    --- a/Day2/main.jl
    +++ b/Day2/main.jl
    @@ -21,6 +21,14 @@ function main()
         result1 = count(map(check_pw1, ranges, letters, passwords))
         println("Part 1 result is ", result1)
     
    +    # Define new function for checking passwords
    +    function check_pw2(positions, letter, pw)
    +        return xor(pw[positions[1]] == letter, pw[positions[2]] == letter)
    +    end
    +
    +    # Run the check and display result
    +    result2 = count(map(check_pw2, ranges, letters, passwords))
    +    println("Part 2 result is ", result2)
    +
     end
     
     main()
    
    4 votes
  7. soks_n_sandals
    (edited )
    Link
    Perl solution for part 1 and part 2! Part 1 & 2 I edited my code slightly from part 1 to create an array from the password string and store each character in the array, then check that the allowed...

    Perl solution for part 1 and part 2!

    Part 1 & 2 I edited my code slightly from part 1 to create an array from the password string and store each character in the array, then check that the allowed letter was in only one of those positions.
    ##########################
    #        PART ONE        #
    ##########################
    use strict;
    use warnings;
    use feature 'say';
    
    open my $input_list, '<', "day2_inp";
    my @pass_strings = <$input_list>;
    close $input_list;
    
    my $input_length=scalar(@pass_strings);
    my $log = 0;                #create a counter
    foreach (1 .. $input_length) {
    
      my $pass_string = $pass_strings[$_-1];
      my @words = split / /, $pass_string;  #split password string by spaces
      my $limits = $words[0];     #find the lower/upper limits of allowed characters
      my $target = $words[1];     #get the target character
      my $password = $words[2];   #get password
      my @limits = split /-/, $limits;  #get an array with the lower/upper limit
      my $lower_lim = $limits[0];
      my $upper_lim = $limits[1];
    
      $target =~ s/://g;          #strip trailing : from target character
      #my $check = qr/$target\{$lower_lim,$upper_lim}/;
      my $check = () = $password =~ m/$target/g; #count how many times the target letter is in the password
    
    #verify that the target letter is used within the requirement
      if ($check >= $lower_lim && $check <= $upper_lim) {
        #say "match!";
        $log=$log+1;
      }
      else {
        #say "no match!";
      }
    
    }
    
    say $log;
    
    ##########################
    #        PART TWO        #
    ##########################
    use strict;
    use warnings;
    use feature 'say';
    
    open my $input_list, '<', "day2_inp";
    #chomp(my @mass = <$input_list>);
    my @pass_strings = <$input_list>;
    close $input_list;
    
    my $input_length=scalar(@pass_strings);
    my $log = 0;                #create a counter
    foreach (1 .. $input_length) {
    
      my $pass_string = $pass_strings[$_-1];
      my @words = split / /, $pass_string;  #split password string by spaces
      my $limits = $words[0];     #find the lower/upper limits of allowed characters
      my $target = $words[1];     #get the target character
      my $password = $words[2];   #get password
      my @limits = split /-/, $limits;  #get an array with the lower/upper limit
      my $lower_lim = $limits[0];
      my $upper_lim = $limits[1];
    
      my @password2 = split //, $password; # create an array of the password letter
      $target =~ s/://g;          #strip trailing : from target character
      #my $check = qr/$target\{$lower_lim,$upper_lim}/;
      #my $check = () = $password =~ m/$target/g;
    
    #say "lower_lim=$lower_lim and upper_lim=$upper_lim";
    #say "target is $target, options are $password2[$lower_lim-1] and $password2[$upper_lim-1]";
    
      if ($target eq $password2[$lower_lim-1] && $target ne $password2[$upper_lim-1]) {
          #say "match!";
          $log=$log+1;
        }
      if ($target ne $password2[$lower_lim-1] && $target eq $password2[$upper_lim-1]) {
          #say "match!";
          $log=$log+1;
        }
    
    }
    
    say $log;
    
    3 votes
  8. [3]
    unknown user
    Link
    My Day 2 Solutions... Part 1 If I were smart, I would have used Regex to parse the inputs... But just messing with .split() kinda worked out fine... #!/usr/bin/python3 with open("passwords","r")...

    My Day 2 Solutions...

    Part 1 If I were smart, I would have used Regex to parse the inputs... But just messing with .split() kinda worked out fine...
    #!/usr/bin/python3
    
    with open("passwords","r") as file:
        lines = file.readlines()
        lines = [line.replace("\n","") for line in lines]
    
    # format everything nicely... min, max, character to look for, password
    for i in range(len(lines)):
        lines[i] = lines[i].split(":")
    
        tempMin,temp = lines[i][0].split("-")
    
        tempMax,char = temp.split(" ")
    
        lines[i] = [int(tempMin),int(tempMax),char,lines[i][1][1:]]
    
    invalidCount = 0
    for password in lines:
        count = 0
        for char in password[3]:
            if char == password[2]:
                count += 1
        if count >= password[0] and count <= password[1]:
            invalidCount += 1
    
    print(invalidCount)
    
    Part 2 The first bit of the code that parses out the values is the same... I guess I could consolidate it all into one script with a parse() function or something if I wanted. I guess the Min and Max variable names don't quite make sense anymore... But that's fine!

    Formatting sidenote... no matter how many new lines I put here... They don't seem to render out my separate paragraphs separately?

    Also, I have this terrible line... if (var1 == True and var2 == False) or (var1 == False and var2 == True):
    It's my equivalent of "or but not and"... Does Python have such an operator?

    #!/usr/bin/python3
    
    with open("passwords","r") as file:
        lines = file.readlines()
        lines = [line.replace("\n","") for line in lines]
    
    # format everything nicely... valid, invalid , character to look for, password
    for i in range(len(lines)):
        lines[i] = lines[i].split(":")
    
        tempMin,temp = lines[i][0].split("-")
    
        tempMax,char = temp.split(" ")
    
        lines[i] = [int(tempMin),int(tempMax),char,lines[i][1][1:]]
    
    validCount = 0
    for password in lines:
    
        var1 = password[3][password[0] - 1] == password[2]
        var2 = password[3][password[1] - 1] == password[2]
    
        # lol - is there like an "or but not and" operator?
        if (var1 == True and var2 == False) or (var1 == False and var2 == True):
            validCount += 1
    
    print(validCount)
    

    Any feedback is welcome :)

    3 votes
    1. Crestwave
      Link Parent
      Yep! This is called exclusive or (XOR) and you can use it with the ^ operator.

      It's my equivalent of "or but not and"... Does Python have such an operator?

      Yep! This is called exclusive or (XOR) and you can use it with the ^ operator.

      3 votes
    2. spit-evil-olive-tips
      Link Parent
      A bunch of minor details: Doesn't matter for this script, but a more portable way of doing this is #!/usr/bin/env python3, because that works no matter where the system Python is installed, and it...

      A bunch of minor details:

      #!/usr/bin/python3

      Doesn't matter for this script, but a more portable way of doing this is #!/usr/bin/env python3, because that works no matter where the system Python is installed, and it also does the right thing if run inside a virtualenv.

      with open("passwords","r") as file:

      Reading is the default, so you can leave off , "r". It only needs to be specified if you're writing, or reading in binary mode (rb).

      lines = [line.replace("\n","") for line in lines]

      line.strip() is a bit more idiomatic. It removes leading and trailing whitespace, whereas your .replace() would remove all embedded newlines in the string, though since we've just read it using readlines(), we know there's only the trailing \n. So they end up working the same, but .strip() makes the intention a bit more clear. It will also handle Windows newlines (\r\n) correctly.

      for i in range(len(lines)):

      More idiomatic in Python is for line in lines, or if you need the index for other purposes, for i, line in enumerate(lines).

      Then all your lines[i] references within the loop can be replaced with just line (with a caveat that the last line of your loop currently mutates lines in-place, so you'd want to build up a second list as you go, or make the valid/invalid password decision on-the-fly without saving the line at all)

      I think using .split() is a completely valid way to do it, since we know the input is well-formed. One thing I'd do differently is to split first on the most common separator (space) rather than on : as you're doing:

      >>> line = '1-2 a: abcdef'
      >>> min_max, char, password = line.split(' ')
      >>> min, max = min_max.split('-')
      >>> char = char.rstrip(':')
      >>> (min, max, char, password)
      ('1', '2', 'a', 'abcdef')
      

      for password in lines:

      A useful thing you can do here is tuple unpacking, so for example in the body of the loop you refer to password[3], password[2] etc. Instead you can do for (min, max, char, password) in lines: (the parens are optional) and then in the body of the loop you've got those 4 variables available. Equivalently, you can also unpack the tuple in the body of the loop:

      for line in lines:
        min, max, char, password = line
        ...
      

      There's also a built-in .count() function you can use instead of looping char-by-char yourself:

      >>> x = 'acab' * 1000
      >>> x.count('a')
      2000
      >>> x.count('d')
      0
      
      3 votes
  9. [2]
    ali
    Link
    Python Solutions by using count. I love that this is possible in python: a <= b <= c Part 1 def part_1(input): invalid = 0 for line in input: rule,text = line.split(":") text = text.strip() count,...

    Python Solutions by using count.
    I love that this is possible in python:

    a <= b <= c

    Part 1
    def part_1(input):
        invalid = 0
        for line in input:
            rule,text = line.split(":")
            text = text.strip()
            count, character = rule.split(" ")
            lower_bound,upper_bound = count.split('-')
            if int(lower_bound) <= text.count(character) <= int(upper_bound):
                invalid+=1
        return invalid
    
    Part 2
    def part_2(input):
        invalid = 0
        for line in input:
            rule, text = line.split(":")
            text = text.strip()
            count, character = rule.split(" ")
            first_position, second_position = count.split('-')
            first = text[int(first_position)-1] == character
            second = text[int(second_position)-1] == character
            if first and not second or not first and second:
                invalid += 1
        return invalid
    
    3 votes
    1. nothis
      Link Parent
      Wait, what? That's pretty cool!

      I love that this is possible in python:

      a <= b <= c

      Wait, what? That's pretty cool!

      3 votes
  10. tomf
    Link
    I'm using Google Sheets! Part 1 =ARRAYFORMULA( COUNTA( IF(ISBLANK(A1:A),, IF(--REGEXEXTRACT(A1:A,"([0-9]+)-")<= LEN(REGEXEXTRACT(A1:A,": (.*)"))- LEN(REGEXREPLACE(REGEXEXTRACT(A1:A,": (.*)"),...

    I'm using Google Sheets!

    Part 1
    =ARRAYFORMULA(
      COUNTA(
       IF(ISBLANK(A1:A),,
        IF(--REGEXEXTRACT(A1:A,"([0-9]+)-")<=
         LEN(REGEXEXTRACT(A1:A,": (.*)"))-
          LEN(REGEXREPLACE(REGEXEXTRACT(A1:A,": (.*)"),
         REGEXEXTRACT(A1:A,"([a-z]):"),"")),
         IF(--REGEXEXTRACT(A1:A,"-([0-9]+)")>=
          LEN(REGEXEXTRACT(A1:A,": (.*)"))-
           LEN(REGEXREPLACE(REGEXEXTRACT(A1:A,": (.*)"),
          REGEXEXTRACT(A1:A,"([a-z]):"),"")),
         TRUE,),))))
    
    Part 2
    =ARRAYFORMULA(
       SUM(
        IF(
         REGEXEXTRACT(
          A1:A,
          "([a-z]):")=
         REGEXEXTRACT(
          REGEXEXTRACT(
           A1:A,
           ": ([a-z]+)"),
          REPT(
           ".",
           REGEXEXTRACT(
            A1:A,
            "([0-9]+)-")-1)&"([a-z])"),
         IF(REGEXEXTRACT(A1:A,"([a-z]):")=
          REGEXEXTRACT(
          REGEXEXTRACT(
           A1:A,
           ": ([a-z]+)"),
           REPT(
            ".",
            REGEXEXTRACT(
             A1:A,
             "-([0-9]+)")-1)&"([a-z])"),
         0,
         1),
          IF(REGEXEXTRACT(A1:A,"([a-z]):")=
           REGEXEXTRACT(
            REGEXEXTRACT(
            A1:A,
            ": ([a-z]+)"),
            REPT(
             ".",
             REGEXEXTRACT(
             A1:A,
             "-([0-9]+)")-1)&"([a-z])"),
           1,
           0))))
    
    3 votes
  11. 3d12
    Link
    I am so happy I was able to successfully use regex on this one! I started parsing based on character indexes but that got painful quick. Luckily, the bit of regex practice I've been getting at...

    I am so happy I was able to successfully use regex on this one! I started parsing based on character indexes but that got painful quick. Luckily, the bit of regex practice I've been getting at work paid off in spades! Repo is here

    Part 1
    const fs = require('fs');
    const readline = require('readline');
    
    let pwArray = []
    
    async function openFileForReading(file) {
    	const fileStream = fs.createReadStream(file);
    
    	const rl = readline.createInterface({
    		input: fileStream,
    		crlfDelay: Infinity
    	});
    
    	for await (const line of rl) {
    		pwArray.push(line);
    	}
    }
    
    function parseLine(line) {
    	const regex = /^(\d+)-(\d+) (\w+): (\w+)/;
    	const found = line.match(regex);
    	return {
    		min: found[1],
    		max: found[2],
    		letter: found[3],
    		password: found[4]
    	}
    }
    
    function passwordValidator(pwInfo) {
    	let charCount = 0;
    	for (let i = 0; i<pwInfo.password.length; i++) {
    		if (pwInfo.password.charAt(i) == pwInfo.letter) {
    			charCount++;
    		}
    	}
    	if (charCount >= pwInfo.min && charCount <= pwInfo.max) {
    		return true;
    	} else {
    		return false;
    	}
    }
    
    (async function mainExecution() {
    	await openFileForReading('input.txt');
    	let validPasswords = 0;
    	for (const line of pwArray) {
    		if (passwordValidator(parseLine(line))) validPasswords++;
    	}
    	console.log("Valid passwords: " + validPasswords);
    })();
    

    And it may be a minor victory to the more experienced developers here, but I was super happy I only had to change one function from part 1 to part 2. This was one instance where the compartmentalization used in part 1 ended up saving me tons of time in part 2.

    Part 2
    const fs = require('fs');
    const readline = require('readline');
    
    let pwArray = []
    
    async function openFileForReading(file) {
    	const fileStream = fs.createReadStream(file);
    
    	const rl = readline.createInterface({
    		input: fileStream,
    		crlfDelay: Infinity
    	});
    
    	for await (const line of rl) {
    		pwArray.push(line);
    	}
    }
    
    function parseLine(line) {
    	const regex = /^(\d+)-(\d+) (\w+): (\w+)/;
    	const found = line.match(regex);
    	return {
    		min: found[1],
    		max: found[2],
    		letter: found[3],
    		password: found[4]
    	}
    }
    
    function passwordValidator(pwInfo) {
    	let pos1check = (pwInfo.password.charAt(pwInfo.min - 1) == pwInfo.letter);
    	let pos2check = (pwInfo.password.charAt(pwInfo.max - 1) == pwInfo.letter);
    	return (pos1check ? !pos2check : pos2check);
    }
    
    (async function mainExecution() {
    	await openFileForReading('input.txt');
    	let validPasswords = 0;
    	for (const line of pwArray) {
    		let parsed = parseLine(line);
    		console.log(parsed);
    		console.log(passwordValidator(parsed));
    		if (passwordValidator(parseLine(line))) validPasswords++;
    	}
    	console.log("Valid passwords: " + validPasswords);
    })();
    
    3 votes
  12. Lynx
    Link
    After finishing a fairly boring python implementation, I decided to implement this as a digital electronic circuit, described in VHDL. The problem is perfect for this, since solving it needs only...

    After finishing a fairly boring python implementation, I decided to implement this as a digital electronic circuit, described in VHDL. The problem is perfect for this, since solving it needs only a small, fixed amount of state.

    The design consists of three main parts:

    • A parser that extracts the fields from the policy header, converts the ASCII-encoded decimal numbers to binary, and marks the start of actual password data for the verifier:
    parser.vhd
    library ieee;
    use ieee.std_logic_1164.all;
    
    entity parser is
    	port (
    		clk : in std_logic;
    		reset : in std_logic;
    		is_record : in std_logic;
    		is_data   : out std_logic;
    
    		char : in character;
    
    		num1, num2 : out natural range 0 to 99;
    		letter : out character
    	);
    end entity;
    
    architecture behaviour of parser is
    	type state_t is (S_NUM1, S_NUM2, S_LETTER, S_COLON, S_END_SPACE, S_DATA);
    	signal state : state_t := S_NUM1;
    
    	subtype digit is natural range 0 to 9;
    	type multiples_lookup_t is array(digit) of natural range 0 to 90;
    	constant TEN_MULTIPLES : multiples_lookup_t := (0, 10, 20, 30, 40, 50, 60, 70, 80, 90);
    
    	-- most significant digit of number
    	signal prev_digit : digit := 0;
    	signal current_digit : digit;
    	signal complete_num : natural range 0 to 99;
    
    	function char_to_digit(input : in character) return digit is
    	begin
    		if not (input < '0') and input <= '9' then
    			return character'pos(input) - character'pos('0');
    		else
    			return 0;
    		end if;
    	end function;
    begin
    	current_digit <= char_to_digit(char);
    	complete_num <= TEN_MULTIPLES(prev_digit) + current_digit;
    
    	process(clk)
    	begin
    		if rising_edge(clk) then
    			if reset then
    				prev_digit <= 0;
    				state <= S_NUM1;
    			else
    				prev_digit <= 0;
    
    				case state is
    					when S_NUM1 =>
    						if is_record then
    							if char = '-' then
    								state <= S_NUM2;
    							else
    								num1 <= complete_num;
    								prev_digit <= current_digit;
    							end if;
    						end if;
    					when S_NUM2 =>
    						if char = ' ' then
    							state <= S_LETTER;
    						else
    							num2 <= complete_num;
    							prev_digit <= current_digit;
    						end if;
    					when S_LETTER =>
    						letter <= char;
    						state <= S_COLON;
    					when S_COLON =>
    						state <= S_END_SPACE;
    					when S_END_SPACE =>
    						state <= S_DATA;
    					when S_DATA =>
    						if not is_record then
    							state <= S_NUM1;
    						end if;
    				end case;
    			end if;
    		end if;
    	end process;
    
    	is_data <= '1' when state = S_DATA else '0';
    end architecture;
    
    • A verifier that takes the policy information and a password stream and evaluates whether the password matches the policy (with two implementations, one for each part)
    verifier.vhd
    library ieee;
    use ieee.std_logic_1164.all;
    
    entity verifier is
    	port (
    		clk : in std_logic;
    		reset : in std_logic;
    		is_data : in std_logic;
    
    		num1, num2 : in natural range 0 to 99;
    		letter : in character;
    
    		char : in character;
    
    		verified : out std_logic
    	);
    end entity;
    
    architecture step1 of verifier is
    	signal count : natural range 0 to 99;
    begin
    	process(clk)
    	begin
    		if rising_edge(clk) then
    			if reset then
    				count <= 0;
    			elsif is_data then
    				if char = letter then
    					count <= count + 1;
    				end if;
    			end if;
    		end if;
    	end process;
    
    	verified <= '1' when num1 <= count and count <= num2 else '0';
    end architecture;
    
    architecture step2 of verifier is
    	signal count : natural range 1 to 99;
    	signal parity : std_logic;
    begin
    	process(clk)
    	begin
    		if rising_edge(clk) then
    			if reset then
    				count <= 1;
    				parity <= '0';
    			elsif is_data then
    				count <= count + 1;
    				if (count = num1 or count = num2) and char = letter then
    					parity <= not parity;
    				end if;
    			end if;
    		end if;
    	end process;
    
    	verified <= parity;
    end architecture;
    
    • A top entity that ties the previous parts together and counts the amount of successfully verified passwords
    top.vhd
    library ieee;
    use ieee.std_logic_1164.all,
        ieee.numeric_std.all;
    
    entity top is
    	generic (
    		COUNTER_WIDTH : positive;
    		STEP : natural range 1 to 2
    	);
    	port (
    		clk : in std_logic;
    		reset : in std_logic;
    		char  : in character;
    		is_record : in std_logic;
    		num_verified : out unsigned(COUNTER_WIDTH-1 downto 0)
    	);
    end entity;
    
    architecture behaviour of top is
    	signal is_data : std_logic;
    	signal num1, num2 : natural range 0 to 99;
    	signal letter : character;
    
    	signal prev_is_record : std_logic;
    	signal record_ended : std_logic;
    
    	signal verified : std_logic;
    begin
    	record_ended <= prev_is_record and not is_record;
    
    	parser_inst: entity work.parser
    		port map (
    			clk   => clk,
    			reset => reset,
    			is_record => is_record,
    			is_data   => is_data,
    			char   => char,
    
    			num1   => num1,
    			num2   => num2,
    			letter => letter
    		);
    
    	generate_verifier: if step = 1 generate
    		verifier_inst: entity work.verifier(step1)
    			port map (
    				clk => clk,
    				reset => reset or record_ended,
    				is_data => is_data,
    				num1   => num1,
    				num2   => num2,
    				letter => letter,
    				char   => char,
    				verified => verified
    			);
    	elsif step = 2 generate
    		verifier_inst: entity work.verifier(step2)
    			port map (
    				clk => clk,
    				reset => reset or record_ended,
    				is_data => is_data,
    				num1   => num1,
    				num2   => num2,
    				letter => letter,
    				char   => char,
    				verified => verified
    			);
    	else generate
    		assert false report "Bad value for ""step""" severity failure;
    	end generate;
    
    	process(clk)
    	begin
    		if rising_edge(clk) then
    			prev_is_record <= is_record;
    			if reset then
    				prev_is_record <= '0';
    				num_verified <= (others => '0');
    			elsif record_ended and verified then
    				num_verified <= num_verified + 1;
    			end if;
    		end if;
    	end process;
    end architecture;
    

    To actually use the design without having to set it up in hardware and toggling a bunch of switches for hours, there's also a simulation testbench that feeds a file into the circuit character-by-character, adds some framing information, and prints out the final number of verified passwords. VHDL isn't exactly an application programming language, so all the non-synthesizable constructs are a bit weird and quite Ada-y, but it's not actually all that bad.

    sim.vhd
    library ieee;
    use ieee.std_logic_1164.all,
        ieee.numeric_std.all;
    
    use std.textio.all;
    
    entity sim is
    	generic (
    		FILENAME : string := "input.txt";
    		COUNTER_WIDTH : positive := 12;
    		STEP : natural range 1 to 2
    	);
    end entity;
    
    architecture a of sim is
    	file file_handle : text open read_mode is FILENAME;
    	signal char_in : character;
    	signal clk, reset, is_record : std_logic;
    	signal num_verified : unsigned(COUNTER_WIDTH-1 downto 0);
    
    	procedure print(s: string) is
    		variable l : line;
    	begin
    		write(l, s);
    		writeline(output, l);
    	end procedure;
    begin
    	process
    		variable current_line : line;
    		variable current_char : character;
    		variable good : boolean;
    
    		procedure cycle_clock is
    		begin
    			wait for 10 ns;
    			clk <= '0';
    			wait for 10 ns;
    			clk <= '1';
    			wait for 0 ns;
    		end procedure;
    	begin
    		clk <= '0';
    		is_record <= '0';
    		char_in <= NUL;
    
    		reset <= '1';
    		cycle_clock;
    		reset <= '0';
    		cycle_clock;
    
    		lines_loop: loop
    			exit lines_loop when endfile(file_handle);
    			readline(file_handle, current_line);
    
    			is_record <= '1';
    
    			chars_loop: loop
    				read(current_line, current_char, good);
    				exit chars_loop when not good;
    
    				char_in <= current_char;
    				cycle_clock;
    			end loop;
    
    			is_record <= '0';
    			cycle_clock;
    		end loop;
    
    		cycle_clock;
    
    		print(to_string(to_integer(num_verified)));
    
    		wait;
    	end process;
    
    	top: entity work.top
    		generic map (
    			COUNTER_WIDTH => COUNTER_WIDTH,
    			STEP => STEP
    		)
    		port map (
    			clk => clk,
    			reset => reset,
    			char => char_in,
    			is_record => is_record,
    			num_verified => num_verified
    		);
    end architecture;
    
    3 votes
  13. jwong
    Link
    Java again. Got tripped up a little on part 2, when I modified my part 1 solution. Part 1 - 5.44 ms import java.io.BufferedReader; import java.io.FileReader; import java.io.File; import...

    Java again. Got tripped up a little on part 2, when I modified my part 1 solution.

    Part 1 - 5.44 ms
    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.HashMap;
    
    public class PasswordPhilosophy {
    
      public static void main(String[] args){
        long startTime = System.nanoTime();
    
        // get input
        BufferedReader reader;
        try {
          String filePath = new File("").getAbsolutePath();
          filePath = filePath.concat("/input.txt");
          reader = new BufferedReader(new FileReader(filePath));
          String line = reader.readLine();
    
          int validPasswords = 0;
    
          while (line != null) {
            int charMin = Integer.parseInt(line.substring(0, line.indexOf("-")));
            int charMax = Integer.parseInt(line.substring(line.indexOf("-")+1, line.indexOf(" ")));
            char requiredChar = line.charAt(line.indexOf(" ")+1);
            int charCount = 0;
    
            for (int i=line.indexOf(":")+2; i<line.length(); ++i) {
              if ( line.charAt(i) == requiredChar ) {
                charCount++;
              }
            }
            if ( charCount >= charMin && charCount <= charMax ) {
              validPasswords++;
            }
            line = reader.readLine();
          }
          reader.close();
          
          long endTime   = System.nanoTime();
          long totalTime = endTime - startTime;
    
          System.out.println("Valid Passwords: " + validPasswords);
          System.out.println("Total Time: " + totalTime + " nanoseconds");
        } catch (IOException e) {
          e.printStackTrace();
        }
    
      }
    }
    
    Part 2 - 34.37ms
    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.HashMap;
    
    public class PasswordPhilosophyPt2 {
    
      public static void main(String[] args){
        long startTime = System.nanoTime();
    
        // get input
        BufferedReader reader;
        try {
          String filePath = new File("").getAbsolutePath();
          filePath = filePath.concat("/input.txt");
          reader = new BufferedReader(new FileReader(filePath));
          String line = reader.readLine();
    
          int validPasswords = 0;
    
          while (line != null) {
            int posOne = Integer.parseInt(line.substring(0, line.indexOf("-")));
            int posTwo = Integer.parseInt(line.substring(line.indexOf("-")+1, line.indexOf(" ")));
            char requiredChar = line.charAt(line.indexOf(" ")+1);
            int charCount = 0;
    
            if ( line.charAt(line.indexOf(":") + posOne + 1) == requiredChar && line.charAt(line.indexOf(":") + posTwo + 1) != requiredChar ) {
               validPasswords++; 
               System.out.println("here");
            } else if ( line.charAt(line.indexOf(":") + posOne+1) != requiredChar && line.charAt(line.indexOf(":") + posTwo+1) == requiredChar ) {
               validPasswords++; 
               System.out.println("here1");
            } else {
               System.out.println("here2");
            }
            //System.out.println("pos1:" + posOne + " char: " + line.charAt(posOne-1) + " required char: " + requiredChar);
            //System.out.println("pos2:" + posTwo);
            line = reader.readLine();
          }
          reader.close();
          
          long endTime   = System.nanoTime();
          long totalTime = endTime - startTime;
    
          System.out.println("Valid Passwords: " + validPasswords);
          System.out.println("Total Time: " + totalTime + " nanoseconds");
        } catch (IOException e) {
          e.printStackTrace();
        }
    
      }
    }
    
    3 votes
  14. clone1
    Link
    MIT scheme solution Part 1 & 2 (define (get-lines parse-line) (with-input-from-file "input" (lambda () (let loop ((line (read-line)) (lines '())) (if (eof-object? line) (reverse lines) (loop...

    MIT scheme solution

    Part 1 & 2
    (define (get-lines parse-line)
      (with-input-from-file "input"
        (lambda ()
          (let loop ((line (read-line))
                     (lines '()))
            (if (eof-object? line)
                (reverse lines)
                (loop (read-line)
                      (cons (parse-line line) lines)))))))
    
    (define (make-pass-line start end char pass)
      (list start end char pass))
    (define (get-start x)
      (car x))
    (define (get-end x)
      (cadr x))
    (define (get-char x)
      (caddr x))
    (define (get-pass x)
      (cadddr x))
    
    (define (parse-password pass-line)
      (let* ((split-line ((string-splitter) pass-line))
             (char (string-ref (cadr split-line) 0))
             (password (caddr split-line))
             (range ((string-splitter 'delimiter #\-) (car split-line)))
             (start (string->number (car range)))
             (end (string->number (cadr range))))
        (make-pass-line start end char password)))
    
    (define lines (get-lines parse-password))
    
    (define (count-char char string)
      (string-count (lambda (char2) (eq? char char2)) string))
    
    (define (is-valid-one? pass-line)
      (let ((char-count (count-char (get-char pass-line)
                                    (get-pass pass-line))))
        (and (>= char-count (get-start pass-line))
             (<= char-count (get-end pass-line)))))
    
    (define (xor a b)
      (and (or a b)
           (not (and a b))))
    
    (define (is-valid-two? pass-line)
      (let* ((pass (get-pass pass-line))
             (char (get-char pass-line))
             (a (string-ref pass (- (get-start pass-line) 1)))
             (b (string-ref pass (- (get-end pass-line) 1))))
        (xor (eq? a char) (eq? b char))))
    
    (count is-valid-one? lines)
    (count is-valid-two? lines)
    
    2 votes
  15. knocklessmonster
    (edited )
    Link
    I know I'm not a great programmer, but I can usually sort my way through this sort of simple stuff somewhat easily. Then again, is it "easy" if I have to comb through documentation to do it? Day 2...

    I know I'm not a great programmer, but I can usually sort my way through this sort of simple stuff somewhat easily. Then again, is it "easy" if I have to comb through documentation to do it?

    Day 2
    using System;
    using System.IO;
    using static System.Console;
    
    namespace _2
    {
        class Program
        {
            static void Main(string[] args)
            {
                string _file = Path.Combine(Directory.GetCurrentDirectory(), "input");
                WriteLine(_file);
                int letterCount = 0;
                int passCount = 0;
                int pass2Count = 0;
                int min = 1;
                int max = 1;
                string[] inputs = File.ReadAllLines(_file);
                foreach (string line in inputs)
                {
                    string[] words = line.Split(" ");
                    string[] rulNum = words[0].Split("-");
                    min = Convert.ToInt32(rulNum[0]);
                    max = Convert.ToInt32(rulNum[1]);
                    char letter = Convert.ToChar(words[1].ToString().Trim(':'));
                    string password = words[2].ToString();
                    letterCount = 0;
                    char[] pass = password.ToCharArray();
                    int passLength = password.Length;
                    for (int i=0; i < password.Length; ++i)
                    {
                        if (letter == pass[i])
                            letterCount +=1;
                    }
                    if (letterCount >= min && letterCount <= max)
                    {
                        passCount += 1;
                    }
                    if (pass[min-1] != letter)
                    {
                        if (pass[max-1] == letter)
                            pass2Count+=1;
                    }
                    if (pass[min-1] == letter)
                        {
                            if (pass[max-1] != letter)
                                pass2Count+=1;
                        }
                };
                WriteLine("The number of valid passwords is {0}", passCount);
                WriteLine("The number of properly organized passwords is {0}", pass2Count);
                ReadKey();
            }
        }
    }
    

    I was wondering if any c# wizards could offer me some help? I spent hours trying to crack part 2 because I couldn't get a simple evaluation to work and stumbled into the bodge above, which basically does the same thing that i wanted to. BTW, don't get it wrong too much, or you get to wait 10 minutes.

    The above statement simply doesn't provide any output, and I know there's data that follows it in the dataset.

    if (max > password.Length)
        WriteLine("True");
    

    The WriteLine() is just a placeholder, but that test doesn't work. My original approach had me

    2 votes
  16. [3]
    nothis
    Link
    I'm not great at Python and decided this is a chance to finally dive into it a little more. I spent like an hour trying to cleanly install Python 3 on macOS, which is positively kafkaesque. But...

    I'm not great at Python and decided this is a chance to finally dive into it a little more. I spent like an hour trying to cleanly install Python 3 on macOS, which is positively kafkaesque. But once it's running, it's quite pleasant how quickly you can get things to work!

    Quick question: Is the whole os-path mumbo jumbo really necessary to just load a file in the same folder? It seems odd but I had to paste this in to avoid a "file not found" error.

    Part 1+2
    import os
    import re
    
    thisFolder = os.path.dirname(os.path.abspath(__file__))
    inputFilePath = os.path.join(thisFolder, 'input.txt')
    
    with open(inputFilePath) as inputFile:
        passwordEntries = inputFile.read().split("\n")
    
    correctCountA = 0
    correctCountB = 0
    for pwEntry in passwordEntries:
        limits = re.findall(r'\d+', pwEntry)
        lower = int(limits[0])
        upper = int(limits[1])
        letter = re.findall(r'(.)\:', pwEntry)[0]
        password = re.findall(r'\: (.+)', pwEntry)[0]
        letterCount = password.count(letter)
    
        if letterCount >= lower and letterCount <= upper:
            correctCountA += 1
    
        if password[lower - 1] == letter and not password[upper - 1] == letter:
            correctCountB += 1
        elif not password[lower - 1] == letter and password[upper - 1] == letter:
            correctCountB += 1
    
    print("Number of correct passwords (definition A): ", correctCountA)
    print("Number of correct passwords (definition B): ", correctCountB)
    
    2 votes
    1. [2]
      blitz
      Link Parent
      Nope! The file path can be absolute or relative. It all depends on which directory you're currently in in your terminal when you run the script. If you ls in your terminal and you see both the...

      Quick question: Is the whole os-path mumbo jumbo really necessary to just load a file in the same folder? It seems odd but I had to paste this in to avoid a "file not found" error.

      Nope! The file path can be absolute or relative. It all depends on which directory you're currently in in your terminal when you run the script. If you ls in your terminal and you see both the script you're trying to run and the file you need to read, all you should need to give the script is the name of the file.

      BUT(!) remember that relative paths are relative to your current working directory in your terminal! So if you're in Documents and both your script and input file are in Documents/code/, say, and you run the script with the invocation python code/script.py, and you specify the input path as just the file name, Python will look for the file in Documents, even though it's running the script in Documents/code/!

      I hope this was helpful! If you want to read more it looks like there are loads of blog posts about absolute vs relative paths on the internet! :)

      4 votes
      1. nothis
        Link Parent
        Ah, thanks! I had an advent_of_code folder with each day a subfolder and python ran from the parent folder so I had to add the subfolder name to the filepath.

        Ah, thanks! I had an advent_of_code folder with each day a subfolder and python ran from the parent folder so I had to add the subfolder name to the filepath.

        1 vote
  17. [6]
    0d_billie
    Link
    OK, day 2 felt more straightforward than day 1, even though it involved a lot of regex shenanigans. Here's my answers: Part 1 runs in 0.0049 seconds #!/usr/bin/env python import re # Create empty...

    OK, day 2 felt more straightforward than day 1, even though it involved a lot of regex shenanigans. Here's my answers:

    Part 1 runs in 0.0049 seconds
    #!/usr/bin/env python
    import re
    
    # Create empty list and import data
    passwords = []
    with open("input", "r") as input:
        for line in input.readlines():
            line = re.sub("\n","",line)     # strips newlines
            line = re.split(":\s|\s",line)  # convert to 3-part list: numbers, letters, password
            passwords.append(line)
            
    # Set variable for final output
    valid_passwords = 0
    
    for password in passwords:
        # find actual numbers in entry and create min and max allowed amounts
        n = re.findall("\d{1,2}",password[0])
        n_min = int(n[0])
        n_max = int(n[1])
        
        # set the letter as l
        l = password[1]
        
        # set the password as p
        p = password[2]
    
        #if l is in p, and number of l is within n_min/n_max, increase valid_passwords by 1
        if l in p:
            if p.count(l) >= n_min and p.count(l) <= n_max:
                valid_passwords +=1
    
    print(valid_passwords)
    
    Part 2 runs in 0.0037 seconds
    Set variable for final output
    valid_passwords = 0
    
    for password in passwords:
        # find actual numbers in entry and create first and last positions
        n = re.findall("\d{1,2}",password[0])
        n_first = int(n[0]) - 1
        n_last = int(n[1]) - 1
    
        # set the letter as l
        l = password[1]
        
        # set the password as p
        p = password[2]
        
        if p[n_first] == l:
            if p[n_last] != l:
                valid_passwords +=1
        if p[n_first] != l:
            if p[n_last] == l:
                valid_passwords +=1
    
    print(valid_passwords)
    
    2 votes
    1. [5]
      0d_billie
      Link Parent
      As a follow-up, I was both confused and annoyed to discover that the "or" operator in python is not exclusive. I was beating my head against the wall for a fair while as I tried to fathom out why...

      As a follow-up, I was both confused and annoyed to discover that the "or" operator in python is not exclusive. I was beating my head against the wall for a fair while as I tried to fathom out why part 2 was spitting out such a high answer. It feels much more elegant to be able to write

          if p[n_first] == l or p[n_last] == l:
              valid_passwords +=1
      

      but it seems that's not allowed. Can anybody advise how to get an exclusive or statement to work, because after extensive Googling, I came up with nothing that actually worked. The ^ operator doesn't seem to count for strings, I believe?

      1 vote
      1. [3]
        Liru
        Link Parent
        ^ has a higher precedence than == or or. You probably did something like this, accidentally (added parentheses to show ordering): if p[n_first] == (l ^ p[n_last]) == l: To solve that, you need to...
        • Exemplary

        ^ has a higher precedence than == or or. You probably did something like this, accidentally (added parentheses to show ordering):

        if p[n_first] == (l ^ p[n_last]) == l:
        

        To solve that, you need to do something like this:

        if (p[n_first] == l) ^ (p[n_last] == l):
        
        3 votes
        1. [2]
          0d_billie
          Link Parent
          Ahhh, that makes a lot of sense. Sort of like the order of operations in maths, right? Thanks!

          Ahhh, that makes a lot of sense. Sort of like the order of operations in maths, right? Thanks!

          2 votes
          1. Deimos
            Link Parent
            If you ever want to be able to look it up, it seems to usually be referred to as "operator precedence" for programming. Here's the relevant section in the Python docs:...

            If you ever want to be able to look it up, it seems to usually be referred to as "operator precedence" for programming. Here's the relevant section in the Python docs: https://docs.python.org/3/reference/expressions.html#operator-precedence

            3 votes
      2. blitz
        Link Parent
        I would be surprised if the or operator is exclusive in any programming language. The or operator performs a logical disjunction on its operands.

        I would be surprised if the or operator is exclusive in any programming language. The or operator performs a logical disjunction on its operands.

        2 votes
  18. heady
    Link
    Both parts import re input = open("day2input", 'r') passwords = [] for line in input: bounds = re.findall(('\d+'), line) letter = re.findall(('[a-z]:'), line) letter = letter[0].strip(':') pwd =...
    Both parts
    import re
    
    input = open("day2input", 'r')
    
    passwords = []
    
    for line in input:
        bounds = re.findall(('\d+'), line)
        letter = re.findall(('[a-z]:'), line)
        letter = letter[0].strip(':')
        pwd = re.findall(('\w[a-z]+'), line)
        password = (int(bounds[0]), int(bounds[1]), letter, str(pwd[0]))
        passwords.append(password)
    
    
    valid_sled_pwds = []
    valid_toboggan_pwds = []
    
    for password in passwords:
        chars = re.findall(password[2], str(password[3]))
        if password[0] <= len(chars) <= password[1]:
            valid_sled_pwds.append(password)
    
    for password in passwords:
        pwd = password[3]
        char1 = pwd[(password[0])-1]
        char2 = pwd[(password[1])-1]
        chars = [char1, char2]
        
        if chars.count(password[2]) == 1:
            valid_toboggan_pwds.append(password)
    
    
    print("valid old passwords: " + str(len(valid_sled_pwds)))
    print("valid new passwords: " + str(len(valid_toboggan_pwds)))
    
    2 votes