16 votes

Day 4: Camp Cleanup

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

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>

31 comments

  1. tjf
    Link
    My Python solutions. Just like yesterday, sets are perfect here. Part 1 #!/usr/bin/env pypy3 import sys def main(): pairs = ([set(range(a, b + 1)) for a, b in c] for c in (([*map(int,...

    My Python solutions. Just like yesterday, sets are perfect here.

    Part 1
    #!/usr/bin/env pypy3
    
    import sys
    
    def main():
        pairs = ([set(range(a, b + 1)) for a, b in c] for c in
                 (([*map(int, d.split('-'))] for d in line.strip().split(','))
                  for line in sys.stdin))
        overlaps = (a.issubset(b) or b.issubset(a) for a, b in pairs)
        print(sum(overlaps))
    
    if __name__ == '__main__':
        main()
    
    Part 2
    #!/usr/bin/env pypy3
    
    import sys
    
    def main():
        pairs = ([set(range(a, b + 1)) for a, b in c] for c in
                 (([*map(int, d.split('-'))] for d in line.strip().split(','))
                  for line in sys.stdin))
        overlaps = (a & b != set() for a, b in pairs)
        print(sum(overlaps))
    
    if __name__ == '__main__':
        main()
    
    8 votes
  2. akk
    (edited )
    Link
    Pretty similar to yesterday if you ask me. Still love Swift! Part 1 and 2 import Foundation var totalFullSubRanges: Int = 0 var totalAnySubRanges: Int = 0 while let line = readLine() { let...

    Pretty similar to yesterday if you ask me. Still love Swift!

    Part 1 and 2
    import Foundation
    
    var totalFullSubRanges: Int = 0
    var totalAnySubRanges: Int = 0
    
    while let line = readLine() {
        let sectionRanges = line.components(separatedBy: ",").map { $0.components(separatedBy: "-").map { Int($0) ?? -1 } }
        let rangeOne = Set(Array(sectionRanges[0][0]...sectionRanges[0][1]))
        let rangeTwo = Set(Array(sectionRanges[1][0]...sectionRanges[1][1]))
        if rangeOne.isSubset(of: rangeTwo) || rangeTwo.isSubset(of: rangeOne) {
            totalFullSubRanges += 1
        }
        
        if !rangeOne.intersection(rangeTwo).isEmpty {
            totalAnySubRanges += 1
        }
    }
    
    print(totalFullSubRanges)
    print(totalAnySubRanges)
    
    5 votes
  3. [4]
    primordial-soup
    Link
    advent of code is the only time that i write dynamically typed code these days. and lo and behold! 2/3rds of the time i took for part 1 was spent because i forgot to convert strings to ints and...

    advent of code is the only time that i write dynamically typed code these days. and lo and behold! 2/3rds of the time i took for part 1 was spent because i forgot to convert strings to ints and didn't realize what was going wrong :')

    Part 1, in Python-ish
    (ls
     > fe(X.split(",") | fe(X.split("-") | fe(int) | tuple)
          | permutations | fe(as_args(λ a, b: a[0] <= b[0] <= b[1] <= a[1])) | any)
     | sum
    )
    
    Python code generated from the above
    from itertools import permutations
    from pipetools import X
    from pipetools import as_args
    from pipetools import foreach
    import sys
    from pyp import pypprint
    fe = foreach
    lines = [x.rstrip('\n') for x in sys.stdin]
    ls = lines
    output = ls > fe(X.split(',') | fe(X.split('-') | fe(int) | tuple) | permutations | fe(as_args(lambda a, b: a[0] <= b[0] <= b[1] <= a[1])) | any) | sum
    if output is not None:
        pypprint(output)
    
    Part 2, in Python-ish
    (ls
     > fe(X.split(",") | fe(X.split("-") | fe(int) | tuple)
          | permutations | fe(as_args(λ a, b: a[0] <= b[0] <= a[1])) | any)
     | sum
    )
    
    Python code generated from the above
    from itertools import permutations
    from pipetools import X
    from pipetools import as_args
    from pipetools import foreach
    import sys
    from pyp import pypprint
    fe = foreach
    lines = [x.rstrip('\n') for x in sys.stdin]
    ls = lines
    output = ls > fe(X.split(',') | fe(X.split('-') | fe(int) | tuple) | permutations | fe(as_args(lambda a, b: a[0] <= b[0] <= a[1])) | any) | sum
    if output is not None:
        pypprint(output)
    
    5 votes
    1. [2]
      bhrgunatha
      Link Parent
      Python-ish looks interesting. I could only find a github compiler to generate C++ from a python subset. Is it a pre-processor, interpreter, compiler? Something else?

      Python-ish looks interesting. I could only find a github compiler to generate C++ from a python subset.

      Is it a pre-processor, interpreter, compiler? Something else?

      3 votes
      1. primordial-soup
        (edited )
        Link Parent
        it is a little pre-processor i cobbled together. it has two stages: sed 's/λ/lambda/g'. (in the future i'd like to actually extend the grammar of an existing Python implementation instead of using...

        it is a little pre-processor i cobbled together. it has two stages:

        1. sed 's/λ/lambda/g'. (in the future i'd like to actually extend the grammar of an existing Python implementation instead of using this hack to mimic an extended grammar.)

        2. pyp with a pyprc.py that is (for the most part)

          `pyprc.py`
          import plumbum.cmd as cmd
          from plumbum import TF
          from functools import *
          from itertools import *
          from operator import *
          from pipetools import *
          stdout = sys.stdout
          p = pipe
          fe = foreach
          fe_do = foreach_do
          Xp = xpartial
          from more_itertools import *
          import numpy as np
          
          ls = lines
          # string input parsing
          n = int(l)
          ns = lines > foreach(int) | list
          y = float(l)
          ys = lines > foreach(float) | list
          f = l.split()
          fs = lines > foreach(X.split()) | list
          

        much of the cool stuff happening is actually not done by preprocessing—it's pipetools's pure-Python tricks with heavy usage of (more_)?itertools on top.

        3 votes
    2. ras
      Link Parent
      Same story here.

      Same story here.

      1 vote
  4. soks_n_sandals
    Link
    Today was way easier in bash than yesterday. I made a mistake in part 1 (see spoiler) and tried to get too clever in part 2. A simple solution was good enough. Part 1+2 In part 1, I missed the...

    Today was way easier in bash than yesterday. I made a mistake in part 1 (see spoiler) and tried to get too clever in part 2. A simple solution was good enough.

    Part 1+2 In part 1, I missed the 'continue' command and some overlaps were double counted. Adding it got the right answer.
    #!/usr/bin/env bash
    
    file=day4.dat
    part=2
    
    if [[ ${part} -eq 1 ]]; then
    # /////////////////////////////////////////////////////////////////////////////
    #part 1 0.55s // 463
    sed 's/-/ /g' $file > $file.tmp
    sum=0
    while IFS=, read -r range1 range2 || [[ -n ${range1} ]]; do
        ranges=($(echo $range1 $range2))
        if [[ ${ranges[2]} -le ${ranges[0]} && ${ranges[3]} -ge ${ranges[1]} ]]; then
            ((sum = sum + 1))
            continue #mandatory to make sure we don't double count somehow
        fi
    
        if [[ ${ranges[0]} -le ${ranges[2]} && ${ranges[1]} -ge ${ranges[3]}  ]]; then
            ((sum = sum + 1))
            continue
        fi
    done < $file.tmp
    
    echo Part 1: $sum
    # /////////////////////////////////////////////////////////////////////////////
    fi
    
    if [[ ${part} -eq 2 ]]; then
    # /////////////////////////////////////////////////////////////////////////////
    #part 2  0.54s // 919
    sed 's/-/ /g' $file > $file.tmp
    sum=0
    while IFS=, read -r range1 range2 || [[ -n ${range1} ]]; do
        ranges=($(echo $range1 $range2))
        if [[ ${ranges[2]} -le ${ranges[0]} && ${ranges[0]} -le ${ranges[3]} ]]; then
            # echo overlap
            # echo ${ranges[@]}
            ((sum=sum+1))
            continue
        fi
    
    
        if [[ ${ranges[0]} -le ${ranges[2]} && ${ranges[2]} -le ${ranges[1]} ]]; then
            # echo overlap
            # echo ${ranges[@]}
            ((sum=sum+1))
            continue
        fi
    
    
    done < $file.tmp
    
    echo Part 2: $sum
    # /////////////////////////////////////////////////////////////////////////////
    fi
    
    rm $file.tmp
    
    4 votes
  5. [4]
    bhrgunatha
    Link
    I'm really annoyed at my performance today, but I'm always too hard on myself. I was delayed about ½ hour and in my haste to grab those sweet, sweet leaderboard points, made so many fundamental...

    I'm really annoyed at my performance today, but I'm always too hard on myself.
    I was delayed about ½ hour and in my haste to grab those sweet, sweet leaderboard points, made so many fundamental and stupid mistakes I feel ashamed at myself, leading to annoyance, bordering anger. :(

    Request: Since I've mistyped it about 100,00 times...

    Note mina,minb not min-efla/b etc..
    ...does anyone have a synonym for efl?
    Part 1
    (define (part-01 input)
      (for/sum ([elves (in-list input)]
                #:when (contained? (assignments elves)))
        1))
    
    (define (assignments elves)
      (map string->number (regexp-match* #px"\\d+" elves)))
    
    (define (contained? elves)
      (match-define (list mina maxa minb maxb) elves)
      (or (and (<= mina minb) (>= maxa maxb))
          (and (<= minb mina) (>= maxb maxa))))
    
    Part 2
    (define (part-02 input)
      (for/sum ([elves (in-list input)]
                #:when (overlap? (assignments elves)))
        1))
    
    (define (overlap? elves)
      (match-define (list mina maxa minb maxb) elves)
      (or (and (>= maxa minb) (>= maxb mina))
          (and (>= maxb mina) (>= maxa minb))))
    
    Speedrun ta;wf Too annoyed, won't fix

    Oh well, there's always tomorrow :)

    4 votes
    1. [3]
      thorondir
      Link Parent
      This, I feel, went much better than day 03. Part 1 ;; function to make input usable (define (sanitize-input input) (for/list ([l (string-split input "\n")]) (parse-values l))) ;; take the small...

      This, I feel, went much better than day 03.

      Part 1
      ;; function to make input usable
      (define (sanitize-input input)
        (for/list ([l (string-split input "\n")])
          (parse-values l)))
        
      
      ;; take the small input from the text
      (define raw-small-input (file->string "small-input_04"))
      (define small-answer-1 2)
      (define small-input (sanitize-input raw-small-input))
      
      
      
      (define (process-line line)
        (define l (first line))
        (define r (second line))
        (if (or (and (<= (first l) (first r))
                     (>= (second l) (second r)))
                (and (>= (first l) (first r))
                     (<= (second l) (second r))))
            #t
            #f))
      
      ;; workhorse, part 1
      (define (part-1 input)
        (for/fold ([acc 0]
                   #:result acc)
                  ([l input])
          (if (process-line l)
              (add1 acc)
              acc)))
      

      And I'm quite proud of the overlap function. xD

      Part 2
      (define (overlap? line)
        (define l (first line))
        (define r (second line))
        (if (or (< (second l) (first r))
                (> (first l) (second r)))
            #f
            #t))
      
      ;; part 2
      (define small-answer-2 4)
      
      (define (part-2 input)
        (for/fold ([acc 0]
                   #:result acc)
                  ([l input])
          (if (overlap? l)
              (add1 acc)
              acc)))
      

      I know the destructuring in process-line and overlap? aren't necessary, but it was easier to think about, this way. :D

      3 votes
      1. [2]
        bhrgunatha
        (edited )
        Link Parent
        You can simplify de-structuring of lists (and structs and others) with match-define (define a (first <alist>)) (define b (second <alist>) (define c (second <alist>) (match-define (list a b c)...

        You can simplify de-structuring of lists (and structs and others) with match-define

        (define a (first <alist>))
        (define b (second <alist>)
        (define c (second <alist>)

        (match-define (list a b c) <alist>)

        you can go even further

        (match-define (list (list a1 a2) (list a3 a4)) '((1 2) (3 4)))

        matching is Racket is very powerful.

        One thing I would point out is that in Racket, the only false value is the boolean value #f, sometimes written #false. Every other value is logically #t. So in conditions null, 0, -1, "", ... even the void value returned by calling e.g. (vector-set! v 1 7) is considered logically #t.

        That means writing (if <expression> #t #f) is redundant; it's logically identical to <expression>. Whatever is using that (and expecting a #t or #f value) will respond exactly the same when you just return <expression> by itself.

        If you want the opposite, (if <expression> #f #t), use (not <expression>).

        Of course it's your decision, and it doesn't mean you should never return a bare #t or #f - I'll often do that with a complex cond with several cases, but it's never needed with if.

        3 votes
        1. thorondir
          Link Parent
          That's very helpful, thank you!

          That's very helpful, thank you!

          3 votes
  6. [3]
    Crestwave
    Link
    Easier one than yesterday for those of us without sets. Tiny AWK solutions: Part 1 #!/usr/bin/awk -f BEGIN { FS = "[,-]" } { sum += (($1 >= $3 && $2 <= $4) || ($3 >= $1 && $4 <= $2)) } END {...

    Easier one than yesterday for those of us without sets. Tiny AWK solutions:

    Part 1
    #!/usr/bin/awk -f
    BEGIN { FS = "[,-]" }
    { sum += (($1 >= $3 && $2 <= $4) || ($3 >= $1 && $4 <= $2)) }
    END { print(sum) }
    
    Part 2
    #!/usr/bin/awk -f
    BEGIN { FS = "[,-]" }
    { sum += ($1 <= $4 && $3 <= $2) }
    END { print(sum) }
    
    4 votes
    1. [2]
      bhrgunatha
      Link Parent
      feeling incredibly stupid now looking at my test for overlaps... which translated would be (($1 <= $4 && $3 <= $2) || ($3 <= $2 && $1 <= $4))

      feeling incredibly stupid now looking at my test for overlaps...

      which translated would be
      (($1 <= $4 && $3 <= $2) || ($3 <= $2 && $1 <= $4))

      2 votes
      1. Crestwave
        Link Parent
        Nah I had something even longer at first. I just took the time afterwards to distill it to a simpler form. :P

        Nah I had something even longer at first. I just took the time afterwards to distill it to a simpler form. :P

        1 vote
  7. asterisk
    (edited )
    Link
    Python import re def cleanup(full: bool = True) -> int: overlap: int = 0 for line in open("input.txt"): a, b, c, d = map(int, re.findall(r"\d+", line)) first = set(range(a, b + 1)) second =...
    Python
    import re
    
    
    def cleanup(full: bool = True) -> int:
        overlap: int = 0
    
        for line in open("input.txt"):
            a, b, c, d = map(int, re.findall(r"\d+", line))
            first = set(range(a, b + 1))
            second = set(range(c, d + 1))
    
            if first.issubset(second) or second.issubset(first) if full else first.intersection(second):
                overlap += 1
    
        return overlap
    
    
    print(cleanup())  # Part One: 498
    print(cleanup(False))  # Part Two: 859
    
    Updated
    -        if full and (first.issubset(second) or second.issubset(first)) or not full and first.intersection(second):
    +        if first.issubset(second) or second.issubset(first) if full else first.intersection(second):
    
    3 votes
  8. jzimbel
    Link
    Elixir Both parts defmodule AdventOfCode.Solution.Year2022.Day04 do def part1(input) do input |> String.split("\n", trim: true) |> Enum.map(&parse_ints/1) |> Enum.count(&superset_subset?/1) end...

    Elixir

    Both parts
    defmodule AdventOfCode.Solution.Year2022.Day04 do
      def part1(input) do
        input
        |> String.split("\n", trim: true)
        |> Enum.map(&parse_ints/1)
        |> Enum.count(&superset_subset?/1)
      end
    
      def part2(input) do
        input
        |> String.split("\n", trim: true)
        |> Enum.map(&parse_ints/1)
        |> Enum.count(&overlap?/1)
      end
    
      defp parse_ints(line) do
        ~r/^(\d+)-(\d+),(\d+)-(\d+)$/
        |> Regex.run(line, capture: :all_but_first)
        |> Enum.map(&String.to_integer/1)
      end
    
      defp superset_subset?([a, b, x, y]) do
        (a <= x and b >= y) or (a >= x and b <= y)
      end
    
      defp overlap?([a, b, x, y]) do
        not Range.disjoint?(a..b, x..y)
      end
    end
    
    3 votes
  9. Eabryt
    Link
    Definitely could have done this better if I had some more knowledge, which thanks to other solutions I have now! So hopefully that will help in future days, but for now you're stuck with this...

    Definitely could have done this better if I had some more knowledge, which thanks to other solutions I have now! So hopefully that will help in future days, but for now you're stuck with this slightly messy code

    Parts 1 & 2
    def part1(lines):
        print(f"Part 1!")
        numTimes = 0
        for line in lines:
            first, second = line.split(',')
            firstA = list(range(int(first.split('-')[0]), int(first.split('-')[1])+1))
            secondA = list(range(int(second.split('-')[0]), int(second.split('-')[1])+1))
            if set(firstA).issubset(set(secondA)) or set(secondA).issubset(set(firstA)):
                numTimes += 1
        print(f"Result: {numTimes}")
    
    
    def part2(lines):
        print(f"Part 2!")
        numTimes = 0
        for line in lines:
            first, second = line.split(',')
            fA = list(range(int(first.split('-')[0]), int(first.split('-')[1])+1))
            sA = list(range(int(second.split('-')[0]), int(second.split('-')[1])+1))
            if any(i in fA for i in sA):
                numTimes += 1
        print(f"Result: {numTimes}")
    
    
    def openFile():
        return open("input.txt", "r").readlines()
    
    
    def main():
        f = openFile()
        part1(f)
        part2(f)
    
    
    if __name__ == '__main__':
        main()
    
    3 votes
  10. tomf
    (edited )
    Link
    Google Sheets! EDIT! I am changing my answer. this took me forever -- and it all boiled down to me not converting REGEX outputs to values. Anyway, this is a few characters lighter anyway... Part 1...

    Google Sheets!

    EDIT! I am changing my answer.

    this took me forever -- and it all boiled down to me not converting REGEX outputs to values. Anyway, this is a few characters lighter anyway...

    Part 1
    =ARRAYFORMULA(
      IF(C2=FALSE,"Part 1",
       QUERY(
        SPLIT(A2:A,"-,"),
        "select Count(Col1)
         where 
          Col1 is not null and
           (Col1 <= Col3 and Col2 >= Col4) or
           (Col3 <= Col1 and Col4 >= Col2)
         label Count(Col1) 'Part 1'")))
    
    Part 2
    =ARRAYFORMULA(
      QUERY(
       SPLIT(A2:A,"-,"),
       "select Count(Col1)
        where Col1 is not null and
         (Col1 <= Col4 and
          Col3 <= Col2)
        label Count(Col1) 'Part 2'"))
    
    2 votes
  11. [2]
    wycy
    Link
    Rust Rust use std::env; use std::io::{self, prelude::*, BufReader}; use std::fs::File; use std::ops::RangeInclusive; extern crate regex; use regex::Regex; struct Team { elf1:...

    Rust

    Rust
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    use std::ops::RangeInclusive;
    
    extern crate regex;
    use regex::Regex;
    
    struct Team {
        elf1: RangeInclusive<usize>,
        elf2: RangeInclusive<usize>,
    }
    impl Team {
        pub fn has_overlaps1(&self) -> bool {
            (self.elf1.contains(&self.elf2.start()) && self.elf1.contains(&self.elf2.end())) ||
            (self.elf2.contains(&self.elf1.start()) && self.elf2.contains(&self.elf1.end()))
        }
        pub fn has_overlaps2(&self) -> bool {
            self.elf1.contains(&self.elf2.start()) || self.elf1.contains(&self.elf2.end()) ||
            self.elf2.contains(&self.elf1.start()) || self.elf2.contains(&self.elf1.end())
        }
    }
    impl From<&String> for Team {
        fn from(s: &String) -> Self {
            let re = Regex::new(r"(\d+)\-(\d+),(\d+)\-(\d+)").unwrap();
            let matches = re.captures(s).unwrap();
            Self {
                elf1: RangeInclusive::new(matches[1].parse().unwrap(),matches[2].parse().unwrap()),
                elf2: RangeInclusive::new(matches[3].parse().unwrap(),matches[4].parse().unwrap()),
            }
        }
    }
    
    fn solve(input: &str) -> io::Result<()> {
        let file = File::open(input).expect("Input file not found.");
        let reader = BufReader::new(file);
    
        // Input
        let input: Vec<String> = match reader.lines().collect() {
            Err(err) => panic!("Unknown error reading input: {}", err),
            Ok(result) => result,
        };
    
        // Part 1
        let part1 = input
            .iter()
            .map(Team::from)
            .filter(|x| x.has_overlaps1())
            .count();
        println!("Part 1: {:?}", part1); // 450
    
        // Part 2
        let part2 = input
            .iter()
            .map(Team::from)
            .filter(|x| x.has_overlaps2())
            .count();
        println!("Part 2: {:?}", part2); // 837
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    2 votes
    1. Toric
      Link Parent
      I ended up writing my own Range implementation, didnt think to look in std for one.

      I ended up writing my own Range implementation, didnt think to look in std for one.

      3 votes
  12. [3]
    kari
    Link
    I struggled with trying to convert each elf's assignment to a set. Nim is confusing... Nim (both parts) import std/[sets, sequtils, strutils, sugar] proc assignmentToSet(assignment: string):...

    I struggled with trying to convert each elf's assignment to a set. Nim is confusing...

    Nim (both parts)
    import std/[sets, sequtils, strutils, sugar]
    
    proc assignmentToSet(assignment: string): HashSet[int] =
      # This is janky as hell... I'm so sorry, nim.
      let assignmentSet = collect:
        let intEnds = collect:
          for a in assignment.split('-'):
            parseInt(a)
        (intEnds[0]..intEnds[1]).toSeq().toHashSet()
      result = assignmentSet[0]
    
    proc day04*() =
      var 
        numProperSubsets = 0 
        numAnySubsets = 0
    
      for line in lines("inputs/day04.in"):
        let pairAssignments = line.split(',')
        let assignment1 = assignmentToSet(pairAssignments[0])
        let assignment2 = assignmentToSet(pairAssignments[1])
    
        let intersection = assignment1 * assignment2
    
        # Part 1
        if intersection == assignment1 or intersection == assignment2:
          numProperSubsets += 1
    
        # Part 2
        if intersection.len > 0:
          numAnySubsets += 1
    
      echo "Part 1: " & $numProperSubsets
      echo "Part 2: " & $numAnySubsets
    
    2 votes
    1. [2]
      petrichor
      Link Parent
      I didn't really use sets (did <= and if x in a..b instead) but here's a bit of a cleaner procedure: assignmentToSet proc assignmentToSet(assignment: string): HashSet[int] = let intEnds = collect:...

      I didn't really use sets (did <= and if x in a..b instead) but here's a bit of a cleaner procedure:

      assignmentToSet
      proc assignmentToSet(assignment: string): HashSet[int] =
        let intEnds = collect:
          for a in assignment.split('-'):
            parseInt(a)
        return collect(for x in intEnds[0] .. intEnds[1]: {x})
      
      2 votes
  13. Toric
    Link
    Decided to start impl on my custom types. Once again, rusts type system is coming in handy. Driver mod part1; mod part2; mod utilities; fn main() { let _input = include_str!("./input.txt"); let...

    Decided to start impl on my custom types. Once again, rusts type system is coming in handy.

    Driver
    mod part1;
    mod part2;
    mod utilities;
    
    fn main() {
        let _input = include_str!("./input.txt");
        let _structured_input = utilities::parse(_input);
    
        println!("Part One");
        println!("Result: {}", part1::part1(&_structured_input));
    
        println!("Part Two");
        println!("Result: {}", part2::part2(&_structured_input));
    }
    
    Utils
    use once_cell::sync::Lazy;
    use regex::Regex;
    #[derive(Debug, PartialEq, Eq)]
    pub struct Range {
        start: u16,
        end: u16,
    }
    
    impl Range {
        pub fn new(start: u16, end: u16) -> Self {
            Self {
                start: start.min(end),
                end: end.max(start),
            }
        }
    
        pub fn calc_size(&self) -> u16 {
            self.start.abs_diff(self.end)
        }
    
        pub fn any_overlap(&self, other: &Self) -> bool {
            self.start <= other.end && self.end >= other.start
        }
    
        pub fn calc_overlap(&self, other: &Self) -> Range {
            let overlap_start = self.start.min(other.start);
            let overlap_end = self.end.max(other.end);
            Range::new(overlap_start, overlap_end)
        }
    
        pub fn complete_overlap(&self, other: &Self) -> bool {
            self.calc_overlap(other) == *self || self.calc_overlap(other) == *other
        }
    
        pub fn start(&self) -> u16 {
            self.start
        }
    
        pub fn end(&self) -> u16 {
            self.end
        }
    }
    
    static PARSE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\d+)-(\d+),(\d+)-(\d+)").unwrap());
    
    pub fn parse(input: &str) -> Vec<(Range, Range)> {
        input
            .lines()
            .map(|line| {
                let cap = PARSE_REGEX.captures(line).unwrap();
                (
                    Range::new(
                        cap.get(1).unwrap().as_str().parse().unwrap(),
                        cap.get(2).unwrap().as_str().parse().unwrap(),
                    ),
                    Range::new(
                        cap.get(3).unwrap().as_str().parse().unwrap(),
                        cap.get(4).unwrap().as_str().parse().unwrap(),
                    ),
                )
            })
            .collect()
    }
    
    Part 1
    use crate::utilities::*;
    
    pub fn part1(input: &[(Range, Range)]) -> usize {
        input
            .iter()
            .filter(|tuple| tuple.0.complete_overlap(&tuple.1))
            .count()
    }
    
    Part 2
    use crate::utilities::*;
    
    pub fn part2(input: &[(Range, Range)]) -> usize {
        input
            .iter()
            .filter(|tuple| tuple.0.any_overlap(&tuple.1))
            .count()
    }
    
    2 votes
  14. Gyrfalcon
    Link
    When I first read the problem description, I went to sets, just like I did yesterday and like it seems like many people did here. Then, I got worried that this was a trick, and that I was going to...

    When I first read the problem description, I went to sets, just like I did yesterday and like it seems like many people did here. Then, I got worried that this was a trick, and that I was going to create some kind of monstrosity with huge sets if I went that path, so I just went with keeping the high and low of each assignment and having to think about the conditionals for overlapping, which tripped me up for a little bit on part 2.

    Parts 1 and 2
    def create_assignments(pair: str) -> Tuple[Tuple[int, int], Tuple[int, int]]:
        first, second = pair.split(",")
        first_low = int(first.split("-")[0])
        first_high = int(first.split("-")[1])
        second_low = int(second.split("-")[0])
        second_high = int(second.split("-")[1])
        return ((first_low, first_high), (second_low, second_high))
    
    
    def are_completely_overlapping(
        assignments: Tuple[Tuple[int, int], Tuple[int, int]]
    ) -> bool:
        return (
            assignments[0][0] <= assignments[1][0]
            and assignments[0][1] >= assignments[1][1]
        ) or (
            assignments[1][0] <= assignments[0][0]
            and assignments[1][1] >= assignments[0][1]
        )   
    
    
    def are_overlapping(assignments: Tuple[Tuple[int, int], Tuple[int, int]]) -> bool:
        return (
            assignments[0][1] >= assignments[1][0]
            and assignments[0][0] <= assignments[1][1]
        )   
    
    
    def main(filepath: str) -> Tuple[int, int]:
        pairs = load_file.load_cleaned_lines(filepath)
        assignments = [create_assignments(pair) for pair in pairs]
        return (
            sum((are_completely_overlapping(assignment) for assignment in assignments)),
            sum((are_overlapping(assignment) for assignment in assignments)),
        ) 
    
    1 vote
  15. Crespyl
    Link
    More sets! I did wonder if maybe there'd be some trick to part 2 that would mean using sets would blow up somehow, but figured that we're still only on day 4. Clojure (both parts) (defn parse-line...

    More sets!

    I did wonder if maybe there'd be some trick to part 2 that would mean using sets
    would blow up somehow, but figured that we're still only on day 4.

    Clojure (both parts)
    (defn parse-line [line]
      (let [[[g1-start g1-end] [g2-start g2-end]]
            (->> line
                 (re-matches #"(\d+)-(\d+),(\d+)-(\d+)")
                 (rest)
                 (map parse-long)
                 (partition 2))
            group1 (set (range g1-start (inc g1-end)))
            group2 (set (range g2-start (inc g2-end)))]
        [group1, group2]))
    
    (defn subset-or-superset [set1 set2]
      (or (clojure.set/subset? set1 set2)
          (clojure.set/subset? set2 set1)))
    
    (defn overlaps? [set1 set2]
      (not-empty (clojure.set/intersection set1 set2)))
    
    (defn part-1 [input]
      (let [set-pairs (map parse-line input)]
        (->> set-pairs
             (map #(apply subset-or-superset %))
             (filter true?)
             (count))))
    
    (defn part-2 [input]
      (let [set-pairs (map parse-line input)]
        (->> set-pairs
             (map #(apply overlaps? %))
             (filter #(not (nil? %)))
             (count))))
    
    1 vote
  16. balooga
    Link
    Here's my TypeScript solution type InputData = [[number, number], [number, number]][]; function formatInput(input: string): InputData { return input.split('\n').map(e => e.split(',').map(f =>...
    Here's my TypeScript solution
    type InputData = [[number, number], [number, number]][];
    
    function formatInput(input: string): InputData {
      return input.split('\n').map(e => e.split(',').map(f => f.split('-').map(g => Number(g)))) as InputData;
    }
    
    export function run(input: string): string[] {
      const data = formatInput(input);
    
    Part 1
      const countFullContainments = (pairs: InputData): number => {
        let count = 0;
        for (const pair of pairs) {
          if (
            (pair[0][0] <= pair[1][0] && pair[0][1] >= pair[1][1]) ||
            (pair[1][0] <= pair[0][0] && pair[1][1] >= pair[0][1])
          ) {
            count++;
          }
        }
        return count;
      };
    
    Part 2

    I got lazy here and decided to count the non-overlapping pairs, and subtract that number from the whole. Gets the job done.

      const countOverlaps = (pairs: InputData): number => {
        let nonOverlapCount = 0;
        for (const pair of pairs) {
          if (pair[0][1] < pair[1][0] || pair[0][0] > pair[1][1]) {
            nonOverlapCount++;
          }
        }
        return pairs.length - nonOverlapCount;
      };
    
      return [`${countFullContainments(data)}`, `${countOverlaps(data)}`];
    }
    
    1 vote
  17. ras
    Link
    JavaScript solution, both parts import { readFileSync } from "fs"; const input = readFileSync("./input.txt", "utf-8").toString(); const groups = input.split("\n"); // Put numbers into an array...
    JavaScript solution, both parts
    import { readFileSync } from "fs";
    
    const input = readFileSync("./input.txt", "utf-8").toString();
    const groups = input.split("\n");
    
    // Put numbers into an array
    const expandNumbers = (start, end) => {
      let numbers = [];
      for (let index = Number(start); index <= Number(end); index++) {
        numbers.push(index);
      }
      return numbers;
    };
    
    // Check if either number contains the other completely
    const containsAll = (left, right) => {
      return (
        left.every((number) => right.includes(number)) ||
        right.every((number) => left.includes(number))
      );
    };
    
    // Check if any of the numbers overlap
    const containsSome = (left, right) => {
      return (
        left.some((number) => right.includes(number)) ||
        right.some((number) => left.includes(number))
      );
    };
    
    let part1 = 0;
    let part2 = 0;
    
    groups.forEach((group) => {
      const [first, second] = group.split(",");
    
      const [firstStart, firstEnd] = first.split("-");
      const [secondStart, secondEnd] = second.split("-");
    
      const firstExpanded = expandNumbers(firstStart, firstEnd);
      const secondExpanded = expandNumbers(secondStart, secondEnd);
    
      if (containsAll(firstExpanded, secondExpanded)) part1++;
      if (containsSome(firstExpanded, secondExpanded)) part2++;
    });
    
    console.log(part1);
    console.log(part2);
    
    1 vote
  18. MeckiSpaghetti
    Link
    Ruby require "active_support/all" sum = File .read("input.txt") .split(/-|,|\s/) .map(&:to_i) .each_slice(4) p1 = sum.count{ |a, b, c, d| (a..b).cover?(c..d) || (c..d).cover?(a..b) } p2 =...
    Ruby
    require "active_support/all"
    
    sum = File
          .read("input.txt")
          .split(/-|,|\s/)
          .map(&:to_i)
          .each_slice(4)
          
    p1 = sum.count{ |a, b, c, d| (a..b).cover?(c..d) || (c..d).cover?(a..b) }
    p2 = sum.count{ |a, b, c, d| (a..b).overlaps?(c..d) }
    
    p p1 # Part 1
    p p2 # Part 2
    
    1 vote
  19. vord
    Link
    Did this back-to-back with Day 3, this one was quite a bit easier IMO. Was able to hit part 2 merely adding a second "if". Typescript Solution import * as fs from 'node:fs/promises'; function...

    Did this back-to-back with Day 3, this one was quite a bit easier IMO. Was able to hit part 2 merely adding a second "if".

    Typescript Solution
    import * as fs from 'node:fs/promises';
    
    function parseNumbers(input: string): number[][] {
    	const pair: string[] = input.split(",");
    	
    	let pair_array: number[][] = [];
    	for (const elf of pair) {
    		pair_array.push(elf.split("-").map((item: string) => parseInt(item)));
    	};
    
    	return pair_array;
    };
    
    async function main(): Promise<void> {
    	const assignments: string[] = (await fs.readFile("input/day4", "utf-8")).split("\n");
    
    	const pairs: number[][][] = assignments.map(
    					(pair: string): number[][] => parseNumbers(pair));
    
    	let fully_contained: number = 0;
    	let overlap: number = 0;
    
    	for (const pair of pairs) {
    		const elf_1_start = pair[0][0];
    		const elf_1_end = pair[0][1];
    		const elf_2_start = pair[1][0];
    		const elf_2_end = pair[1][1];
    
    		// Part 1
    		if (   (elf_1_start <= elf_2_start && elf_1_end >= elf_2_end)
    			|| (elf_2_start <= elf_1_start && elf_2_end >= elf_1_end)) {
    			fully_contained++;
    		};
    		
    		// Part 2
    		if (   (elf_1_start <= elf_2_start && elf_1_end >= elf_2_start)
    			|| (elf_2_start <= elf_1_start && elf_2_end >= elf_1_start)) {
    			overlap++;
    		};
    	}
    
    	// Output
    	console.log("Fully Contained Range: ",fully_contained);
    	console.log("Overlapping Range: ",overlap);
    };
    
    main();
    
    1 vote
  20. whispersilk
    Link
    I'm using Rust this year, and trying to keep it std-only throughout the month. Part 1 use std::error::Error; use std::fs::File; use std::io::Read; fn main() -> Result<(), Box<dyn Error>> { let mut...

    I'm using Rust this year, and trying to keep it std-only throughout the month.

    Part 1
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_4.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let number_containing = contents.lines().fold(0, |acc, line| {
    		let mut pieces = line.splitn(2, ',').map(Range::from_str);
    		let first = pieces.next().unwrap();
    		let second = pieces.next().unwrap();
    		if Range::contains(&first, &second) {
    			acc + 1
    		} else {
    			acc
    		}
    	});
    	println!("{number_containing}");
    	Ok(())
    }
    
    struct Range {
    	start: u32,
    	end: u32,
    }
    
    impl Range {
    	// Creates a new Range from a string of the form "<start>-<end>"
    	fn from_str(s: &str) -> Range {
    		let mut pieces = s.splitn(2, '-');
    		let start = pieces.next().unwrap().parse::<u32>().unwrap();
    		let end = pieces.next().unwrap().parse::<u32>().unwrap();
    		Range {
    			start,
    			end
    		}
    	}
    
    	// Returns true if x contains y or y contains x
    	fn contains(x: &Range, y: &Range) -> bool {
    		(x.start <= y.start && x.end >= y.end) || (y.start <= x.start && y.end >= x.end)
    	}
    }
    
    Part 2
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_4.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let number_containing = contents.lines().fold(0, |acc, line| {
    		let mut pieces = line.splitn(2, ',').map(Range::from_str);
    		let first = pieces.next().unwrap();
    		let second = pieces.next().unwrap();
    		if Range::overlaps(&first, &second) {
    			acc + 1
    		} else {
    			acc
    		}
    	});
    	println!("{number_containing}");
    	Ok(())
    }
    
    struct Range {
    	start: u32,
    	end: u32,
    }
    
    impl Range {
    	// Creates a new Range from a string of the form "<start>-<end>"
    	fn from_str(s: &str) -> Range {
    		let mut pieces = s.splitn(2, '-');
    		let start = pieces.next().unwrap().parse::<u32>().unwrap();
    		let end = pieces.next().unwrap().parse::<u32>().unwrap();
    		Range {
    			start,
    			end
    		}
    	}
    
    	// Returns true if x contains y or y contains x
    	#[allow(dead_code)]
    	fn contains(x: &Range, y: &Range) -> bool {
    		(x.start <= y.start && x.end >= y.end) || (y.start <= x.start && y.end >= x.end)
    	}
    
    	// Returns true if x and y overlap
    	fn overlaps(x:&Range, y: &Range) -> bool {
    		(x.start >= y.start && x.end <= y.end)
    		|| (x.end >= y.start && x.end <= y.end)
    		|| (y.start >= x.start && y.end <= x.end)
    		|| (y.end >= x.start && y.end <= x.end)
    	}
    }
    
    1 vote