13 votes

Day 6: Tuning Trouble

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

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>

24 comments

  1. tjf
    (edited )
    Link
    My Python solutions, which are identical other than the WINSIZ constant. Surprised there were so many test cases given, since this one is rather straightforward. Part 1 #!/usr/bin/env pypy3 import...

    My Python solutions, which are identical other than the WINSIZ constant.
    Surprised there were so many test cases given, since this one is rather
    straightforward.

    Part 1
    #!/usr/bin/env pypy3
    
    import sys
    
    WINSIZ = 4
    
    def main():
        data = sys.stdin.read().strip()
        nchars = next(i + WINSIZ for i in range(len(data) - WINSIZ)
                      if len(set(data[i:i + WINSIZ])) == WINSIZ)
        print(nchars)
    
    if __name__ == '__main__':
        main()
    
    Part 2
    #!/usr/bin/env pypy3
    
    import sys
    
    WINSIZ = 14
    
    def main():
        data = sys.stdin.read().strip()
        nchars = next(i + WINSIZ for i in range(len(data) - WINSIZ)
                      if len(set(data[i:i + WINSIZ])) == WINSIZ)
        print(nchars)
    
    if __name__ == '__main__':
        main()
    
    6 votes
  2. [5]
    primordial-soup
    (edited )
    Link
    Part 1, in Python-ish (l > p | (windowed, X, 4) | (locate, X, all_unique) | first) + 4 Python code generated from the above from more_itertools import all_unique from more_itertools import first...
    Part 1, in Python-ish
    (l > p
     | (windowed, X, 4)
     | (locate, X, all_unique)
     | first) + 4
    
    Python code generated from the above
    from more_itertools import all_unique
    from more_itertools import first
    from more_itertools import locate
    from more_itertools import windowed
    from pipetools import X
    from pipetools import pipe
    import sys
    p = pipe
    for l in sys.stdin:
        l = l.rstrip('\n')
        output = (l > p | (windowed, X, 4) | (locate, X, all_unique) | first) + 4
        if output is not None:
            print(output)
    

    sed 's/4/14/' for Part 2.

    6 votes
    1. [4]
      Gyrfalcon
      Link Parent
      Do you have a link to where I could learn more about Python-ish? When I try to google it I just get a bunch of people talking about things that are like or similar to Python and not what you are...

      Do you have a link to where I could learn more about Python-ish? When I try to google it I just get a bunch of people talking about things that are like or similar to Python and not what you are using.

      2 votes
      1. [3]
        Crestwave
        Link Parent
        https://tildes.net/~comp.advent_of_code/13l9/day_4_camp_cleanup#comment-7lvt
        4 votes
        1. [2]
          primordial-soup
          Link Parent
          thanks for linking to this! @Gyrfalcon—you won't find it searching "Python-ish" online, that's just a name i made up for the purpose of posting AoC solutions to Tildes :)

          thanks for linking to this!

          @Gyrfalcon—you won't find it searching "Python-ish" online, that's just a name i made up for the purpose of posting AoC solutions to Tildes :)

          3 votes
          1. Gyrfalcon
            Link Parent
            Well I definitely appreciate you posting, even though I can't say I understand what is going on all that much better after reading you describe what it is.

            Well I definitely appreciate you posting, even though I can't say I understand what is going on all that much better after reading you describe what it is.

            1 vote
  3. [4]
    bhrgunatha
    (edited )
    Link
    Part 1 The nice thing about Racket's for iterators is they can be nested (for*) or in-parallel (for). This makes iterating a small sized window nicer because you don't need to deal with indexes...
    Part 1 The nice thing about Racket's for iterators is they can be nested (for*) or in-parallel (for). This makes iterating a small sized window nicer because you don't need to deal with indexes except for initialising them.
    (define (part-01 input)
      (for/first ([(a i) (in-indexed (in-string input))]
                  [b (in-string (substring input 1))]
                  [c (in-string (substring input 2))]
                  [d (in-string (substring input 3))]
                  #:when (= 4 (set-count (set a b c d))))
        (+ i 4)))
    
    Part 2 Extend the idea to part 2 but convert the string to a list. Named let in Racket/Scheme is an idiom like defining an internal function where the let bound variables correspond to function arguments. You _can_ define internal procedures in Racket but it's overhead for a simple case like this. The Racket libraries are littered with hundreds or thousands of named lets.
    (define (part-02 input)
      (let search ([cs (string->list input)]
                   [i 0])
        (cond [(= 14 (set-count (list->set (take cs 14))))
               (+ i 14)]
              [else (search (rest cs) (add1 i))])))
    
    Speedrun Better to keep a running count of letters in a sliding substring. If Racket had a nice counting library like python's I wouldn't need to write these.

    Attempt 1:

    Part 1 ~75% elapsed time
    Part 2 ~35% elapsed time

    Attempt 2:
    I wish I could remember that hash-count is a constant time count of keys.
    Simply replace (= n (length (hash-keys cs)) with (= n (hash-count cs)) as the exit clause below gives
    Part 1 ~50% elapsed time
    Part 2 ~20% elapsed time

    (define (package-start s n)
      (for/fold ([cs (counts (substring s 0 n))]
                 [found? #f]
                 #:result found?)
                ([i (in-naturals)]
                 [start (in-string s)]
                 [end (in-string (substring s n))]
                 #:break found?)
        (if (= n (length (hash-keys cs)))
            (values cs (+ i n))
            (values (udpate-counts cs start end) found?))))
    
    (define (udpate-counts counts removal addition)
      (define removed
        (if (<= (hash-ref counts removal 0) 1)
            (hash-remove counts removal)
            (hash-update counts removal sub1)))
      (hash-update removed addition add1 0))
    
    (define (counts s)
      (for/fold ([cs (hasheqv)])
                ([c (in-string s)])
        (hash-update cs c add1 0)))
    
    (define (part-01 input) (package-start input 4))
    (define (part-02 input) (package-start input 14))
    
    4 votes
    1. [3]
      thorondir
      Link Parent
      Reading your solutions [and the docs, to figure out what you did xD] is always super helpful. :D I went about it a bit differently, by manually keeping track of the index. Which also meant part...

      Reading your solutions [and the docs, to figure out what you did xD] is always super helpful. :D

      I went about it a bit differently, by manually keeping track of the index. Which also meant part two was a change of adding a single character. xD

      Both Parts
      (define (unique? lst)
        (= (length lst) (set-count (list->set lst))))
      
      (define (process-slice slice idx slice-length)
        (cond [(< (length slice) slice-length)
               (displayln "slice too small")
               (values empty -1)]
              [(>= (length slice) slice-length)
               (define new-slice (take slice slice-length))
               (cond
                 [(unique? new-slice) (values empty (+ idx slice-length))]
                 [else (process-slice (rest slice) (add1 idx) slice-length)])]
              [else
               (values 'never-supposed-to-get-here -1)]))
      
      (define (part-1 input)
        (define-values (m i) (process-slice input 0 4))
        i)
      
      (define (part-2 input)
        (define-values (m i) (process-slice input 0 14))
        i)
      
      2 votes
      1. [2]
        bhrgunatha
        Link Parent
        That's kind of you to say, thanks. I did essentially the same for part-02, except I didn't check the length because AoC basically guarantees we'll find an answer. In real code you bet I'd be...

        That's kind of you to say, thanks.

        I did essentially the same for part-02, except I didn't check the length because AoC basically guarantees we'll find an answer. In real code you bet I'd be checking if the list is running out of items.

        One thing to be careful about is that length has to consume the whole list - the compiler doesn't have internal state for list length.

        Instead of repeatedly checking the length, it would be more efficient to pass the length as an argument to process-slice and keep reducing that value as you chop off more of the list. That way, you don't go through the whole list every iteration, instead you just subtract from the value you calculate once at the start.

        3 votes
        1. thorondir
          Link Parent
          Oh, right. O(n) in a place I didn't expect. Thanks for the pointers! :)

          Oh, right. O(n) in a place I didn't expect.

          Thanks for the pointers! :)

          2 votes
  4. asterisk
    Link
    Python buffer = open("input.txt").read() def device(length: int): for i in range(len(buffer) - length + 1): if len(set(buffer[i : i + length])) == length: return length + i print(device(4)) # Part...
    Python
    buffer = open("input.txt").read()
    
    
    def device(length: int):
        for i in range(len(buffer) - length + 1):
            if len(set(buffer[i : i + length])) == length:
                return length + i
    
    
    print(device(4))  # Part One: 1625
    print(device(14))  # Part Two: 2250
    
    4 votes
  5. jzimbel
    (edited )
    Link
    Elixir 👶 Both parts Wasn't sure what to name the function tbh. It's not really giving the index. defmodule AdventOfCode.Solution.Year2022.Day06 do def part1(input), do: index_of_marker(input, 4)...

    Elixir

    👶

    Both parts

    Wasn't sure what to name the function tbh. It's not really giving the index.

    defmodule AdventOfCode.Solution.Year2022.Day06 do
      def part1(input), do: index_of_marker(input, 4)
      def part2(input), do: index_of_marker(input, 14)
    
      defp index_of_marker(input, size) do
        input
        |> String.to_charlist()
        |> Stream.chunk_every(size, 1, :discard)
        |> Enum.find_index(&(MapSet.size(MapSet.new(&1)) == size))
        |> Kernel.+(size)
      end
    end
    
    About |>

    I abuse it in my solutions (because it's fun), but the |> ("pipe") operator is basically just syntactic sugar.

    Functions in Elixir, for the most part, are not tightly coupled with data types. As a counterexample, arrays in JS have array functions as "methods"—myArray.map(...). The method gets the "subject" array as an implicit first this argument. There are no methods in Elixir. Data and logic are more strictly separated. Instead, we have functions where you explicitly pass the "subject" as the first argument: Enum.map(my_list, ...).

    |> exists mostly to make "chaining" possible. It takes the result of the previous expression, and passes it as the first argument of the next function call. The code runs the same, but it reads more like a pipeline of sequential operations than one giant messy one-liner.

    One thing I really like about this approach is that, in referencing the module for each function call—e.g. String.*, Enum.*, MapSet.*—you can sort of annotate what type you're working with at each step. In a longer pipeline, it's often very easy to see the type transformations that are happening; something that you don't get with chained method calls in other languages.

    numbers_str = "1 2 25 93 8"
    
    sum = Enum.sum(Enum.map(String.split(numbers_str, " "), &String.to_integer/1))
    
    # compiles to the exact same byte code as
    
    sum =
      numbers_str
      # We're using a function from the String module, so the subject is currently a string
      |> String.split(" ")
      # We're using a function from the Enum module, so the subject is currently an enumerable (a list, specifically)
      |> Enum.map(&String.to_integer/1)
      # Still an enumerable
      |> Enum.sum()
    
    3 votes
  6. tomf
    Link
    Google Sheets Not the prettiest thing, but it works. both parts This does both parts.. I'm still new to SCAN, though. :) =ARRAYFORMULA( SCAN(,{4,14}, LAMBDA(a,c, QUERY( {SEQUENCE(LEN(A2),1,c),...

    Google Sheets

    Not the prettiest thing, but it works.

    both parts

    This does both parts.. I'm still new to SCAN, though. :)

    =ARRAYFORMULA(
      SCAN(,{4,14},
       LAMBDA(a,c, 
        QUERY(
         {SEQUENCE(LEN(A2),1,c),
          BYROW(SPLIT(REGEXREPLACE(MID(A2,SEQUENCE(LEN(A2)),c),"(.)","$1|"),"|"),
           LAMBDA(x,TRANSPOSE(UNIQUE(TRANSPOSE(x)))))},
         "select Col1
          where Col"&c+1&" is not null
          limit 1"))))
    
    3 votes
  7. MeckiSpaghetti
    (edited )
    Link
    Ruby Part 1 i = File.read("input.txt") i.chars.each_cons(4) do |s| if s == s.uniq p i.index(s.join)+4 break end end Part 2 i = File.read("input.txt") i.chars.each_cons(14) do |s| if s == s.uniq p...

    Ruby

    Part 1
    i = File.read("input.txt")
    
    i.chars.each_cons(4) do |s|
      if s == s.uniq
        p i.index(s.join)+4
        break
      end
    end
    
    Part 2
    i = File.read("input.txt")
    
    i.chars.each_cons(14) do |s|
      if s == s.uniq
        p i.index(s.join)+14
        break
      end
    end
    
    3 votes
  8. Eabryt
    Link
    I felt like this one was especially easy, but maybe that's just me. Part1 & Part2 import queue def checkRepeats(input): chars = [] for item in input.queue: if item in chars: return False...

    I felt like this one was especially easy, but maybe that's just me.

    Part1 & Part2
    import queue
    
    
    def checkRepeats(input):
        chars = []
        for item in input.queue:
            if item in chars:
                return False
            chars.append(item)
        return True
    
    
    def part1(input):
        print(f"Part 1!")
        window = queue.Queue(4)
        for index,char in enumerate(input):
            if window.full():
                if checkRepeats(window):  
                    break
                window.get() 
            window.put(char)
        print(f"Index: {index}")
    
    
    def part2(input):
        print(f"Part 2!")
        window = queue.Queue(14)
        for index,char in enumerate(input):
            if window.full():
                if checkRepeats(window):  
                    break
                window.get() 
            window.put(char)
        print(f"Index: {index}")
    
    
    def openFile():
        return open("input.txt", "r").read()
    
    
    def main():
        f = openFile()
        part1(f)
        part2(f)
    
    
    if __name__ == '__main__':
        main()
    
    3 votes
  9. wycy
    Link
    Rust use std::env; use std::io::{self}; use std::collections::HashSet; fn packet_start(s: &str) -> usize { const MARKER_SIZE: usize = 4; let mut start = MARKER_SIZE; for chs in...
    Rust
    use std::env;
    use std::io::{self};
    use std::collections::HashSet;
    
    fn packet_start(s: &str) -> usize {
        const MARKER_SIZE: usize = 4;
        let mut start = MARKER_SIZE;
        for chs in s.chars().collect::<Vec<_>>().windows(MARKER_SIZE) {
            let (a,b,c,d) = (chs[0],chs[1],chs[2],chs[3]);
            if a != b && a != c && a != d && b != c && b != d && c != d { break }
            else { start += 1; }
        }
        start
    }
    
    fn message_start(s: &str) -> usize {
        const MARKER_SIZE: usize = 14;
        let mut start = MARKER_SIZE;
        'outer: for chs in s.chars().collect::<Vec<_>>().windows(MARKER_SIZE) {
            start += 1;
            let mut char_set: HashSet<char> = HashSet::new();
            for ch in chs {
                if char_set.contains(ch) { continue 'outer; }
                else { char_set.insert(*ch); }
            }
            break;
        }
        start - 1
    }
    
    fn solve(input: &str) -> io::Result<()> {
        // Input
        let input_str = std::fs::read_to_string(input).unwrap();
        let input_str = input_str.trim();
    
        // Output
        println!("Part 1: {}",packet_start(input_str)); // 1578
        println!("Part 2: {}",message_start(input_str)); // 2178
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    2 votes
  10. kari
    Link
    Today was much easier than I expected :) nim (both parts) import std/[sets, sequtils] proc day06*() = let f = open("inputs/day06.in") defer: f.close() let chars = f.readLine().toSeq() var...

    Today was much easier than I expected :)

    nim (both parts)
    import std/[sets, sequtils]
    
    proc day06*() =
      let f = open("inputs/day06.in")
      defer: f.close()
    
      let chars = f.readLine().toSeq()
      var
        startOfPktIdx = -1
        startOfMsgIdx = -1
        curIdx = 0
        pktSet: HashSet[char]
        msgSet: HashSet[char]
    
      while startOfPktIdx == -1 or startOfMsgIdx == -1:
        # Part 1
        if startOfPktIdx == -1:
          pktSet = chars[curIdx .. curIdx + 3].toHashSet()
          if pktSet.len == 4:
            startOfPktIdx = curIdx + 4
        # Part 2
        if startOfMsgIdx == -1:
          msgSet = chars[curIdx .. curIdx + 13].toHashSet()
          if msgSet.len == 14:
            startOfMsgIdx = curIdx + 14
        curIdx += 1
    
      echo "Part 1: " & $startOfPktIdx
      echo "Part 2: " & $startOfMsgIdx
    
    2 votes
  11. ras
    Link
    Today's was nice and easy. Solution - Parts 1 and 2 - JavaScript import { readFileSync } from "fs"; const input = readFileSync("input.txt", "utf8").toString().split("\n"); const MARKER_SIZE = 4;...

    Today's was nice and easy.

    Solution - Parts 1 and 2 - JavaScript
    import { readFileSync } from "fs";
    
    const input = readFileSync("input.txt", "utf8").toString().split("\n");
    const MARKER_SIZE = 4;
    // const MARKER_SIZE = 14;
    
    const findMarker = (buffer) => {
      for (let i = 0; i < buffer.length; i++) {
        let s = new Set();
        let x = [...buffer.slice(i, i + MARKER_SIZE)];
        for (const i of x) {
          s.add(i);
        }
        if (s.size === MARKER_SIZE) return i + MARKER_SIZE;
      }
    };
    
    input.forEach((item, index) => {
      console.log(findMarker(item));
    });
    
    2 votes
  12. Toric
    Link
    today was nice and easy. Had to find rusts window() function, though. Driver mod part1; mod part2; mod utilities; fn main() { let _input = include_str!("./input.txt"); println!("Part One");...

    today was nice and easy. Had to find rusts window() function, though.

    Driver
    mod part1;
    mod part2;
    mod utilities;
    
    fn main() {
        let _input = include_str!("./input.txt");
    
        println!("Part One");
        println!("Result: {}", part1::part1(_input));
    
        println!("Part Two");
        println!("Result: {}", part2::part2(_input));
    }
    
    Utilities
    pub fn find_dupes_stupid<T: PartialEq>(slice: &[T]) -> bool {
        for i in 1..slice.len() {
            if slice[i..].contains(&slice[i - 1]) {
                return true;
            }
        }
        false
    }
    
    Part 1
    use crate::utilities::*;
    
    pub fn part1(input: &str) -> usize {
        input
            .as_bytes()
            .windows(4)
            .position(|x| !find_dupes_stupid(x))
            .unwrap()+4
    }
    
    Part 2
    use crate::utilities::*;
    
    pub fn part2(input: &str) -> usize {
        input
            .as_bytes()
            .windows(14)
            .position(|x| !find_dupes_stupid(x))
            .unwrap()+14
    }
    
    2 votes
  13. Crestwave
    Link
    Quick and dirty because I am quite late. Part 1 #!/usr/bin/awk -f BEGIN { FS = "" } { for (i = 1; i <= NF; ++i) { a[$i] += 1 if (i >= 4) { if (a[$i]*a[$(i-1)]*a[$(i-2)]*a[$(i-3)] == 1) { print(i)...

    Quick and dirty because I am quite late.

    Part 1
    #!/usr/bin/awk -f
    BEGIN { FS = "" }
    {
    	for (i = 1; i <= NF; ++i) {
    		a[$i] += 1
    
    		if (i >= 4) {
    			if (a[$i]*a[$(i-1)]*a[$(i-2)]*a[$(i-3)] == 1) {
    				print(i)
    				exit
    			}
    
    			a[$(i-3)] -= 1
    		}
    	}
    }
    
    Part 2
    #!/usr/bin/awk -f
    BEGIN { FS = "" }
    {
    	for (i = 1; i <= NF; ++i) {
    		a[$i] += 1
    
    		if (i >= 14) {
    			for (j in a)
    				if (a[j] > 1)
    					break
    
    			if (a[j] < 2) {
    				print(i)
    				exit
    			}
    
    			a[$(i-13)] -= 1
    		}
    	}
    }
    
    2 votes
  14. 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; struct Buffer { inner: [(usize, char); 4], } impl...

    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;
    
    struct Buffer {
    	inner: [(usize, char); 4],
    }
    
    impl Buffer {
    	fn new() -> Buffer {
    		Buffer { inner: [(0, '\n'); 4] }
    	}
    
    	fn add(&mut self, new_elem: (usize, char)) {
    		let least_recent = self.inner.iter().enumerate().min_by(|x, y| x.1.0.cmp(&y.1.0)).unwrap();
    		self.inner[least_recent.0] = new_elem;
    	}
    
    	fn unique(&self) -> bool {
    		self.inner.iter().all(|e| e.1 != '\n' && self.inner.iter().filter(|e2| e.1 == e2.1).count() == 1)
    	}
    }
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_6.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let mut recents: Buffer = Buffer::new();
    	let pos = contents.chars().enumerate().take_while(|(idx, c)| {
    		recents.add((idx + 1, *c));
    		!recents.unique()
    	}).count() + 1;
    	println!("{pos}");
    	Ok(())
    }
    
    Part 2
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    
    struct Buffer {
    	inner: [(usize, char); 14],
    }
    
    impl Buffer {
    	fn new() -> Buffer {
    		Buffer { inner: [(0, '\n'); 14] }
    	}
    
    	fn add(&mut self, new_elem: (usize, char)) {
    		let least_recent = self.inner.iter().enumerate().min_by(|x, y| x.1.0.cmp(&y.1.0)).unwrap();
    		self.inner[least_recent.0] = new_elem;
    	}
    
    	fn unique(&self) -> bool {
    		self.inner.iter().all(|e| e.1 != '\n' && self.inner.iter().filter(|e2| e.1 == e2.1).count() == 1)
    	}
    }
    
    fn main() -> Result<(), Box<dyn Error>> {
    	let mut file = File::open("input_6.txt")?;
    	let mut contents = String::new();
    	file.read_to_string(&mut contents)?;
    	let mut recents: Buffer = Buffer::new();
    	let pos = contents.chars().enumerate().take_while(|(idx, c)| {
    		recents.add((idx + 1, *c));
    		!recents.unique()
    	}).count() + 1;
    	println!("{pos}");
    	Ok(())
    }
    
    2 votes
  15. soks_n_sandals
    Link
    Both of today's parts in bash. Part 1+2 There's probably a more elegant way than deleting matching letters from a stream of 4 characters then checking that 3 are still left to determine...

    Both of today's parts in bash.

    Part 1+2 There's probably a more elegant way than deleting matching letters from a stream of 4 characters then checking that 3 are still left to determine uniqueness.
    #!/usr/bin/env bash
    
    file=day6.dat
    part=1
    
    if [[ ${part} -eq 1 ]]; then
    # /////////////////////////////////////////////////////////////////////////////
        #part 1 // 0.08s // 1300 dbpw
        searchWidth=4 #width of character subset to check
    else
        #part 2 // 0.26s // 3986 lcfjqpdwznsbrh
        searchWidth=14 #width of character subset to check
    fi
    
    mapfile -t stream < $file
    charLength=${#stream}
    i=0 #counter for outer loop
    while [[ $i -lt $charLength ]]; do
    
    #   -- extract subset of letters from bit stream
        currentFour=${stream:i:searchWidth} 
    
        j=0 #counter for inner loop
        count=0 #number of unique letters in subset
    
    #   -- extract one letter from our subset
    #   -- delete matches of extracted letter
    #   -- if only 1 letter is matched, look at the next letter, otherwise just start again
    #   -- if we have four unique letters, print the spot and quit
        while [[ $j -lt $searchWidth ]]; do
            check=${currentFour:$j:1}
            removed=${currentFour//[$check]/}
            length=${#removed}
            if [[ length -eq $(($searchWidth - 1)) ]]; then
                ((count = count + 1))
                (( j = j + 1))
            else
                break
            fi
        done
    
    #   -- quit searching after the first set of unique letters
        if [[ $count -eq $searchWidth ]]; then
            break 1
        fi
        (( i = i + 1))
    done
    
    echo Position of stream for part ${part}: $(($i + $searchWidth))
    echo Unique letters: $currentFour
    
    1 vote
  16. Gyrfalcon
    Link
    Well after yesterday saying that I had never done regular expressions in Python I saw a text matching challenge and tried it and then figured out that this is not, in fact, the sort of task that...

    Well after yesterday saying that I had never done regular expressions in Python I saw a text matching challenge and tried it and then figured out that this is not, in fact, the sort of task that regular expressions make easy. I ended up doing this a little differently than the other Python solutions I see so far today, not using either queues or sets, but getting to test drive for else syntax, which was neat. I also did it kind of a clunky, single use way for part one, and then wrote part 2 generally so I could replace my part 1 function, though I left it for posterity. I do think my solution has a useful benefit, in that if it finds, say that the last two characters in a long search window are the same, it will skip forward such that the window starts with what was previously the last character, since we know that all windows that include those two characters at the end will not be what we are looking for.

    Parts 1 and 2, Python
    def find_packet_marker(message: str) -> int:
        end = 3 
        while end < len(message):
            if message[end - 3] in message[end - 2 : end + 1]: 
                end += 1
                continue
    
            if message[end - 2] in message[end - 1 : end + 1]: 
                end += 2
                continue
    
            if message[end - 1] == message[end]:
                end += 3
                continue
    
            return end + 1 
    
        return -1
    
    
    def find_marker(message: str, marker_size: int) -> int:
    
        end = marker_size - 1 
        while end < len(message):
            for offset in range(marker_size - 1, 0, -1):
                if message[end - offset] in message[end - (offset - 1) : end + 1]: 
                    end += marker_size - offset
                    break
            else:
                return end + 1 
    
        return -1
    
    
    def main(filepath: str) -> Tuple[int, int]:
    
        message = load_file.load_cleaned_lines(filepath)[0]
        return (find_marker(message, 4), find_marker(message, 14))
    
    1 vote
  17. balooga
    (edited )
    Link
    Here's my TypeScript solution. Seconding what others have said that today's was refreshingly easy. type InputData = string; export function run(input: string): string[] { Parts 1 and 2 const...
    Here's my TypeScript solution. Seconding what others have said that today's was refreshingly easy.
    type InputData = string;
    
    export function run(input: string): string[] {
    
    Parts 1 and 2
      const findMarker = (stream: InputData, numberOfCharacters: number): number => {
        const previousLetters = [] as string[];
        for (let i = 0; i < stream.length; i++) {
          previousLetters.push(stream[i]);
          if (previousLetters.length <= numberOfCharacters) {
            continue;
          }
          previousLetters.shift();
          if (new Set(previousLetters).size === numberOfCharacters) {
            return i + 1;
          }
        }
        return -1;
      };
    
      return [`${findMarker(input, 4)}`, `${findMarker(input, 14)}`];
    }
    

    Edit: Renamed a variable that still had a name specific to part 1 even though it was used for both parts.

    1 vote