DEV Community

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

Posted on • Edited 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
 
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