# Advent of Code 2022 - Day 4: Camp Cleanup

1 2 |
#lang iracket/lang #:require racket (require "../advent.rkt") |

## Day 4 - Part 1

**Camp Cleanup**

Before we get started with today’s puzzle, I have some **cleanup** of my own to do!

**1. all-from-out**

At the beginning of each day, the second line of the prelude above is:

`(require "../advent.rkt")`

This is Racket’s way of importing a module. Rather than also having to import common modules I typically use, such as the `threading`

module, e.g. `(require "../advent.rkt" threading)`

, I’d like to be able to just import my `advent.rkt`

module and get access to `threading`

as well. I learned a long time ago that the answer to the question, “Can Racket do <X>”, is almost always, “yes”, and this is no exception; it’s accomplished with the `all-from-out`

macro. My `advent.rkt`

module already had `(require threading)`

for it’s own purposes, so I just needed a directive to have it export all of the exports from `threading`

as if they were defined in `advent.rkt`

:

`(provide (all-from-out threading))`

**2. A parsing fix**

One of the parsers I got from Peter Norvig is the `numbers`

parser, and it allows parsing input such as `1,2 | -3,4`

into a list of numbers, `'(1 2 -3 4)`

; however, today’s input was like `49-51,31-50`

, and the `numbers`

parser would output `'(49 -51 31 -50`

. The `-`

chars were meant to be separators, not negative signs. The original `numbers`

parser used the regex pattern `-?[0-9.]+`

. Modifying the regex pattern to be `((?<![0-9])-)?[0-9.]+`

instead, which uses a **negative lookbehind** pattern, correctly outputs `'(49 51 31 50)`

.

The **negative lookbehind** pattern `(?<![0-9])`

is used in front of the `-`

, so that the `-`

will *only* match if it’s **not** preceded by a numeric digit. This is a **very** handy regex technique.

Ok, with the modified `numbers`

parser in place, let’s parse today’s input!

`1` |
(define in (parse-aoc 4 numbers)) |

```
----------------------------------------------------------------------------------------------------
day04.txt -> 11358 chars, 1000 lines; first 3 lines; last 2 lines:
----------------------------------------------------------------------------------------------------
49-51,31-50
96-99,2-95
2-62,62-98
...
32-38,33-71
3-5,4-98
----------------------------------------------------------------------------------------------------
(parse 4) -> 1000 entries:
----------------------------------------------------------------------------------------------------
((49 51 31 50)
(96 99 2 95)
...
(3 5 4 98))
----------------------------------------------------------------------------------------------------
```

Great, it appears to be working perfectly! For Part 1, we need to determine if one elf’s range of section numbers is a subset/superset of the other elf’s range. The first thing that popped into my head was to simply compare range endpoints. Since parsing produced a list of 4 numbers for each elf pair, I need to **destructure** the list, and Racket’s `match-let`

is handy for this. The match pattern of `(list a b c d)`

both matches and binds the values of the list’s elements:

1 2 3 4 |
(define (overlap? lst) (match-let ([(list a b c d) lst]) (or (and (<= a c) (>= b d)) (and (<= c a) (>= d b))))) |

With this `overlap?`

predicate, all that remains is to `count`

the occurrences:

`1` |
(count overlap? in) |

`453`

## Part 2

For Part 2, rather than looking for subsets/supersets, we just need to determine if the ranges overlap at all. Rather than modify my approach for Part 1 for this case, it seemed simpler to me to just convert the ranges to sets and determine if the intersection is non-empty:

1 2 3 4 5 |
(define (overlap-partial? lst) (match-let ([(list a b c d) lst]) (let ([ one (inclusive-range a b) ] [ two (inclusive-range c d) ]) (not (set-empty? (set-intersect one two)))))) |

Again, all we need to do now is `count`

the occurences:

`1` |
(count overlap-partial? in) |

`919`

Today was enjoyable, the hammer has yet to drop on us with a tough puzzle, but it will!

## Refactor

Now, with the hindsight of having seen both parts, how would I construct a solution? Assuming each elf’s range is a `set`

, for Part 1, we need to determine if one range is a subset of another. For Part 2, we need to determine if the intersection of the two sets is non-empty. Let’s create a predicate for each part and a common `need-reorg?`

function:

1 2 3 4 5 6 7 |
(define (part1? a b) (or (subset? a b) (subset? b a))) (define (part2? a b) (not (set-empty? (set-intersect a b)))) (define ((need-reorg? part?) lst) (match-let ([(list a b c d) lst]) (part? (inclusive-range a b) (inclusive-range c d)))) |

Now we can count the occurences for each part:

`1` |
(count (need-reorg? part1?) in) |

`453`

`1` |
(count (need-reorg? part2?) in) |

`919`