13 votes

Day 3: Binary Diagnostic

Today's problem description: https://adventofcode.com/2021/day/3

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>

29 comments

  1. [2]
    tjf
    Link
    Python golf, day 3. I'm quite proud of part 1 especially. Again tips and improvements are appreciated. Part 1 r=*map(str.strip,open(0)), F=lambda f:int(''.join([f(x,key=x.count)for x in...

    Python golf, day 3. I'm quite proud of part 1 especially. Again tips and improvements are appreciated.

    Part 1
    r=*map(str.strip,open(0)),
    F=lambda f:int(''.join([f(x,key=x.count)for x in zip(*r)]),2)
    print(F(max)*F(min))
    
    Part 2
    r=*map(str.strip,open(0)),
    def F(f,R,i):c=[*zip(*R)][i];R=[x for x in R if x[i]==(f(r[0])if 2*c.count('0')==len(c)else f(c,key=c.count))];return F(f,R,i+1)if len(R)-1 else int(R[0],2)
    print(F(max,r,0)*F(min,r,0))
    
    8 votes
    1. asterisk
      Link Parent
      Wow, yeah, the first part is really neat!

      Wow, yeah, the first part is really neat!

      3 votes
  2. [3]
    PapaNachos
    Link
    Part B felt like a pretty substantial jump in difficulty after the last 2 days that felt more like warms ups. But also it was a much more interesting challenge Day 3 Part A – Python Just counting...

    Part B felt like a pretty substantial jump in difficulty after the last 2 days that felt more like warms ups. But also it was a much more interesting challenge

    Day 3 Part A – Python Just counting up the ones in each column and then doing some binary math to get the different values. Not too fancy, but it works.
    #data = test_data
    data = real_data
    data = data.split('\n')
    
    str_len = len(data[0])
    ones_count = list([0] * str_len)
    for val in data:
        for index, i in enumerate(val):
            if i == '1':
                ones_count[index] = ones_count[index] + 1
    multiple = 2**(str_len-1)
    e_rate = 0
    g_rate = 0
    for val in ones_count:
        if val > (len(data)/2):
            e_rate = e_rate + multiple
        else:
            g_rate = g_rate + multiple
        multiple = multiple /2
    
    print(e_rate)
    print(g_rate)
    print(e_rate * g_rate)
    
    Day 3 Part B – Python This took a second to wrap my head around what it was even asking. Recursion seemed like the best bet since the list has to narrow every time. Keeping track of what type of object I was working with too a bit of tracking. And my 2 methods are basically the same, except "0" and "1" are flipped. I'm sure they could be streamlined into one method with some work
    #data = test_data
    data = real_data
    data = data.split('\n')
    #data = [int(i) for i in data]
    
    def find_oxygen(recursive_data, index):
        if len(recursive_data) == 1:
            return str(recursive_data[0][index:])
        elif len(recursive_data) == 0:
            return "1"
        else:
            data_len = len(recursive_data)
            ones_count = 0
            result = "0"
            for val in recursive_data:
                if val[index] == "1":
                    ones_count = ones_count + 1
            if ones_count >= data_len/2:
                result = "1"
            new_recursive_data = []
            for val in recursive_data:
                if val[index] == result:
                    new_recursive_data.append(val)
            return result + find_oxygen(new_recursive_data, index + 1)
    def find_co2(recursive_data, index):
        if len(recursive_data) == 1:
            return str(recursive_data[0][index:])
        elif len(recursive_data) == 0:
            return "1"
        else:
            data_len = len(recursive_data)
            ones_count = 0
            result = "1"
            for val in recursive_data:
                if val[index] == "1":
                    ones_count = ones_count + 1
            if ones_count >= data_len/2:
                result = "0"
            new_recursive_data = []
            for val in recursive_data:
                if val[index] == result:
                    new_recursive_data.append(val)
            return result + find_co2(new_recursive_data, index + 1)
    oxy_count = int(find_oxygen(data, 0), 2)
    co2_count = int(find_co2(data, 0), 2)
    
    print(oxy_count * co2_count)
    
    Tips
    • This one you really need to read the problem description to figure out what it's asking

    • The sample data is only 5 characters long, but your real data will be longer, so make sure to make you code so that it can handle longer inputs

    • Binary math is useful in this one, so it's helpful to refresh yourself on it

    • I found recursion a useful way to handle part B. If you're not familiar, recursion is a tool where a method is able to call itself. It's useful if you need to do something like look and increasingly smaller subsets of a data set

    6 votes
    1. [2]
      spit-evil-olive-tips
      Link Parent
      you can also use collections.Counter or the more versatile defaultdict for this. defaultdict is especially neat because of how the factory function for missing values works. you can do...

      ones_count = list([0] * str_len)

      you can also use collections.Counter or the more versatile defaultdict for this.

      defaultdict is especially neat because of how the factory function for missing values works. you can do defaultdict(int) or defaultdict(list) for example, and it looks like you're specifying the type - but really it works because int() and list() are used as functions to create missing values, so they initialize to 0 and the empty list, respectively.

      that means you can provide an arbitrary function as the factory method for missing values, so for example defaultdict(lambda: defaultdict(list)) gives you a 2-level nested dictionary where the values are lists. then you can do some_dict['foo']['bar'].append('a') or whatever without needing initialization code to make sure the the list you're appending to already exists.

      6 votes
      1. PapaNachos
        (edited )
        Link Parent
        Oh yeah! I always forget collections exists. Usually when I need a bigger or more complex data structure I break out pandas, but default dicts can be extremely useful. I didn't know you could nest...

        Oh yeah! I always forget collections exists. Usually when I need a bigger or more complex data structure I break out pandas, but default dicts can be extremely useful. I didn't know you could nest them like that though, that's really neat!

        3 votes
  3. Crespyl
    Link
    Gross, this is what I get for trying to go fast. Not very satisfied with either part, and I'm not really comfortable enough with Ruby's bit-bashing features to do it the way I think I'd prefer (at...

    Gross, this is what I get for trying to go fast. Not very satisfied with either part, and I'm not really comfortable enough with Ruby's bit-bashing features to do it the way I think I'd prefer (at least not off the cuff, I'll probably come back tomorrow and revisit it). Part 1 was more or less okay, 2 was way more repetitive than I'm happy with.

    But hey, I'm getting faster.

    Part 1 Ruby
    def compute_p1(input)
      zeroes = Hash.new(0)
      ones = Hash.new(0)
    
      input
        .lines
        .map(&:chomp)
        .each do |line|
        line.chars.each_with_index do |char, i|
          case char
          when '0'
            zeroes[i] += 1
          when '1'
            ones[i] += 1
          end
        end
      end
    
      gamma = zeroes
                .sort
                .zip(ones.sort)
                .map { |zs,os| zs[1] > os[1] ? 0 : 1 }
                .join('')
                .to_i(2)
    
      epsilon = zeroes
                  .sort
                  .zip(ones.sort)
                  .map { |zs,os| zs[1] < os[1] ? 0 : 1 }
                  .join('')
                  .to_i(2)
    
      gamma * epsilon
    end
    
    Part 2 Ruby
    def compute_p2(input)
      numbers = input.lines.map(&:chomp)
      zeroes, ones = count_bits(numbers)
    
      oxygen_gen_rating_set = Set.new(numbers)
      bit_criteria_idx = 0
      while oxygen_gen_rating_set.size > 1
        tgt = ones[bit_criteria_idx] >= zeroes[bit_criteria_idx] ? '1' : '0'
        oxygen_gen_rating_set = oxygen_gen_rating_set.filter { _1[bit_criteria_idx] == tgt }
        bit_criteria_idx += 1
        zeroes, ones = count_bits(oxygen_gen_rating_set)
      end
    
      oxygen_gen_rating = oxygen_gen_rating_set.first.to_i(2)
    
      co2_scrubber_rating_set = Set.new(numbers)
      bit_criteria_idx = 0
      while co2_scrubber_rating_set.size > 1
        tgt = ones[bit_criteria_idx] < zeroes[bit_criteria_idx] ? '1' : '0'
        co2_scrubber_rating_set = co2_scrubber_rating_set.filter { _1[bit_criteria_idx] == tgt }
        bit_criteria_idx += 1
        zeroes, ones = count_bits(co2_scrubber_rating_set)
      end
    
      co2_scrubber_rating = co2_scrubber_rating_set.first.to_i(2)
    
      return oxygen_gen_rating * co2_scrubber_rating
    end
    
    def count_bits(numbers)
      zeroes = Hash.new(0)
      ones = Hash.new(0)
    
      numbers.each do |number|
        number.chars.each_with_index do |char, i|
          case char
          when '0'
            zeroes[i] += 1
          when '1'
            ones[i] += 1
          end
        end
      end
    
      [zeroes, ones]
    end
    
    5 votes
  4. [9]
    tomf
    (edited )
    Link
    There's gotta be a better way to do this. Part 1 This is slow, but covers part 1. =ARRAYFORMULA( CONCATENATE( IFERROR( VLOOKUP( SEQUENCE(12), QUERY( IFERROR( SPLIT( FLATTEN( IF(ISBLANK(A2:A),,...

    There's gotta be a better way to do this.

    Part 1

    This is slow, but covers part 1.

    =ARRAYFORMULA(
      CONCATENATE(
       IFERROR(
        VLOOKUP(
         SEQUENCE(12),
         QUERY(
          IFERROR(
           SPLIT(
            FLATTEN(
             IF(ISBLANK(A2:A),,
              COLUMN(A1:L1)&"|"&
              SPLIT(REGEXREPLACE(TO_TEXT(A2:A),"(.)","$1|"),"|",TRUE,TRUE))),
            "|",TRUE,TRUE)),
          "select Col1, Col2, Count(Col1)
           where Col2 is not null
           group by Col1, Col2
           order by Count(Col1) desc
           label Count(Col1) ''"),
         2,FALSE))))
    

    Since Sheets can only process binary up to ten digits, I had to use this old chestnut to get around it

    =SUMPRODUCT(
      --MID(Q1,LEN(Q1)+1-ROW(INDIRECT("1:"&LEN(Q1))),1),
      (2^(ROW(INDIRECT("1:"&LEN(Q1)))-1)))
    

    I came in at 22:10, which ranked me at 7800 -- which is good for me. :)

    Part 2

    ok, I tweaked this. The first formula is

    =INDEX(
      QUERY(
       {IF(ISBLANK($A2:$A),,
       SPLIT(REGEXREPLACE(TO_TEXT($A2:$A),"(.)","$1|"),"|",TRUE,TRUE)),$A$2:$A1001},
       "select Col1, Count(Col1)
        where Col1 is not null
        group by Col1
        order by Count(Col1) desc, Col1 desc
        label Count(Col1) ''"),
      1,1)
    

    from there I pop this in the next cell and drag it across.

    =INDEX(
      QUERY(
       {IF(ISBLANK($A$2:$A),,
       SPLIT(REGEXREPLACE(TO_TEXT($A$2:$A),"(.)","$1|"),"|",TRUE,TRUE)),$A$2:$A1001},
       "select Col"&COLUMN(B2)&", Count(Col"&COLUMN(B2)&")
        where 
         Col"&COLUMN(B2)&" is not null and
         Col13 starts with '"&CONCATENATE($F1:F1)&"'
        group by Col"&COLUMN(B2)&"
        order by Count(Col"&COLUMN(B2)&") desc, Col"&COLUMN(B2)&" desc
        label Count(Col"&COLUMN(B2)&") ''"),
      1,1)
    

    Not pretty. I haven't seen what anybody else did, so I'll most likely refine it into a oner like the others. I still haven't looked, but I did do it over so its a little leaner.

    In at 1:03:27 and ranked 6986th -- also pretty great for me. Sheets really isn't great for some of these things.

    Here's my sheet so far

    5 votes
    1. [6]
      bhrgunatha
      Link Parent
      [...] I think the answer is obvious but I love that your abusing spreadsheets to do it and the cover sheet is lovely.

      There's gotta be a better way to do this.

      [...]

      Here's my sheet so far

      I think the answer is obvious but I love that your abusing spreadsheets to do it and the cover sheet is lovely.

      6 votes
      1. [5]
        tomf
        Link Parent
        ha yeah. I haven't seen any of the other spreadsheet folks crank out a better method. There really must be one. What's the obvious answer I'm missing, though? :)

        ha yeah. I haven't seen any of the other spreadsheet folks crank out a better method. There really must be one.

        What's the obvious answer I'm missing, though? :)

        3 votes
        1. [2]
          bhrgunatha
          Link Parent
          I was just joking - python, ruby, rust, go, kotlin, swift ... I've tried using spreadhseets for mildly complex calculations and it's so painful that I'm in awe that it's even possible.

          I was just joking - python, ruby, rust, go, kotlin, swift ...

          I've tried using spreadhseets for mildly complex calculations and it's so painful that I'm in awe that it's even possible.

          2 votes
          1. tomf
            Link Parent
            doing a sexy solution for part two is killing me! I'm so close, but not quite there... and it's driving me nuts.

            doing a sexy solution for part two is killing me! I'm so close, but not quite there... and it's driving me nuts.

            2 votes
        2. [2]
          cfabbro
          Link Parent
          I believe they're suggesting you should be using MS Excel instead. ;)/s

          I believe they're suggesting you should be using MS Excel instead. ;)/s

          1 vote
          1. tomf
            Link Parent
            ha. maybe. Sheets can do pretty much everything Excel can these days. I've yet to see a better method among the spreadsheet folks. I've sorted out some better methods for the first part (below),...

            ha. maybe. Sheets can do pretty much everything Excel can these days.

            I've yet to see a better method among the spreadsheet folks. I've sorted out some better methods for the first part (below), but nothing for the second

            Alternate Part 1 Method
            =ARRAYFORMULA(
              CONCATENATE(
               TEXT(
                QUERY(
                 IFERROR(
                  SPLIT(
                   REGEXREPLACE(
                    A2:A,
                    "(.)",
                    "$1|"),
                   "|",TRUE,TRUE)),
                  "select 
                    Avg(Col1), Avg(Col2), Avg(Col3), Avg(Col4), Avg(Col5), Avg(Col6), 
                    Avg(Col7), Avg(Col8), Avg(Col9), Avg(Col10), Avg(Col11), Avg(Col12)
                   label 
                    Avg(Col1) '', Avg(Col2) '', Avg(Col3) '', Avg(Col4) '', Avg(Col5) '', Avg(Col6) '', 
                    Avg(Col7) '', Avg(Col8) '', Avg(Col9) '', Avg(Col10) '', Avg(Col11) '', Avg(Col12) ''"),
                 "0")))
            
            1 vote
    2. [2]
      cfabbro
      (edited )
      Link Parent
      Love the sheet front page, it's FAB-U-LOUS! p.s. Death to Smoochy is seriously underrated. RIP RW

      Love the sheet front page, it's FAB-U-LOUS!

      p.s. Death to Smoochy is seriously underrated. RIP RW

      4 votes
      1. tomf
        Link Parent
        ha. I think that's one of the few remaining easter eggs in Sheets. If you put 'pride' across the first five columns, it'll do that. I think its super classy :) As of last night that is the best...

        ha. I think that's one of the few remaining easter eggs in Sheets. If you put 'pride' across the first five columns, it'll do that. I think its super classy :)

        As of last night that is the best transparent PNG of Rainbow Randolph, too. haha

        2 votes
  5. Crestwave
    Link
    When it comes to Advent of Code, copying an entire block and changing one line >>> implementing a reusable function. :-) Part 1 #!/usr/bin/awk -f function bin2dec(bin,i, bit, dec) { for (i = 1; i...

    When it comes to Advent of Code, copying an entire block and changing one line >>> implementing a reusable function. :-)

    Part 1
    #!/usr/bin/awk -f
    function bin2dec(bin,	i, bit, dec) {
    	for (i = 1; i <= length(bin); ++i) {
    		bit = substr(bin, i, 1)
    		dec = (dec * 2) + bit
    	}
    
    	return dec
    }
    
    BEGIN { FS = "" }
    
    {
    	for (i = 1; i <= NF; ++i)
    		a[i] += $i
    }
    
    END {
    	for (i in a) {
    		j = j (a[i] >= (NR / 2))
    		k = k (a[i] <= (NR / 2))
    	}
    
    	print bin2dec(j) * bin2dec(k)
    }
    
    Part 2
    #!/usr/bin/awk -f
    function bin2dec(bin,	i, bit, dec) {
    	for (i = 1; i <= length(bin); ++i) {
    		bit = substr(bin, i, 1)
    		dec = (dec * 2) + bit
    	}
    
    	return dec
    }
    
    { input[NR] = $0 }
    
    END {
    	for (i in input) {
    		oxy[i] = input[i]
    		co2[i] = input[i]
    	}
    
    	for (i = 1; i <= length($0); ++i) {
    		split("", bits)
    		for (j in oxy)
    			bits[substr(oxy[j], i, 1)] += 1
    
    		if (!bits[0] || !bits[1])
    			break
    
    		for (j in oxy)
    			if (substr(oxy[j], i, 1) != (bits[1] >= bits[0]))
    				delete oxy[j]
    	}
    
    
    	for (i = 1; i <= length($0); ++i) {
    		split("", bits)
    		for (j in co2)
    			bits[substr(co2[j], i, 1)] += 1
    
    		if (!bits[0] || !bits[1])
    			break
    
    		for (j in co2)
    			if (substr(co2[j], i, 1) != (bits[1] < bits[0]))
    				delete co2[j]
    	}
    
    	for (i in oxy)
    		for (j in co2)
    			print bin2dec(oxy[i]) * bin2dec(co2[j])
    }
    
    5 votes
  6. [2]
    bhrgunatha
    (edited )
    Link
    Racket Notes I like small, easily tested procedures and data that is easily consumed/managed. I converted each line of input into list of characters. I considered vectors but the lines were short...

    Racket

    Notes

    I like small, easily tested procedures and data that is easily consumed/managed. I converted each line of input into list of characters. I considered vectors but the lines were short enough it didn't seem worth it. I'll build up a list of characters for gamma and epsilon that will need converting to a number. Racket's built-in string->number accepts a radix so it's trivial to convert a string of bits.

    (define (encode report) 
      (map string->list report))
    
    (define (decode diagnostic)
      (string->number (list->string diagnostic) 2))
    

    I'll need to count binary digits.
    Originally I transposed the input with the mind-bending (apply map list report) - drop me a note if you want an explanation - but refactored to a positional count instead when I got to Part 2.

    (define (count-bits@ report position)
      (for*/fold ([0s 0]
                  [1s 0])
                 ([r report])
        (if (eq? #\0 (list-ref r position))
            (values (add1 0s) 1s)
            (values 0s (add1 1s)))))
    
    Part 1

    The only strange thing is gamma and epsilon are built by pre-pending a 0 or 1 after counting 1s and 0s from the first position to the last, then reversing the results. It's a very common Lisp/Scheme/Racket idiom since lists are basically similar to linked lists, so pre-pending is constant time and appending is O(n). The same is true appending to strings, it's generally not worth it.

    (define (part-01 input)
      (define report (encode input))
      (for/fold ([gamma null]
                 [epsilon null]
                 #:result (* (decode (reverse gamma))
                             (decode (reverse epsilon))))
                ([position (in-range (length (first report)))])
        (define-values (0s 1s) (count-bits@ report position))
        (if (> 0s 1s)
            (values (cons #\0 gamma) (cons #\1 epsilon))
            (values (cons #\1 gamma) (cons #\0 epsilon)))))
    
    Part 2

    Bit rating criteria returns the bit value to check at each position (as a character)

    (define (o2-rating-criteria report position)
      (define-values (0s 1s) (count-bits@ report position))
      (if (> 0s 1s) #\0 #\1))
    
    (define (co2-rating-criteria report position)
      (define-values (0s 1s) (count-bits@ report position))
      (if (< 1s 0s) #\1 #\0))
    

    Pass the specific criteria to the rating search which is a simple recursive search that filters out invalid ratings until only one is left. As usual, no error handling or validation, since Eric is kind enough to give us input that gives guaranteed results.

    (define (rating diagnostics bit-criteria [position 0])
      (cond [(null? (rest diagnostics)) (decode (first diagnostics))]
            [else (define bit (bit-criteria diagnostics position))
                  (define filtered
                    (for/list ([d diagnostics] #:when (eq? (list-ref d position) bit))
                      d))
                  (rating filtered bit-criteria (add1 position))]))
    

    Then Part 2 is a trivial multiplication

    (define (part-02 input)
      (define report (encode input))
      (define  o2-rating (rating report  o2-rating-criteria))
      (define co2-rating (rating report co2-rating-criteria))
      (* o2-rating co2-rating))
    

    If anyone is actually reading, let me know if I should drop the explanations and just show code since I know I'm overly verbose.

    5 votes
    1. DataWraith
      Link Parent
      No, keep the explanations if you have the time. I don't know Racket myself, but it's fascinating to see how terse the solutions are, and the explanation definitively helps.

      No, keep the explanations if you have the time. I don't know Racket myself, but it's fascinating to see how terse the solutions are, and the explanation definitively helps.

      3 votes
  7. spit-evil-olive-tips
    Link
    part 1 was fairly easy. I used Python's Counter to build up a mapping of bit-position to number of 1s in that position. Part 1 from collections import Counter with open('003.txt') as file: lines =...

    part 1 was fairly easy. I used Python's Counter to build up a mapping of bit-position to number of 1s in that position.

    Part 1
    from collections import Counter
    
    with open('003.txt') as file:
        lines = file.readlines()
    
    counts = Counter()
    
    for line in lines:
        for index, char in enumerate(reversed(line.strip())):
            if char == '1':
                counts[index] += 1
    
    gamma = 0
    epsilon = 0
    for position, count in counts.items():
        if count > 0.5 * len(lines):
            gamma += 2 ** position
        else:
            epsilon += 2 ** position
    
    print(epsilon * gamma)
    

    then part 2...was ugly. I thought it was a rather steep difficulty increase for day 3.

    the biggest stumbling block I had was realizing that it wanted me to re-compute the bit "votes" after each iteration. initially I was only doing that once, at the beginning, and then re-using those results as I narrowed down the candidates.

    Part 2
    from collections import Counter
    
    
    def get_most_common(lines):
        counts = Counter()
        for line in lines:
            for index, char in enumerate(line):
                if char == '1':
                    counts[index] += 1
    
        winners = {}
        for index, count in counts.items():
            if count > 0.5 * len(lines):
                winners[index] = '1'
            elif count < 0.5 * len(lines):
                winners[index] = '0'
    
        return winners
    
    def discard_ratings(ratings, position, value):
        remainder = []
        for rating in ratings:
            if rating[position] == value:
                remainder.append(rating)
    
        return remainder
    
    def find_rating(candidates, most):
        position = 0
        while len(candidates) > 1:
            winners = get_most_common(candidates)
            winner = winners.get(position, '1')
            if not most:
                winner = '1' if winner == '0' else '0'
            candidates = discard_ratings(candidates, position, winner)
            position += 1
    
        return int(candidates[0], 2)
    
    def main():
        with open('003.txt') as file:
            lines = [line.strip() for line in file.readlines()]
    
        og_rating = find_rating(lines.copy(), True)
        co2_rating = find_rating(lines.copy(), False)
        print(og_rating * co2_rating)
    
    main()
    
    4 votes
  8. asterisk
    Link
    Python with open("input.txt") as file: diagnostic = [[int(i) for i in line.strip()] for line in file] γ = list() ε = list() for i in zip(*diagnostic): most = max(i, key=i.count)...
    Python
    with open("input.txt") as file:
        diagnostic = [[int(i) for i in line.strip()] for line in file]
    
    γ = list()
    ε = list()
    
    for i in zip(*diagnostic):
        most = max(i, key=i.count)
        γ.append(str(most))
        ε.append(str(abs(most - 1)))
    
    print("Part One:", int("".join(ε), 2) * int("".join(γ), 2))
    
    
    def bit_criteria(variation: str) -> int:
        variation = 1 if variation == "oxygen" else 0
        rating = diagnostic.copy()
    
        for l in range(len(list(zip(*diagnostic)))):
            bit = list()
    
            for i in rating[:]:
                bit.append(i[l])
    
            ones = bit.count(1)
            zeros = bit.count(0)
    
            if ones == zeros:
                number = 1 if variation else 0
            elif ones > zeros:
                number = 1 if variation else 0
            else:
                number = 0 if variation else 1
    
            for k in rating[:]:
                if k[l] != number:
                    rating.remove(k)
    
            if len(rating) == 1:
                rating = rating[0]
                break
    
        return int("".join([str(i) for i in rating]), 2)
    
    
    print("Part Two:", bit_criteria("oxygen") * bit_criteria("carbon dioxide"))
    
    4 votes
  9. Eabryt
    Link
    My journey to learn Rust continues and today it sort of got the better of me. I got turned around in what I was trying to do and learn, and ended up pulling some bits from the solution @wycy. His...

    My journey to learn Rust continues and today it sort of got the better of me. I got turned around in what I was trying to do and learn, and ended up pulling some bits from the solution @wycy. His (or Her's, or Their!) coding style is similar to mine so looking through it I was able to understand what they were doing, instead of some of the voodoo magic stuff other people are doing.

    On the plus side, my understanding of Rust increased. Plus, I swear all the documentation I find when I search how to do things, makes it needlessly complicated.

    4 votes
  10. Kremor
    (edited )
    Link
    Solutions in Go Part 1 func GetEpsilon(ones, zeros []int) int64 { s := "" for i, n := range ones { if n > zeros[i] { s += "0" } else { s += "1" } } r, _ := strconv.ParseInt(s, 2, 32) return r }...

    Solutions in Go

    Part 1
    func GetEpsilon(ones, zeros []int) int64 {
    	s := ""
    	for i, n := range ones {
    		if n > zeros[i] {
    			s += "0"
    		} else {
    			s += "1"
    		}
    	}
    
    	r, _ := strconv.ParseInt(s, 2, 32)
    
    	return r
    }
    
    func GetGamma(ones, zeros []int) int64 {
    	s := ""
    	for i, n := range ones {
    		if n > zeros[i] {
    			s += "1"
    		} else {
    			s += "0"
    		}
    	}
    
    	r, _ := strconv.ParseInt(s, 2, 32)
    
    	return r
    }
    
    func main() {
    	data := ReadData()
    
    	ones := []int{}
    	zeros := []int{}
    
    	for i := 0; i < len(data[0]); i++ {
    		ones = append(ones, 0)
    		zeros = append(zeros, 0)
    	}
    
    	for _, l := range data {
    		for i, r := range l {
    			if r == '0' {
    				zeros[i] += 1
    			} else {
    				ones[i] += 1
    			}
    		}
    	}
    
    	e := GetEpsilon(ones, zeros)
    	g := GetGamma(ones, zeros)
    
    	fmt.Println(e * g)
    }
    
    Part 2
    type Method func([]string, int) byte
    
    func Count(a *[]string, i int) (int, int) {
    	o := 0
    	z := 0
    	for _, l := range *a {
    		if l[i] == 48 {
    			z += 1
    		} else {
    			o += 1
    		}
    	}
    	return o, z
    }
    
    func GetReading(data []string, f Method) int64 {
    	current := data
    	filtered := []string{}
    	i := 0
    
    	for {
    		c := f(current, i)
    		for _, l := range current {
    			if l[i] == c {
    				filtered = append(filtered, l)
    			}
    		}
    
    		current = filtered
    		filtered = []string{}
    		i++
    
    		if len(current) == 1 {
    			break
    		}
    	}
    
    	r, _ := strconv.ParseInt(current[0], 2, 32)
    	return r
    }
    
    func GetLeastCommon(a []string, i int) byte {
    	o, z := Count(&a, i)
    
    	if o < z {
    		return 49
    	}
    	return 48
    }
    
    func GetMostCommon(a []string, i int) byte {
    	o, z := Count(&a, i)
    
    	if z > o {
    		return 48
    	}
    	return 49
    }
    
    func main() {
    	data := ReadData()
    
    	csr := GetReading(data, GetLeastCommon)
    	ogr := GetReading(data, GetMostCommon)
    
    	fmt.Println(csr * ogr)
    }
    
    3 votes
  11. pnutzh4x0r
    Link
    JavaScript Repo Link Python is my primary language, but in order to get some practice, I've also been trying out some JavaScript. Part 1 function count_columns(report) { let counts = new...

    JavaScript

    Repo Link

    Python is my primary language, but in order to get some practice, I've also been trying out some JavaScript.

    Part 1
    function count_columns(report) {
        let counts = new Array(report[0].length).fill(0);
        report.forEach(number => {
        	for (let index in number) {
                counts[index] += parseInt(number[index]);
            }
        });
        return counts;
    }
    
    function most_common(counts, total) {
        return counts.map(count => 2*count >= total ? '1' : '0')
    }
    
    function least_common(counts, total) {
        return counts.map(count => 2*count <  total ? '1' : '0')
    }
    
    let report  = IO.readlines();
    let counts  = count_columns(report)
    let gamma   = parseInt(most_common(counts, report.length).join(''), 2);
    let epsilon = parseInt(least_common(counts, report.length).join(''), 2);
    IO.print(gamma * epsilon);
    
    Part 2

    Below, I'm only including the parts that are different from Part 1.

    function filter_numbers(report, aggregator) {
        for (let index = 0; report.length > 1; index++) {
        	let counts = count_columns(report);
        	let common = aggregator(counts, report.length);
    
        	report = report.filter(number => number[index] == common[index]);
        }
    
        return parseInt(report[0], 2);
    }
    
    let report = IO.readlines();
    let oxygen = filter_numbers(report, most_common);
    let carbon = filter_numbers(report, least_common);
    IO.print(oxygen * carbon);
    
    2 votes
  12. DataWraith
    Link
    Yeah, this one seemed quite a bit harder than yesterday's. My code could probably be more efficient and nicer, but I've spent enough time on it for today... Part 1 — Rust fn solve(input: &str) ->...

    Yeah, this one seemed quite a bit harder than yesterday's.

    My code could probably be more efficient and nicer, but I've spent enough time on it for today...

    Part 1 — Rust
    fn solve(input: &str) -> u64 {
        let parsed: Vec<u64> = input
            .lines()
            .map(|l| u64::from_str_radix(l, 2).unwrap())
            .collect();
    
        let count = parsed.len() as u64;
        let length = input.lines().next().unwrap().len();
    
        let mut gamma = 0;
        let mut epsilon = 0;
    
        for i in 0..length {
            let bit = 1 << i;
            let freq = parsed.iter().fold(0, |acc, num| acc + ((num & bit) >> i));
    
            if freq > count - freq {
                gamma |= bit
            } else {
                epsilon |= bit
            }
        }
    
        gamma * epsilon
    }
    
    fn main() {
        println!("{}", solve(include_str!("../../input-03.txt")));
    }
    
    Part 2 — Rust
    fn filtered(input: Vec<u64>, position: usize, cmp: fn(u64, u64) -> bool) -> Vec<u64> {
        let bit_pos = 1 << position;
    
        let freq = input
            .iter()
            .fold(0, |acc, num| acc + ((num & bit_pos) >> position));
    
        // Keep the ones if cmp() returns true, otherwise the zeros
        let check_bit = if cmp(freq, input.len() as u64 - freq) {
            bit_pos
        } else {
            0
        };
    
        input
            .iter()
            .filter(|&x| (x & bit_pos) == check_bit)
            .copied()
            .collect()
    }
    
    fn solve(input: &str) -> u64 {
        let parsed: Vec<u64> = input
            .lines()
            .map(|l| u64::from_str_radix(l, 2).unwrap())
            .collect();
    
        let length = input.lines().next().unwrap().len();
    
        let mut oxygen = parsed.clone();
        let mut co2 = parsed.clone();
    
        for i in (0..length).rev() {
            // Keep the one bit if the frequencies are equal
            oxygen = filtered(oxygen, i, |one_freq, zero_freq| one_freq >= zero_freq);
    
            if oxygen.len() == 1 {
                break;
            }
        }
    
        for i in (0..length).rev() {
            // Do the opposite: keep the zero bit if the frequencies are equal
            co2 = filtered(co2, i, |one_freq, zero_freq| one_freq < zero_freq);
    
            if co2.len() == 1 {
                break;
            }
        }
    
        oxygen[0] * co2[0]
    }
    
    fn main() {
        println!("{}", solve(include_str!("../../input-03.txt")));
    }
    
    2 votes
  13. blitz
    Link
    I think I'm finally starting to understand Rust lifetimes! I wanted to pass around a bunch of references and tried my best to avoid reallocating or moving Strings, and this is what I came up with!...

    I think I'm finally starting to understand Rust lifetimes! I wanted to pass around a bunch of references and tried my best to avoid reallocating or moving Strings, and this is what I came up with!

    Sourcehut Link

    I'm still not super adept at manipulating types of references, so if anyone has any suggestions for how I could clean those up (Is there something better than &[&String]?) I'd love to hear feedback.

    2 votes
  14. Gyrfalcon
    Link
    Part 2 was a big oof for me. First I misunderstood it, and then I spent a lot of time wondering if I understood integer division and the answer is still only maybe. Also, the function that I reuse...

    Part 2 was a big oof for me. First I misunderstood it, and then I spent a lot of time wondering if I understood integer division and the answer is still only maybe. Also, the function that I reuse in part 2... didn't appear until I read part 2 lol. I also tried to be fancy using the Nim include prelude thing to have it import a lot of commonly used standard library stuff but it only worked for some of the imports it claims to do? Oh and I keep feeling like I am reading in the files in the least idiomatic way possible, but they changed the readLines function to require you to specify the number of lines, which is not too helpful.

    Part 1
    import sugar, std/[strformat, sequtils, strutils, math]
    
    proc getOnes(data: seq[string]): seq[int] =
      var ones: seq[int] = collect(newSeq):
        for idx in 1 .. data[0].len: 0
    
      for value in data:
        for index, digit in value:
          if digit == '1':
            inc ones[index]
    
      return ones
    
    var input: seq[string] = split(readFile("input.txt"),  "\n")
    input = input[0 .. ^2] # strip off trailing newline
    var ones: seq[int] = getOnes(input)
    
    var gamma: string = ""
    var epsilon: string = ""
    
    for value in ones:
      if value > (input.len div 2):
        gamma = gamma & "1"
        epsilon = epsilon & "0"
      else:
        gamma = gamma & "0"
        epsilon = epsilon & "1"
    
    
    var product: int = parseBinInt(gamma)*parseBinInt(epsilon)
    
    echo &"Gamma: {gamma}\nEpsilon: {epsilon}\nProduct: {product}"
    
    Part 2
    var generator: seq[string] = input
    var generatorOnes: seq[int] = ones
    var compareIdx: int = 0
    
    while generator.len > 1:
      if generatorOnes[compareIdx] >= int(ceil(generator.len / 2)):
        generator = filter(generator, proc(x: string): bool = x[compareIdx] == '1')
      else:
        generator = filter(generator, proc(x: string): bool = x[compareIdx] == '0')
      generatorOnes = getOnes(generator)
      inc compareIdx
    
    var scrubber: seq[string] = input
    var scrubberOnes: seq[int] = ones
    compareIdx = 0
    
    while scrubber.len > 1:
      if scrubberOnes[compareIdx] >= int(ceil(scrubber.len / 2)):
        scrubber = filter(scrubber, proc(x: string): bool = x[compareIdx] == '0')
      else:
        scrubber = filter(scrubber, proc(x: string): bool = x[compareIdx] == '1')
      scrubberOnes = getOnes(scrubber)
      inc compareIdx
    
    var secondProduct: int = parseBinInt(generator[0])*parseBinInt(scrubber[0])
    
    echo &"Generator: {generator[0]}\nScrubber: {scrubber[0]}\nProduct: {secondProduct}"
    
    2 votes
  15. 3d12
    Link
    Haha, this one absolutely killed me. While I defeated my own purpose here in my haste to get caught up on the problems I was behind on, I did manage to get a functional solution regardless of its...

    Haha, this one absolutely killed me. While I defeated my own purpose here in my haste to get caught up on the problems I was behind on, I did manage to get a functional solution regardless of its readability. I've already promised myself no more of this sloppy type of implementation, since my goal here is to get familiar with the more convenient parts of Node/JS and not just implement everything with indices and for loops. :)

    Part 1
    const fs = require('fs');
    const readline = require('readline');
    
    let binaryInputArr = [];
    
    async function openFileForReading(file) {
    	const fileStream = fs.createReadStream(file);
    
    	const rl = readline.createInterface({
    		input: fileStream,
    		crlfDelay: Infinity
    	});
    
    	for await (const line of rl) {
    		try {
    			//directionArr.push(parseInt(line));
    			binaryInputArr.push(line);
    		} catch(e) {
    			console.error(e);
    		}
    	}
    }
    
    (async function mainExecution() {
    	await openFileForReading('input.txt');
    	let positionalCounts = [];
    	const lengthCheckString = binaryInputArr[0];
    	for (let i=0; i<lengthCheckString.length; i++) {
    		positionalCounts.push({ zeroes: 0, ones: 0 });
    	}
    	for (const binaryInput of binaryInputArr) {
    		for (let i=0; i<binaryInput.length; i++) {
    			if (binaryInput[i] === '0') {
    				positionalCounts[i].zeroes++;
    			} else if (binaryInput[i] === '1') {
    				positionalCounts[i].ones++;
    			}
    		}
    	}
    	let gammaRate = [];
    	let epsilonRate = [];
    	for (let i=0; i<positionalCounts.length; i++) {
    		if (positionalCounts[i].zeroes > positionalCounts[i].ones) {
    			gammaRate.push('0');
    			epsilonRate.push('1');
    		} else if (positionalCounts[i].ones > positionalCounts[i].zeroes) {
    			gammaRate.push('1');
    			epsilonRate.push('0');
    		}
    	}
    	let gammaRateString = gammaRate.join('');
    	let gammaRateInt = parseInt(gammaRateString, 2);
    	let epsilonRateString = epsilonRate.join('');
    	let epsilonRateInt = parseInt(epsilonRateString, 2);
    	console.log("Answer found! " + (gammaRateInt * epsilonRateInt));
    })();
    
    Part 2
    const fs = require('fs');
    const readline = require('readline');
    
    let binaryInputArr = [];
    
    async function openFileForReading(file) {
    	const fileStream = fs.createReadStream(file);
    
    	const rl = readline.createInterface({
    		input: fileStream,
    		crlfDelay: Infinity
    	});
    
    	for await (const line of rl) {
    		try {
    			//directionArr.push(parseInt(line));
    			binaryInputArr.push(line);
    		} catch(e) {
    			console.error(e);
    		}
    	}
    }
    
    (async function mainExecution() {
    	await openFileForReading('input.txt');
    	let oxygenGenRating = '';
    	let filteredArray = binaryInputArr;
    	let positionalCounts = [];
    	let binaryInputIndex = 0;
    	while (oxygenGenRating === '') {
    		if (filteredArray.length === 1) {
    			oxygenGenRating = filteredArray[0];
    			break;
    		}
    		const lengthCheckString = filteredArray[0];
    		positionalCounts = [];
    		for (let i=0; i<lengthCheckString.length; i++) {
    			positionalCounts.push({ zeroes: 0, ones: 0 });
    		}
    		for (let i=0; i<filteredArray.length; i++) {
    			let currentString = filteredArray[i];
    			for (let j=0; j<positionalCounts.length; j++) {
    				if (currentString[j] === '0') {
    					positionalCounts[j].zeroes++;
    				} else if (currentString[j] === '1') {
    					positionalCounts[j].ones++;
    				}
    			}
    		}
    		let filterChoice = '';
    		if (positionalCounts[binaryInputIndex].zeroes > positionalCounts[binaryInputIndex].ones) {
    			filterChoice = '0';
    		} else if (positionalCounts[binaryInputIndex].ones >= positionalCounts[binaryInputIndex].zeroes) {
    			filterChoice = '1';
    		}
    		filteredArray = filteredArray.filter(binaryNumber => binaryNumber[binaryInputIndex] === filterChoice);
    		binaryInputIndex++;
    	}
    	let co2ScrubRating = '';
    	let filteredArray2 = binaryInputArr;
    	positionalCounts = [];
    	binaryInputIndex = 0;
    	while (co2ScrubRating === '') {
    		if (filteredArray2.length === 1) {
    			co2ScrubRating = filteredArray2[0];
    			break;
    		}
    		const lengthCheckString = filteredArray2[0];
    		positionalCounts = [];
    		for (let i=0; i<lengthCheckString.length; i++) {
    			positionalCounts.push({ zeroes: 0, ones: 0 });
    		}
    		for (let i=0; i<filteredArray2.length; i++) {
    			let currentString = filteredArray2[i];
    			for (let j=0; j<positionalCounts.length; j++) {
    				if (currentString[j] === '0') {
    					positionalCounts[j].zeroes++;
    				} else if (currentString[j] === '1') {
    					positionalCounts[j].ones++;
    				}
    			}
    		}
    		let filterChoice = '';
    		if (positionalCounts[binaryInputIndex].zeroes <= positionalCounts[binaryInputIndex].ones) {
    			filterChoice = '0';
    		} else if (positionalCounts[binaryInputIndex].ones < positionalCounts[binaryInputIndex].zeroes) {
    			filterChoice = '1';
    		}
    		filteredArray2 = filteredArray2.filter(binaryNumber => binaryNumber[binaryInputIndex] === filterChoice);
    		binaryInputIndex++;
    	}
    	let oxygenGenRatingDecimal = parseInt(oxygenGenRating,2);
    	let co2ScrubRatingDecimal = parseInt(co2ScrubRating,2);
    	console.log("Answer found! \nOxygen Generator Rating: " + oxygenGenRatingDecimal
    		+ "\nCO2 Scrubber Rating: " + co2ScrubRatingDecimal
    		+ "\n" + oxygenGenRatingDecimal + " * " + co2ScrubRatingDecimal + " = " + (oxygenGenRatingDecimal * co2ScrubRatingDecimal));
    })();
    
    2 votes
  16. wycy
    Link
    Rust Kind of gross, but it works. Rust use std::env; use std::io::{self, prelude::*, BufReader}; use std::fs::File; fn main() { let args: Vec<String> = env::args().collect(); let filename =...

    Rust

    Kind of gross, but it works.

    Rust
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        day03(&filename).unwrap();
    }
    fn epsilon_from_gamma(gamma: &str) -> String {
        let mut epsilon = String::new();
        for ch in gamma.chars() {
            match ch {
                '0' => epsilon.push_str("1"),
                '1' => epsilon.push_str("0"),
                other => panic!("Unknown digit: {}", other),
            }
        }
        epsilon
    }
    fn binary_from_string(string: &str) -> isize {
        isize::from_str_radix(&string, 2).unwrap()
    }
    #[derive(Debug, Clone, Copy)]
    enum SystemType {
        OxygenGenerator,
        CO2Scrubber,
    }
    fn get_generator_scrubber(input: &Vec<String>, system: SystemType) -> isize {
        let mut inputs = input.clone();
        let len = inputs[0].len();
        for i in 0..len {
            if inputs.len() == 1 { break; }
            let mut num0 = 0;
            let mut num1 = 0;
            for number in &inputs {
                match number.chars().nth(i).unwrap() {
                    '0' => num0 += 1,
                    '1' => num1 += 1,
                    other => panic!("Unknown digit: {}", other),
                }
            }
            let keep = match system {
                SystemType::OxygenGenerator => {
                    if num0 > num1 { '0' }
                    else           { '1' }
                },
                SystemType::CO2Scrubber => {
                    if num0 > num1 { '1' }
                    else           { '0' }
                },
            };
            inputs.retain(|x| x.chars().nth(i).unwrap() == keep);
        }
        binary_from_string(inputs.iter().next().unwrap())
    }
    fn day03(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 len = input[0].len();
        let mut gamma_rate = String::new();
        for i in 0..len {
            let mut num0 = 0;
            let mut num1 = 0;
            for number in &input {
                match number.chars().nth(i).unwrap() {
                    '0' => num0 += 1,
                    '1' => num1 += 1,
                    other => panic!("Unknown digit: {}", other),
                }
            }
            if num0 > num1 {
                gamma_rate.push_str("0");
            } else {
                gamma_rate.push_str("1");
            }
        }
        let epsilon_rate = epsilon_from_gamma(&gamma_rate);
        let gamma_rate   = binary_from_string(&gamma_rate);
        let epsilon_rate = binary_from_string(&epsilon_rate);
        println!("Part 1: {}", gamma_rate * epsilon_rate); // 3895776
        // Part 2
        let o2  = get_generator_scrubber(&input, SystemType::OxygenGenerator);
        let co2 = get_generator_scrubber(&input, SystemType::CO2Scrubber);
        println!("Part 2: {}", o2 * co2); // 7928162
        Ok(())
    }
    
    1 vote
  17. ras
    Link
    Yeah, I'm not going to lie, part 2 today threw me for a little loop. The other exercises up until that point were pretty breezy, but I had to spend a good bit of time reading the requirements for...

    Yeah, I'm not going to lie, part 2 today threw me for a little loop. The other exercises up until that point were pretty breezy, but I had to spend a good bit of time reading the requirements for this one just to figure out what I needed to do.

    Common code - Javascript
    const bitCount = (readings) => 
        readings
        .map((num) => num.split(""))
        .reduce((acc, number) => {
          number.forEach((digit, i) => {
            if (!acc[i]) {
              acc[i] = { '0': 0, '1': 0 };
            }
            acc[i][digit]++;
          });
          return acc;
        }, []);
    
    Part 1 - Javascript
    // Got the input into an array of strings called data
    
    const calculatePowerConsumption = (readings) => {
        let gamma = bitCount(readings).map(currentReadingCount => currentReadingCount["1"] > readings.length / 2 ? "1" : "0").join("");
        let epsilon = bitCount(readings).map(currentReadingCount => currentReadingCount["0"] > readings.length / 2 ? "1" : "0").join("");
    
        return parseInt(gamma, 2) * parseInt(epsilon, 2);
    }
    
    let result = calculatePowerConsumption(data);
    
    Part 2 - Javascript

    I thought about refactoring this to a more general solution, but decided not to.

    const calculateOxygenGeneratorRating = (readings) => {
      let position = 0;
      while (readings.length > 1) {
        let currentReadingCount = bitCount(readings)[position];
        readings = readings.filter(
          (reading) =>
            reading[position] === (currentReadingCount["1"] >= readings.length / 2 ? "1" : "0")
        );
        position++;
      }
    
      return readings[0];
    };
    
    const calculateCO2GeneratorRating = (readings) => {
      let position = 0;
      while (readings.length > 1) {
        let currentReadingCount = bitCount(readings)[position];
        readings = readings.filter(
          (reading) => reading[position] === (currentReadingCount["1"] >= currentReadingCount["0"] ? "0" : "1")
        );
        position++;
      }
    
      return readings[0];
    };
    
    result = parseInt(calculateOxygenGeneratorRating([...data]), 2) * parseInt(calculateCO2GeneratorRating([...data]), 2);
    
    1 vote