12 votes

Day 4: Secure Container

Today's problem description: https://adventofcode.com/2019/day/4


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>

12 comments

  1. markh
    Link
    Join the leaderboard using code 730956-de85ce0c! This was, to me, the easiest challenge so far. Here's a simple, dirty solution in Javascript. Part 1 const generateArray = (low, high) => { let...

    Join the leaderboard using code 730956-de85ce0c!


    This was, to me, the easiest challenge so far. Here's a simple, dirty solution in Javascript.

    Part 1
    const generateArray = (low, high) => {
        let array = [];
        for (var i = low; i <= high; i++) {
            array.push(i);
        }
        return array;
    }
    
    const isSixDigits = x => {
        const a = x.toString().split('');
        return a.length === 6;
    }
    
    const hasAdjacentDigits = x => {
        const a = x.toString().split('');
        const b = a.filter((el, idx, arr) => {
            return el === arr[idx + 1];
        });
        return b.length > 0;
    }
    
    const digitsNeverDecrease = x => {
        const a = x.toString().split('');
        const b = a.filter((el, idx, arr) => {
            return el <= arr[idx + 1];
        })
        return b.length === 5;
    }
    
    const entireRange = generateArray(193651, 649729);
    
    const filteredRange = entireRange.filter(isSixDigits).filter(hasAdjacentDigits).filter(digitsNeverDecrease);
    
    console.log(filteredRange.length);
    
    Part 2
    const generateArray = (low, high) => {
        let array = [];
        for (var i = low; i <= high; i++) {
            array.push(i);
        }
        return array;
    }
    
    const isSixDigits = x => {
        const a = x.toString().split('');
        return a.length === 6;
    }
    
    const hasAdjacentButNotTooAdjecentDigits = x => {
        const a = x.toString().split('');
        const b = a.filter((el, idx, arr) => {
            const matchesNext = el === arr[idx + 1];
            const matchesTwoInFront = el === arr[idx + 2]
            const matchesPrevious = el === arr[idx - 1];
    
            if (matchesNext && !(matchesTwoInFront || matchesPrevious)) {
                return el === arr[idx + 1];
            }
        });
    
        return b.length > 0;
    }
    
    const digitsNeverDecrease = x => {
        const a = x.toString().split('');
        const b = a.filter((el, idx, arr) => {
            return el <= arr[idx + 1];
        })
        return b.length === 5;
    }
    
    const entireRange = generateArray(193651, 649729);
    
    const filteredRange = entireRange.filter(isSixDigits).filter(hasAdjacentButNotTooAdjecentDigits).filter(digitsNeverDecrease);
    
    console.log(filteredRange.length);
    
    4 votes
  2. tesseractcat
    (edited )
    Link
    Day 4 in Befunge Day 4 took a long time, partially because I had to fix some kinks in my interpreter so it functioned more accurately according to the spec, so I could feed my code into an ultra...

    Day 4 in Befunge

    Day 4 took a long time, partially because I had to fix some kinks in my interpreter so it functioned more accurately according to the spec, so I could feed my code into an ultra fast Befunge compiler and get the answer in a reasonable amount of time. The other reason it took a while was because I initially had my code only check in 3 sections, so [56][34][11] would have been valid, and I had a hard to see error with end of number detection. But I fixed it all eventually. I haven't completed part 2 yet.

    Part 1

    For this to complete in any reasonable amount of time, I recommend BefunExec to run it.

    v
    >069*0p066*0p067*0p068*0pv
    v                        <
    v                                                                    
    >&:10p30p&20p    v_v# - g02 g03  <
                                                          
                       >68*0g .@
                     > 69*0g1+69*0p v            
     v\-10 p03:+1g03 $p0*660 $p0*760<  Advent of Code 2019 - Day 4 Pt. 1
     v v      $<                    >^
     >  >:0`#v_^                                 
             >:52*% \52*/v    <-- Convert to digits  v                   <
       =^                <          #                                    $
       v    g05    _v#+1:<   >$$$$$$^          <     <                   $
       \            >$      :|                                           $
       :
       5
       0
       p
       \
       >-:#v_77*66*0p$   ^   >67*0g#^_ 66*0g#v_^>  68*0g1+68*0p          ^
           >0`#v_77*67*0p^                   >  ^
               >         ^
    
    Part 2
    v
    >069*0p066*0p067*0p068*0pv
    v                        <
    v                                                                    
    >&:10p30p&20p    v_v# - g02 g03  <
                                                          
                       >68*0g .@     ^                           <
                     > 69*0g1+69*0p v>" ":65*1p:66*1p:67*1p:68*1p^            
     v\-10 p03:+1g03 $p0*660 $p0*760<  Advent of Code 2019 - Day 4
     v v      $<                    >^
     >  >:0`#v_^                                 
             >:52*% \52*/v    <-- Convert to digits  v              $$$$$<
        ^                <          #                                    $
       v    g05    _v#+1:<   >$$$$$$^  $$$$$$$$<    <<                   $
       \            >$      :|                                           $
       :                                             
       5                                             
       0 Part 2 - If a rabbit were an elf, would it's ears look like this <o.^ or like this <o.)
       p                 #                          #                    #
       \                                             
       >-:#v_77*66*0p$   ^   >67*0g#^_ 66*0g#v_^>   v >      68*0g1+68*0p^
           >0`#v_77*67*0p^                   >  ^   #                >:65*1g-#v_66*1g1+66*1p            v
               >         ^                          ^_^#-1g1*76 $<            >:65*1p 66*1g2-#v_167*1p> v
                                                                 ^              <       <
                                                                 >      66*1g2-#^_167*1p^
                                                    >$30g >:0`#v_^    v           <           >166*1p ^
                                                               >:52*%^>\52*/v     ^                     <
                                                          ^                 <
    
    4 votes
  3. mftrhu
    Link
    This one was almost trivial to implement, and I ended up doing it on my phone during a break at work. I'll have to rewrite it in Julia once I'll be back home, though. Part 1 from itertools import...

    This one was almost trivial to implement, and I ended up doing it on my phone during a break at work. I'll have to rewrite it in Julia once I'll be back home, though.

    Part 1
    from itertools import combinations_with_replacement
    import re
    
    def has_double(n):
        return re.search(r"(\d)\1", str(n))
    
    def in_range(n):
        return 357253 <= n <= 892942
    
    candidates = combinations_with_replacement("123456789", 6)
    candidates = map(lambda t: int(''.join(t)), candidates)
    candidates = filter(in_range, candidates)
    candidates = filter(has_double, candidates)
    candidates = list(candidates)
    print(len(candidates))
    
    Part 2
    from itertools import combinations_with_replacement
    import re
    
    def has_double(n):
        n = str(n)
        m = re.findall(r"(\d)\1", n)
        return m and (not
            all(re.search(r"%s{3,}" % d, n) for d in m))
    
    def in_range(n):
        return 357253 <= n <= 892942
    
    candidates = combinations_with_replacement("123456789", 6)
    candidates = map(lambda t: int(''.join(t)), candidates)
    candidates = filter(in_range, candidates)
    candidates = filter(has_double, candidates)
    candidates = list(candidates)
    print(len(candidates))
    
    3 votes
  4. mozz
    Link
    I'm trying out the advent challenges as an opportunity to learn go. This is the first day that my code is clean enough that I'm not actively embarrassed by it. Part 1 package main import ( "fmt"...

    I'm trying out the advent challenges as an opportunity to learn go. This is the first day that my code is clean enough that I'm not actively embarrassed by it.

    Part 1
    package main
    
    import (
    	"fmt"
    	"strconv"
    )
    
    func check(number int) bool {
    	previous, hasRepeating := rune(0), false
    	for _, char := range strconv.Itoa(number) {
    		if char == previous {
    			hasRepeating = true
    		} else if char < previous {
    			// The digits should never decrease
    			return false
    		}
    		previous = char
    	}
    	return hasRepeating
    }
    
    func main() {
    	start, end := 359282, 820401
    	count:= 0
    	for i := start; i <= end; i++ {
    		if check(i) {
    			count++
    		}
    	}
    	fmt.Println(count)
    }
    
    3 votes
  5. Crestwave
    Link
    Whew, I thought it was almost getting too hard for me with yesterday's challenge, but this one seems much easier. Here's a quick and dirty awk implementation; might clean it up a bit later. Part 1...

    Whew, I thought it was almost getting too hard for me with yesterday's challenge, but this one seems much easier. Here's a quick and dirty awk implementation; might clean it up a bit later.

    Part 1
    #!/usr/bin/awk -f
    {
        split($0, range, "-")
        for (i = range[1]; i < range[2]; ++i) {
            for (j = 1; j < length(i); ++j) {
                if (substr(i, j, 1) == substr(i, j+1, 1))
                    double = 1
    
                if (substr(i, j, 1) > substr(i, j+1, 1)) {
                    double = 0
                    break
                }
            }
    
            unique += double
            double = 0
        }
    
        print unique
    }
    
    Part 2

    The same as part 1, except with line 6 extended to this:

    if (substr(i, j, 1) == substr(i, j+1, 1) && substr(i, j+2, 1) != substr(i, j, 1) && (j == 1 || substr(i, j-1, 1) != substr(i, j, 1)))
    

    Yes, I meant it when I said quick and dirty.

    2 votes
  6. [2]
    asterisk
    Link
    PHP functions.php <?php function isSixtLength($input) { return strlen((string) $input) == 6; } function isSameAdjent($input) { for ($i = 0; $i < strlen($input); $i++) { if ($i && $input[$i] ==...
    PHP

    functions.php

    <?php
    
    function isSixtLength($input)
    {
        return strlen((string) $input) == 6;
    }
    
    function isSameAdjent($input)
    {
        for ($i = 0; $i < strlen($input); $i++) {
            if ($i && $input[$i] == $input[$i - 1]) {
                return true;
            }
        }
        return false;
    }
    
    function isDecrease($input)
    {
        for ($i = 0; $i < strlen($input); $i++) {
            if ($i && $input[$i] < $input[$i - 1]) {
                return true;
            }
        }
        return false;
    }
    
    function findBi($input)
    {
        for ($i = 0; $i < strlen($input); $i++) {
            switch ($i) {
                case 0:
                    if ($input[$i] == $input[$i + 1] && $input[$i] != $input[$i + 2]) {
                        return true;
                    }
                    break;
                case 1:
                    if (
                        $input[$i] == $input[$i + 1] && $input[$i] != $input[$i + 2] && $input[$i] != $input[$i - 1]
                        || $input[$i] == $input[$i - 1] && $input[$i] != $input[$i + 1]
                    ) {
                        return true;
                    }
                    break;
                case strlen($input) - 2:
                    if (
                        $input[$i] == $input[$i + 1] && $input[$i] != $input[$i - 1]
                        || $input[$i] == $input[$i - 1] && $input[$i] != $input[$i - 2] && $input[$i] != $input[$i + 1]
                    ) {
                        return true;
                    }
                    break;
                case strlen($input) - 1:
                    if ($input[$i] == $input[$i - 1] && $input[$i] != $input[$i - 2]) {
                        return true;
                    }
                    break;
                default:
                    if (
                        $input[$i] == $input[$i + 1] && $input[$i] != $input[$i + 2] && $input[$i] != $input[$i - 1]
                        || $input[$i] == $input[$i - 1] && $input[$i] != $input[$i - 2] && $input[$i] != $input[$i + 1]
                    ) {
                        return true;
                    }
            }
        }
        return false;
    }
    

    part-1.php

    <?php include('functions.php');
    
    $count = 0;
    foreach (range(197487, 673251) as $number) {
        if (isSixtLength($number) && isSameAdjent((string) $number) && !isDecrease((string) $number)) {
            $count++;
        }
    }
    
    echo $count; 
    

    part-2.php

    <?php include('functions.php');
    
    $count = 0;
    foreach (range(197487, 673251) as $number) {
        if (isSixtLength($number) && findBi((string) $number) && !isDecrease((string) $number)) {
            $count++;
        }
    }
    
    echo $count;
    
    2 votes
    1. asterisk
      Link Parent
      Rewrited function findBi() <?php function findBi($input) { if (isSameAdjent(substr($input, 0, 2)) && !isSameAdjent(substr($input, 1, 2)) || !isSameAdjent(substr($input, strlen($input)-3, 2)) &&...
      Rewrited function findBi()
      <?php function findBi($input)
      {
          if (isSameAdjent(substr($input, 0, 2)) && !isSameAdjent(substr($input, 1, 2))
          || !isSameAdjent(substr($input, strlen($input)-3, 2)) && isSameAdjent(substr($input, strlen($input)-2, 2))) {
              return true;
          }
      
          for ($i = 1; $i < strlen($input) - 1; $i++) {
              if (!isSameAdjent(substr($input, $i-1, 2)) && isSameAdjent(substr($input, $i, 2)) && !isSameAdjent(substr($input, $i+1, 2))) {
                  return true;
              }
          }
          return false;
      }
      
      1 vote
  7. 9000
    Link
    Language: Rust As everyone else is saying, this one was much easier. I felt that there was probably a pure math way to solve this, but I couldn't quite work it out and I don't think it would work...

    Language: Rust

    As everyone else is saying, this one was much easier. I felt that there was probably a pure math way to solve this, but I couldn't quite work it out and I don't think it would work for arbitrary ranges. Anyway, the solution:

    Solution
    fn main() {
        let mut num_1: usize = 0;
        let mut num_2: usize = 0;
        for n in 382345..843167{
            let mut increasing = true;
            let mut double_1 = false;
            let mut double_2 = false;
            let mut prev_digit = n % 10;
            let mut run_length = 1;
            for digit in 1..6 {
                let next_digit = (n / 10_u32.pow(digit)) % 10;
                if prev_digit < next_digit {
                    increasing = false;
                    break;
                } else if prev_digit == next_digit {
                    double_1 = true;
                    run_length += 1;
                } else {
                    if run_length == 2 {
                        double_2 = true;
                    }
                    run_length = 1;
                }
                prev_digit = next_digit;
            }
            if increasing && double_1 {
                num_1 += 1;
            }
            if increasing && (double_2 || run_length == 2) {
                num_2 += 1;
            }
        }
    
        println!("Number of passwords (Part 1): {}", num_1);
        println!("Number of passwords (Part 2): {}", num_2);
    }
    
    2 votes
  8. nothis
    (edited )
    Link
    This one felt significantly easier. Maybe because I was actually trying to concentrate and doing it fast (seeing the top-100 swoosh by, of course). The main difficulty seems to have been getting...

    This one felt significantly easier. Maybe because I was actually trying to concentrate and doing it fast (seeing the top-100 swoosh by, of course). The main difficulty seems to have been getting the digits but it's almost a programming exercise cliche.

    I hard-coded almost all of it, expecting it to bite me in the ass for the second star (varying number of digits, etc) but it seems for the second star just copy-pasting it manually was also the fastest way. Anyway, couldn't help but clean it up a tiny little bit.

    Part 1
    const getPossiblesCount = (min, max) => {
      let count = 0;
    
      for (let i = min; i <= max; i++) {
        const digits = getDigits(i);
        let doubles = false;
        let increasing = true;
    
        for (let d = 0; d < digits.length - 1; d++) {
          if (digits[d] === digits[d + 1]) {
            doubles = true;
          }
          if (digits[d] > digits[d + 1]) {
            increasing = false;
          }
        }
    
        if (doubles && increasing) {
          count++;
        }
      }
    
      return count;
    };
    
    const getDigits = number => {
      let digits = [];
    
      for (let d = 100000; d >= 1; d = d / 10) {
        let digit = Math.floor(number / d);
        digits.push(digit);
        number -= digit * d;
      }
    
      return digits;
    };
    
    console.log(getPossiblesCount(130254, 678275));
    
    Part 2
    const getPossiblesCount = (min, max) => {
      let count = 0;
    
      for (let i = min; i <= max; i++) {
        const digits = getDigits(i);
        let doubles = false;
        let increasing = true;
    
        for (let d = 0; d < digits.length - 1; d++) {
          if (digits[d] === digits[d + 1]) {
            if (
              (d === 0 && digits[d + 1] !== digits[d + 2]) ||
              (d === digits.length - 1 &&
                digits[digits.length - 3] !== digits[digits.length - 2]) ||
              (digits[d - 1] !== digits[d] && digits[d + 1] !== digits[d + 2])
            ) {
              doubles = true;
            }
          }
          if (digits[d] > digits[d + 1]) {
            increasing = false;
          }
        }
    
        if (doubles && increasing) {
          count++;
        }
      }
    
      return count;
    };
    
    const getDigits = number => {
      let digits = [];
    
      for (let d = 100000; d >= 1; d = d / 10) {
        let digit = Math.floor(number / d);
        digits.push(digit);
        number -= digit * d;
      }
    
      return digits;
    };
    
    console.log(getPossiblesCount(130254, 678275));
    

    Part of me likes how simple these exercises are (especially that the outcome is a single number so you don't have to fiddle with formatting), part of me would love there to be a higher emphasis on optimization challenges (though that would limit the amount of possible languages you can use). I heard this will get harder so I assume the later ones might simply run too long if you don't optimize?

    1 vote
  9. Crespyl
    Link
    After days 2 and 3 it's nice to see a simpler one again to take a breather (even as excited as I am to revisit the VM puzzle). Part 1, Crystal/Ruby #!/usr/bin/env crystal RANGE_MIN = 171309...

    After days 2 and 3 it's nice to see a simpler one again to take a breather (even as excited as I am to revisit the VM puzzle).

    Part 1, Crystal/Ruby
    #!/usr/bin/env crystal
    
    RANGE_MIN = 171309
    RANGE_MAX = 643603
    
    def check_digits(n)
      n.to_s.size == 6
    end
    
    def check_range(n)
      n >= RANGE_MIN && n <= RANGE_MAX
    end
    
    def check_adjacency(n)
      result = false
      n.to_s.chars.reduce('0') { |prev, char|
        if prev == char
          result = true
        end
        char
      }
      return result
    end
    
    def check_increase(n)
      result = true
      n.to_s.chars.reduce('0') { |prev, char|
        if prev.to_i > char.to_i
          result = false
        end
        char
      }
      return result
    end
    
    def check(n)
      check_digits(n) && check_range(n) && check_increase(n) && check_adjacency(n)
    end
    
    matches = 0
    (RANGE_MIN..RANGE_MAX).each do |i|
      matches += 1 if check(i)
    end
    puts matches
    
    Part 2

    Same as before, except I refactored the check_adjacency bit with an improved version

    def check_adjacency_p2(n)
      n.to_s.chars.group_by { |c| c }.any? { |k,v| v.size == 2 }
    end
    
    1 vote
  10. Hvv
    Link
    I think that my code always tends towards "dumpster fire that also happens to be correct" so in the future I will probably try to focus on good code beyond strict correctness. Part 2 is included...

    I think that my code always tends towards "dumpster fire that also happens to be correct" so in the future I will probably try to focus on good code beyond strict correctness.

    Part 2 is included as a module in the code, since the problem makes it very easy to add Part 2 to Part 1.

    Parts 1/2, C++
    #include <bits/stdc++.h>
    using namespace std;
    //Manually checks all possible numbers in the given range
    //Complexity O(n 10^n) where n is the number of digits
    //Luckily n = 6, so this is reasonably fast
    //Part 2 is quite modular, so it's also included in this code
    
    //Check A:
    //Are the digits nondecreasing left to right, or equivalently, nonincreasing right to left?
    bool cha(int a) {
    	int val = 10;
    //Dirty code note: an int will cast to false if zero and to true if nonzero
    //That might be false but is almost surely true for nonnegative ints
    	while(a) {
    		if(a%10 > val) return false;
    		val = a%10;
    		a /= 10;
    	}
    	return true;
    }
    //Check B:
    //Is there some consecutive pair of digits that are the same?
    bool chb(int a) {
    	int ld = -1;
    	while(a) {
    		if(ld == a%10) return true;
    		ld = a%10;
    		a /= 10;
    	}
    	return false;
    }
    //Check C (stricter check than Check B):
    //Is there a streak of exactly two digits in the (6-digit) number?
    bool chc(int a) {
    	int ld = -1;
    	int ct = 0;
    	while(a) {
    		if(ld == a%10) {
    			ct++;
    		} else {
    			if(ct == 1) return true;
    			ct = 0;
    		}
    		ld = a%10;
    		a /= 10;
    	}
    	return ct == 1;
    }
    //The 6 digit check is automatically fulfilled by the bounds
    int main() {
    	int l,r;
    	//scanf will happily take the input as is and crunch them into the right format
    	scanf("%d-%d",&l,&r);
    	//Count all the valid numbers from the left bound to the right bound
    	int ctr = 0;
    	//It doesn't say whether those bounds are inclusive, but luckily it doesn't matter
    	for(int i=l;i<=r;i++) {
    		if(cha(i) && chb(i)) {				//Part 1 solution implementation
    		//if(cha(i) && chc(i)) {			//Part 2 solution implementation
    				ctr++;
    		}
    	}
    	cout << ctr << '\n';
    }
    
    1 vote
  11. Gyrfalcon
    Link
    This one was pretty easy on the first part, but the second part took some thinking (and guessing) to get right. Things started to fall into place once I realized it would be more readable and...

    This one was pretty easy on the first part, but the second part took some thinking (and guessing) to get right. Things started to fall into place once I realized it would be more readable and easier to understand if I split some of my increasingly ugly conditional expressions into multiple if statements. I've added comments this time, but I may have overcompensated for my past uncommented spaghetti.

    I am also a little miffed that these release right at my bed time. It's tough to make it on the leaderboard when I often don't have a chance to really work on them until the following afternoon!

    Parts 1&2, Python 3
    # Just reads in the data and gets it formatted right
    def read_range(file_name):
    
        with open(file_name, 'r') as fid:
            code_range = fid.readline().strip().split('-')
    
        for idx in range(len(code_range)):
            code_range[idx] = int(code_range[idx])
    
        return code_range
    
    # Does ths simple validation routine for part one
    def validate_code(code):
    
        # Get our code as a list of integers
        code = [int(num) for num in str(code)]
    
        # Make sure they are strictly ascending
        for idx in range(len(code) - 1):
            if code[idx] > code[idx + 1]:
                return 0 # Code is invalid
    
        # Check if there are any repeats
        for idx in range(len(code) - 1):
            if code[idx] == code[idx + 1]:
                return 1 # Code is valid
    
        # If there's no repeats, the code is invalid
        return 0
    
    # Does the more complex validation routine for part 2
    def advanced_validate_code(code):
    
        # Get our code as a list of integers
        code = [int(num) for num in str(code)]
    
        # Make sure they are strictly ascending
        for idx in range(len(code) - 1):
            if code[idx] > code[idx + 1]:
                return 0 # Code is invalid
    
        # Check for repeats, not including 3+
        for idx in range(len(code) - 1):
            # If you arent right at the end and it isn't a 3 going forward
            if idx < len(code) - 2 and (code[idx] == code[idx + 1]
                                        and code[idx+1] != code[idx+2]):
                # If it's right at the beginning it can't have a 3 going backward
                if  idx == 0:
                    return 1 # Code is valid
                # Otherwise check backward to be sure
                elif code[idx] != code[idx - 1]:
                    return 1 # Code is valid
            # Check that if it's at the very end, there's no 3 going backward
            elif idx == (len(code) - 2) and code[idx] == code[idx + 1]:
    
                if idx >= 1 and code[idx] != code[idx - 1]:
                    return 1 # Code is valid
        # If there's no repeats without 3+, then the code is invalid
        return 0
    
    # Applies the simple validation function over a range defined in a file and sums
    # up the number of valid codes
    def validate_range(file_name):
    
        code_range = read_range(file_name)
    
        num_valid = 0
        for code in range(code_range[0], code_range[1] + 1):
            num_valid += validate_code(code)
    
        return num_valid
    
    # Applies the advanced validation function over a range defined in a file and
    # sums up the number of valid codes
    def advanced_validate_range(file_name):
    
        code_range = read_range(file_name)
    
        num_valid = 0
        for code in range(code_range[0], code_range[1] + 1):
            num_valid += advanced_validate_code(code)
    
        return num_valid
    
    input_file = "PuzzleInput.txt"
    
    print(validate_range(input_file))
    
    print(advanced_validate_range(input_file))
    
    1 vote