DEV Community

Cover image for Advent of Code 2020 Solution Megathread - Day 2: Password Philosophy
Ryan Palo
Ryan Palo

Posted on • Updated on

Advent of Code 2020 Solution Megathread - Day 2: Password Philosophy

With Day 1 in the books, hopefully you’re starting to get into the swing of things. I know that I definitely didn’t spend a few hours fiddling with my AoC tooling yesterday. That would be obsessive and overkill. 😐

The Puzzle

Today’s puzzle involves passwords and security policies. Each listing has a password alongside the security policy in place for it, but some passwords aren’t compliant! It’s our job to analyze the passwords and sort things out.

The Leaderboards

As always, this is the spot where I’ll plug any leaderboard codes shared from the community.

Ryan's Leaderboard: 224198-25048a19
Enter fullscreen mode Exit fullscreen mode

If you want to generate your own leaderboard and signal boost it a little bit, send it to me either in a DEV message or in a comment on one of these posts and I'll add it to the list above.

Yesterday’s Languages

Updated 03:05PM 12/12/2020 PST.

Language Count
Python 5
C 2
Rust 2
JavaScript 2
C# 1
Raku 1
PHP 1
Scratch 1
Elixir 1
Haskell 1
COBOL 1
Ruby 1

Merry Coding!

Latest comments (23)

Collapse
 
kudostoy0u profile image
Kudos Beluga • Edited

I started learning Rust and immediately felt satisfied coding in it, and Advent of Code seemed like the perfect place to get better at rust. Probably not the best or cleanest way to do it (didn't use XOR), but here's day 2 in Rust:

use std::fs;
fn part1(stringtocompute: Vec<&str>, chartofind: &str, min: u16, max: u16) -> u32 {
    let mut occurences = 0;
    for i in stringtocompute {
        if &i == &chartofind {
            occurences += 1;
        }
    }
    if occurences >= min && occurences <= max {
        return 1;
    } else {
        return 0;
    }
}
fn part2(stringtocompute: Vec<&str>, chartofind: &str, min: u16, max: u16) -> u32 {
    let mut positionsfound = 0;
    for i in 0..stringtocompute.len() {
        if stringtocompute[i] == chartofind && i == min.into() {
            positionsfound += 1;
        } else if stringtocompute[i] == chartofind && i == max.into() {
            positionsfound += 1;
        }
    }
    if positionsfound == 1 {
        return 1;
    } else {
        return 0;
    }
}
fn main() {
    let contents = fs::read_to_string("./day2.txt").expect("Bad file read");
    let mut values = contents.split("\n").collect::<Vec<_>>();
    values.pop();
    let mut valids: u32 = 0;
    let mut valids2: u32 = 0;
    for i in &values {
        let nums = &i.split("-").collect::<Vec<_>>();
        let min = nums[0].parse::<u16>().unwrap();
        let max = nums[1].split(" ").collect::<Vec<_>>()[0]
            .parse::<u16>()
            .unwrap();
        let chartofind = nums[1].split(":").collect::<Vec<_>>()[0]
            .split(" ")
            .collect::<Vec<_>>()[1];
        let stringtocompute = i.split(": ").collect::<Vec<_>>()[1]
            .split("")
            .collect::<Vec<_>>();
        valids += part1(stringtocompute.clone(), chartofind, min, max);
        valids2 += part2(stringtocompute, chartofind, min, max);
    }
    println!("Part 1: {:?}\nPart 2: {:?}", valids, valids2);
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
clothierdroid profile image
David Clothier • Edited

SQL (0 lines of code huehue)

Get this table naming 'day2'

table

2.1. Solution

SELECT COUNT(*) FROM 
(
SELECT
   letra
 , pass
 , nmin
 , nmax
 ,(LENGTH(pass) - LENGTH(REPLACE(pass, letra, ''))) AS times
FROM
   day2
HAVING
   times BETWEEN nmin AND nmax
) puzzle_answer
Enter fullscreen mode Exit fullscreen mode

2.2. Solution

SELECT COUNT(*) 
FROM   day2 
WHERE  LOCATE(letra, pass, nmin) = nmin
       XOR LOCATE(letra, pass, nmax) = nmax 
Enter fullscreen mode Exit fullscreen mode
Collapse
 
pihentagy profile image
pihentagy • Edited

Short and hopefully non-cryptic python. Ideas to improve?

def explode(line):
    policy, password = line.split(': ',1)
    nums, letter = policy.split()
    a, b = map(int, nums.split('-'))
    return a,b , letter, password

def compliant(line):
    min, max, letter, password = explode(line)
    return min <= password.count(letter) <= max

def compliant2(line):
    p1, p2, letter, password = explode(line)
    return (password[p1-1] == letter) != (password[p2-1] == letter)

print(sum(1 for _ in filter(compliant, open('input2'))))
print(sum(1 for _ in filter(compliant2, open('input2'))))
Enter fullscreen mode Exit fullscreen mode
Collapse
 
thibpat profile image
Thibaut Patel

My JavaScript walkthrough:

Collapse
 
harsha profile image
Harshavardhan

Here's my solution in Python

def partOne(low, high, letter, password):
    count = 0
    for char in password:
        if char == letter:
            count += 1
    return high >= count >= low


def partTwo(position1, position2, letter, password):
    return (password[position1] == letter) ^ (password[position2] == letter)


out1 = 0
out2 = 0
with open("Problem-2/Day-2-Password-Philosophy.txt") as file:
    for line in file:
        freq, letter, password = line.split(" ")
        letter = letter[:len(letter) - 1]
        low, high = map(int, freq.split('-'))
        if partOne(low, high, letter, password):
            out1 += 1
        if partTwo(low - 1, high - 1, letter, password):
            out2 += 1

print(out1, out2)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jibaru profile image
Ignacior

Javascript solution:

let fs = require("fs");

var entryList = fs.readFileSync("input.txt", "utf8").split("\n");

function checkCharacter(str, chr, min, max) {
  let count = 0;
  for (let c of str) {
    if (c == chr) {
      count++;
    }
  }
  return !(count < min || count > max);
}

let validPasswords = 0;

for (let entry of entryList) {
  let patterns = entry.split(": ");
  let validatorPattern = patterns[0].split(" ");
  let minMax = validatorPattern[0].split("-");

  if (
    checkCharacter(
      patterns[1],
      validatorPattern[1],
      parseInt(minMax[0]),
      parseInt(minMax[1])
    )
  ) {
    validPasswords++;
  }
}
console.log("Part one");
console.log("Valid passwords: ", validPasswords);

function isValidPassword(str, chr, validPos, invalidPos) {
  return (
    (str.charAt(validPos) === chr || str.charAt(invalidPos) === chr) &&
    str.charAt(validPos) != str.charAt(invalidPos)
  );
}

validPasswords = 0;

for (let entry of entryList) {
  let patterns = entry.split(": ");
  let validatorPattern = patterns[0].split(" ");
  let validInvalid = validatorPattern[0].split("-");

  if (
    isValidPassword(
      patterns[1],
      validatorPattern[1],
      parseInt(validInvalid[0]) - 1,
      parseInt(validInvalid[1]) - 1
    )
  ) {
    validPasswords++;
  }
}
console.log("Part two");
console.log("Valid passwords: ", validPasswords);

Enter fullscreen mode Exit fullscreen mode
Collapse
 
harrygibson profile image
Harry Gibson

I can't remember the last time I had a genuine use for XOR in my day job!

def parse_line(line):
    nums, char, pw = line.split(' ')
    num_1, num_2 = [int(i) for i in nums.split('-')]
    char = char.strip(':')
    return num_1, num_2, char, pw

def is_valid_part1(line):
    min_occ, max_occ, char, pw = parse_line(line)
    n = pw.count(char)
    return min_occ <= n <= max_occ

def is_valid_part2(line):
    loc_1, loc_2, char, pw = parse_line(line)
    if max(loc_1, loc_2) > len(pw) + 1: return False
    return (pw[loc_1-1] == char) ^ (pw[loc_2-1] == char)


with open('input.txt') as f:
    input_lines = [l.strip() for l in f]

print (f"Part 1: {sum(map(is_valid_part1, input_lines))} passwords were valid")
print (f"Part 2: {sum(map(is_valid_part2, input_lines))} passwords were valid")
Enter fullscreen mode Exit fullscreen mode
Collapse
 
klnjmm profile image
Jimmy Klein • Edited

Hi,

For this 2nd day, I want to play a little with regexp :) . Code In PHP

Full size here : Advent of Code - Day 2

Advent of Code - Day 2

Collapse
 
aspittel profile image
Ali Spittel

Mine!

def check_count_in_range(password, letter, start, stop):
    return password.count(letter) >= start and password.count(letter) <= stop


def check_indexes(password, letter, start, stop):
    return (password[start] == letter or password[stop] == letter) and (password[start] != password[stop])


with open('input.txt') as file:
    letters = [num.split(": ") for num in file]
    part1_tally = 0
    part2_tally = 0
    for policy, password in letters:
        length, letter = policy.split(" ")
        start, stop = length.split("-")
        start, stop = int(start), int(stop)

        # part 1
        if check_count_in_range(password, letter, start, stop): part1_tally += 1

        # part 2
        if check_indexes(password, letter, start - 1, stop - 1): part2_tally += 1


    print("Part 1", part1_tally)
    print("Part 2", part2_tally)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld • Edited

What I came up with in Ruby:

require 'benchmark'

class PasswordPolicy
  def self.from_line(line)
    length, char, password = line.split(' ')
    first, second = length.split('-').map(&:to_i)
    char = char.delete(':')

    PasswordPolicy.new(positions: [first - 1, second - 1], char: char, password: password)
  end

  def initialize(positions:, char:, password:)
    self.positions = positions
    self.char = char
    self.password = password
  end

  def valid?
    positions.count { |i| password[i] == char } == 1
  end

  private

  attr_accessor :positions, :char, :password
end

lines = File.readlines('input.txt')

valids = 0

Benchmark.bmbm do |x|
  x.report do
    valids = lines.count do |line|
      PasswordPolicy.from_line(line).valid?
    end
  end
end

puts valids
Enter fullscreen mode Exit fullscreen mode

And for comparison, here is the inlined version:

entries = File.readlines('input.txt').map do |line|
  positions, char, password = line.split
  left, right = positions.split(?-).map(&:to_i)
  [left, right, char.first, password.chomp]
}

puts entries.count do |left, right, char, password|
  (password[left - 1] == char) != (password[right - 1] == char)
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
neilgall profile image
Neil Gall

Using AoC to brush up my Rust this year.

I adapted Bodil's parser combinators into a little parser module, as I suspect there'll be much more parsing to come.

And then the solution is:


mod parser;

use std::fs::File;
use std::io::prelude::*;
use std::ops::Range;
use parser::*;


// ---- model

#[derive(Debug, Eq, PartialEq)]
struct Password {
    position1: usize,
    position2: usize,
    character: char,
    password: String
}

// ---- model parser

fn password(input: &str) -> ParseResult<Password> {
    let pos1p = first(integer, string("-"));
    let pos2p = first(integer, whitespace);
    let charp = first(letter, string(": "));
    let passp = one_or_more(letter);
    let parser = map(seq(pos1p, seq(pos2p, seq(charp, passp))), |(n1, (n2, (c, p)))| Password {
        position1: n1 as usize,
        position2: n2 as usize,
        character: c,
        password: p.into_iter().collect()
    });
    parser.parse(input)
}

// --- input file

fn read_file(filename: &str) -> std::io::Result<String> {
    let mut file = File::open(filename)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

fn parse_input(input: &str) -> ParseResult<Vec<Password>> {
    let p = one_or_more(first(password, whitespace));
    p.parse(input)
}


// --- problem

impl Password {
    fn part1_is_valid(&self) -> bool {
        let n = self.password.chars().filter(|c| c == &self.character).count();
        self.position1 <= n && n <= self.position2
    }

    fn part2_is_valid(&self) -> bool {
        let c1 = self.password.chars().nth(self.position1 - 1) == Some(self.character);
        let c2 = self.password.chars().nth(self.position2 - 1) == Some(self.character);
        (c1 || c2) && !(c1 && c2)
    }
}

fn part1(passwords: &Vec<Password>) -> usize {
    passwords.iter().filter(|p| p.part1_is_valid()).count()
}

fn part2(passwords: &Vec<Password>) -> usize {
    passwords.iter().filter(|p| p.part2_is_valid()).count()
}

fn main() {
    let input = read_file("../input.txt").unwrap();
    let (_, passwords) = parse_input(&input).unwrap();
    println!("part1 {}", part1(&passwords));
    println!("part2 {}", part2(&passwords));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_passwords() {
        let (rest, passwords) = parse_input("1-3 a: abcde\n1-3 b: cdefg\n2-9 c: ccccccccc").unwrap();
        assert_eq!(rest, "");
        assert_eq!(passwords,
            vec![
                Password { position1: 1, position2: 3, character: 'a', password: String::from("abcde") },
                Password { position1: 1, position2: 3, character: 'b', password: String::from("cdefg") },
                Password { position1: 2, position2: 9, character: 'c', password: String::from("ccccccccc") }
            ]
        );
    }

    #[test]
    fn test_part1_is_valid_1() {
        let p = Password { position1: 1, position2: 3, character: 'a', password: String::from("abcde") };
        assert_eq!(p.part1_is_valid(), true);
    }

    #[test]
    fn test_part1_is_valid_2() {
        let p = Password { position1: 1, position2: 3, character: 'b', password: String::from("cdefg") };
        assert_eq!(p.part1_is_valid(), false);
    }

    #[test]
    fn test_part1_is_valid_3() {
        let p = Password { position1: 2, position2: 9, character: 'c', password: String::from("ccccccccc") };
        assert_eq!(p.part1_is_valid(), true);
    }

    #[test]
    fn test_part2_is_valid_1() {
        let p = Password { position1: 1, position2: 3, character: 'a', password: String::from("abcde") };
        assert_eq!(p.part2_is_valid(), true);
    }

    #[test]
    fn test_part2_is_valid_2() {
        let p = Password { position1: 1, position2: 3, character: 'b', password: String::from("cdefg") };
        assert_eq!(p.part2_is_valid(), false);
    }

    #[test]
    fn test_part2_is_valid_3() {
        let p = Password { position1: 2, position2: 9, character: 'c', password: String::from("ccccccccc") };
        assert_eq!(p.part2_is_valid(), false);
    }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
benwtrent profile image
Benjamin Trent • Edited

My Rust solution for Day 2. Also using an XOR :). Great minds think alike.

#[derive(Debug, Eq, PartialEq)]
pub struct PasswordPolicy {
    character: char,
    min: usize,
    max: usize,
} 

impl From<&String> for PasswordPolicy {
    fn from(s: &String) -> Self {
        let spaces:Vec<&str> = s.split(" ").collect();
        let character:char = spaces[1].chars().next().unwrap();
        let vec:Vec<usize> = spaces[0].split("-").map(|i| i.parse().unwrap()).collect();
        PasswordPolicy {
            character,
            min: vec[0],
            max: vec[1]
        }
    }
}


impl PasswordPolicy {

    pub fn satisfied_1(&self, password: &str) -> bool {
        let ct = password.chars().filter(|c| *c == self.character).count();
        (self.min - 1) < ct && ct < (self.max + 1)
    }

    pub fn satisfied_2(&self, password: &str) -> bool {
        let mut ans = false;
        for (i, c) in password.chars().enumerate() {
            if (i + 1) == self.min  || (i + 1) == self.max {
                ans ^= self.character == c;
            }
        }
        ans
    }
}

#[aoc_generator(day2)]
fn input_to_vec(input: &str) -> Vec<(PasswordPolicy, String)> {
    input.lines().map(|i| {
        let splt = i.split(": ").map(|s| String::from(s)).collect::<Vec<String>>();
        (PasswordPolicy::from(&splt[0]), splt[1].to_string())
    }).collect()
}

#[aoc(day2, part1)]
fn valid_password_count(input: &Vec<(PasswordPolicy, String)>) -> usize {
    input.iter().filter(|(policy, password)| policy.satisfied_1(password.as_str())).count()
}

#[aoc(day2, part2)]
fn valid_password_count2(input: &Vec<(PasswordPolicy, String)>) -> usize {
    input.iter().filter(|(policy, password)| policy.satisfied_2(password.as_str())).count()
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
galoisgirl profile image
Anna

COBOL (second one is on my GitHub):

   IDENTIFICATION DIVISION.
   PROGRAM-ID. AOC-2020-02-1.
   AUTHOR. ANNA KOSIERADZKA.

   ENVIRONMENT DIVISION.
   INPUT-OUTPUT SECTION.
   FILE-CONTROL.
       SELECT INPUTFILE ASSIGN TO "d2.input"
       ORGANIZATION IS LINE SEQUENTIAL.

   DATA DIVISION.
   FILE SECTION.
     FD INPUTFILE
     RECORD IS VARYING IN SIZE FROM 8 to 50
     DEPENDING ON REC-LEN.
     01 INPUTRECORD PIC X(50).
   WORKING-STORAGE SECTION.
     01 FILE-STATUS PIC 9 VALUE 0.
     01 REC-LEN  PIC 9(2) COMP.
     01 WS-MIN PIC 9(4).
     01 WS-MAX PIC 9(4).
     01 WS-CHAR PIC A.
     01 WS-STRING-EMPTY PIC X.
     01 WS-PASSWORD PIC A(50).
     01 WS-SUBSTR-1 PIC X(5). 
     01 WS-CHAR-COUNT PIC 9(2).

   LOCAL-STORAGE SECTION.
     01 CORRECT-ROWS UNSIGNED-INT VALUE 0.

   PROCEDURE DIVISION.
   001-MAIN.
        OPEN INPUT INPUTFILE.
        PERFORM 002-READ UNTIL FILE-STATUS = 1.
        CLOSE INPUTFILE.
        DISPLAY CORRECT-ROWS.
        STOP RUN.

   002-READ.
        READ INPUTFILE
            AT END MOVE 1 TO FILE-STATUS
            NOT AT END PERFORM 003-PROCESS-RECORD
        END-READ.

   003-PROCESS-RECORD.
       MOVE 0 TO WS-CHAR-COUNT.
       UNSTRING INPUTRECORD DELIMITED BY SPACE OR "-" OR ":" INTO 
           WS-MIN
           WS-MAX
           WS-CHAR
           WS-STRING-EMPTY
           WS-PASSWORD.
       INSPECT WS-PASSWORD TALLYING WS-CHAR-COUNT FOR ALL WS-CHAR.
       IF WS-CHAR-COUNT >= WS-MIN AND WS-CHAR-COUNT <= WS-MAX THEN 
          ADD 1 TO CORRECT-ROWS
       END-IF.
Enter fullscreen mode Exit fullscreen mode
Collapse
 
patryk profile image
Patryk Woziński • Edited

IMO you’re a real hipster using Cobol! Great job! :)

Collapse
 
galoisgirl profile image
Anna

Thanks! Gotta stay busy in the pandemic somehow. :)

Thread Thread
 
patryk profile image
Patryk Woziński

May I ask you - have you used Cobol before? Or it's just your pandemic-advent challenge? :D

Thread Thread
 
galoisgirl profile image
Anna • Edited

I started in the spring and wrote a couple things like FizzBuzz: github.com/GaloisGirl/Coding/tree/...
Then I had a bit of a life, and now I'm having a second wave of COBOL.

Collapse
 
katafrakt profile image
Paweł Świątkowski

Late for the party, as always, but here's a few words about yesterday's challenge:

Collapse
 
karaajc profile image
Kara Carrell

Hey Y'all!! This time, I actually brought the input.txt file in as it was, and again, made a ruby solution that im sure could be a bit more refactored, but i got to play with regex! so that was fun...
Here's my full code, and here's a snippet of what i did to solve it:

class PasswordChecker

  attr_reader :parse_passwords, :check_passwords

  def initialize(passwords)
    @passwords = passwords
    @parsed_passwords = parse_passwords
  end

  def parse_passwords
    parsed = @passwords.map do |item|
      item_split = item.match(/(^\d*)-(\d*) (\w): (\w*)/)
      {
        min: item_split[1].to_i,
        max: item_split[2].to_i,
        letter: item_split[3],
        password: item_split[4]
      }
    end
    parsed
  end

  def validate_password_frequency(log)
    # puts "rules are minimum #{log[:min]} and max #{log[:max]}"
    # puts "there are #{log[:password].count(log[:letter])} letter #{log[:letter]}'s"
    log[:password].count(log[:letter]).to_i.between?(log[:min],log[:max])
  end

  def validate_password_accuracy(log)
    return false unless log[:password].include?(log[:letter])
    return false if log[:password][log[:min] - 1] == log[:letter] && log[:password][log[:max] - 1] == log[:letter]

    # puts "rules are letter #{log[:letter]} must be at position #{log[:min]} and then at position #{log[:max]}"
    # puts "there's a #{log[:password][log[:min] - 1]} at position #{log[:min]} and a #{log[:password][log[:max] - 1]} at position #{log[:max]}"
    log[:password][log[:min] - 1] == log[:letter] || log[:password][log[:max] - 1] == log[:letter]
  end

  def check_passwords(way="frequency")
    @parsed_passwords.map do |log|
        if way == "accuracy"
          validate_password_accuracy(log)
        else
          validate_password_frequency(log)
        end
    end.count(true)
  end

end
Enter fullscreen mode Exit fullscreen mode