PapaNachos's recent activity

  1. Comment on What did you do this week? in ~talk

    PapaNachos
    Link Parent
    Yeah, I've talked about it a few times. That was one of the last pieces I made before our maker space shut down. And it's the one I'm most proud of. But I'm so excited to have a laser again. I'm...

    Yeah, I've talked about it a few times. That was one of the last pieces I made before our maker space shut down. And it's the one I'm most proud of. But I'm so excited to have a laser again.

    I'm glad you think it's a good idea too. It's encouraging to hear more people think we're on the right track. And hopefully it's something that will bring people in and keep them coming back. We want to build an inclusive community since ultimately board games are a social experience.

    2 votes
  2. Comment on What did you do this week? in ~talk

    PapaNachos
    Link Parent
    Thanks! That piece was for a charity raffle during SGDQ 2016! I actually got some help from the dev team. There's definitely some really neat stuff on Etsy, but we're not currently on there, we...

    Thanks! That piece was for a charity raffle during SGDQ 2016! I actually got some help from the dev team.

    There's definitely some really neat stuff on Etsy, but we're not currently on there, we have our own site. Soon we'll include a much larger selection of products we build ourselves, but for now our selection is somewhat limited.

    Our end goal is to open a board game store over in Detroit with a small maker space built in. But for numerous reasons that's difficult at the moment, so we're working on saving up by starting to produce accessories for games.

    We feel there's a natural synergy with having a small maker space incorporated into our store because we believe people will want all sorts of custom components and accessories. So 3D printed figures, terrain, organizers, etc...

    2 votes
  3. Comment on What did you do this week? in ~talk

    PapaNachos
    Link
    My girlfriend and I are working to open a business and as part of that we're getting back into wood burning. We used to make all kinds of art work, but unfortunately the makerspace we used shut...

    My girlfriend and I are working to open a business and as part of that we're getting back into wood burning.

    We used to make all kinds of art work, but unfortunately the makerspace we used shut down, so we lost access to a laser. And most lasers that can cut through the sort of materials we prefer to use are either extremely expensive, highly sketchy, or both.

    But now I'm happy to announce that our new laser finally arrived. It's an Epilog Fusion Edge 60W, which is definitely not cheap, but is essentially a newer, fancier version of what we used to use, so it should have the cutting power we're looking for. And we know Epilog is a great brand because we've used them before.

    And this one is ours, so we don't need to share it or only use it for a few hours at a time. I'm hoping we can create some awesome stuff that will help our business grow.

    6 votes
  4. Comment on Day 21: Allergen Assessment in ~comp.advent_of_code

    PapaNachos
    Link
    The holidays are getting busier, so I think I'm going to bow out for the rest of the year. I might come back later, but I'm not going to try to keep up with these day-of. It's been fun though, and...

    The holidays are getting busier, so I think I'm going to bow out for the rest of the year. I might come back later, but I'm not going to try to keep up with these day-of.

    It's been fun though, and I hope everyone learned something along the way! Best of luck to those of your sticking with it.

    4 votes
  5. Comment on Day 20: Jurassic Jigsaw in ~comp.advent_of_code

    PapaNachos
    Link Parent
    Spoilers I don't know if it holds for part B yet, but at least for my data set that assumption held for what I considered important for part A.
    Spoilers

    I don't know if it holds for part B yet, but at least for my data set that assumption held for what I considered important for part A.

    3 votes
  6. Comment on Day 20: Jurassic Jigsaw in ~comp.advent_of_code

    PapaNachos
    (edited )
    Link
    Part A has some interesting tricks to it. Part B is uh... Part B will be interesting. Some of the shortcuts I used for part A definitely won't help with part B. Edit: I made a ton of progress on...

    Part A has some interesting tricks to it. Part B is uh... Part B will be interesting. Some of the shortcuts I used for part A definitely won't help with part B.

    Edit: I made a ton of progress on part B, but I've had a headache all day and this isn't helping. I'll probably come back to this one in the future. We'll see how I'm feeling later tonight

    Day 20 Part A – Python

    For this one I use the same trick I used on day 5 (replacing the text with binary), Then I also reverse the strings, and I convert them to decimal. That gives me 8 numbers for each tile that I can then compare.

    From there, I compare each tile's borders to each other tile. The ones that only match up 4 times (2 borders x2 because flipped) are the corner pieces.

    #tiles = test_data.split('\n\n')
    tiles = input_data.split('\n\n')
    
    tile_dict = {}
    
    class tile(object):
        def __init__(self, name, val):
            self.name = name
            self.original_data = val
            self.all_configurations = None
            self.all_borders = None
        def you_spin_me_right_round(self):
            #this should handle flipping, but not rotations
            front = []
            back = []
            for row in self.original_data:
                front.append(row[0])
                back.append(row[-1])
            borders = [''.join(front), ''.join(back), ''.join(self.original_data[0]),''.join(self.original_data[-1])]
            flipped_borders = []
            for border in borders:
                flipped_borders.append(border[::-1])
            binary_borders = []
            for border in borders + flipped_borders:
                border = border.replace('.', '0')
                border = border.replace('#', '1')
                binary_borders.append(int(border,2))
            #border configurations can go here if they're necessary
            self.all_borders = binary_borders
    
    for tile_data in tiles:
        name = tile_data.split('\n')[0][5:9]
        new_tile = tile(name, list(map(list,tile_data.split('\n')[1:])))
        new_tile.you_spin_me_right_round()
        tile_dict[name] = new_tile
    
    tile_scores = {}
    for tile_key in tile_dict.keys():
        tile_score = 0
        base_tile = tile_dict[tile_key]
        for comparision_tile_key in tile_dict.keys():
            if tile_key is not comparision_tile_key:
                for border in base_tile.all_borders:
                    if border in tile_dict[comparision_tile_key].all_borders:
                        tile_score = tile_score + 1
        tile_scores[tile_key] = tile_score
    score_product = 1
    for key in tile_scores.keys():
        if tile_scores[key] == 4:
            score_product = score_product * int(key)
            print(key)
    print(score_product)
    
    Tips and Commentary
    • For part A you only really need to care about the borders

    • The borders can be converted into numbers a few different ways

    4 votes
  7. Comment on Day 19: Monster Messages in ~comp.advent_of_code

    PapaNachos
    Link
    Wow that one was hard! I was about to give up and go to bed when I finally got it working! I'm kinda proud of the regex-powered monstrosity I built. I was even able to incorporate some of the...

    Wow that one was hard! I was about to give up and go to bed when I finally got it working! I'm kinda proud of the regex-powered monstrosity I built. I was even able to incorporate some of the challenges from previous days to make parts of it work.

    Part A was a solid challenge, but Part B added a huge level of complexity.

    Day 19 Part A – Python

    For part A I build a tree out of nodes, sort of like the bag problem on day 7. Then I built a recursive function that digs down from rule 0 all the way to the bottom of the tree, taking every branch possible. Andon the way back up through the tree I auto-generate a regex pattern that accounts for all the branches and possibilities.

    This beautiful bastard in all it's horrific glory:

    (a((((b((ba|a(b|a))a|(bb|aa)b)|ab(bb|ab))a|((b(ab|aa)|a(ba|ab))b|((bb|ab)a|(aa|(b|a)b)b)a)b)b|((b(b(ba|ab)|a(b|a)(b|a))|a(bbb|a(bb|ab)))a|(b((aa|(b|a)b)a|(ba|ab)b)|a(aba|bbb))b)a)a|(a(a((b(b|a)(b|a)|a(ba|bb))a|(b(ba|aa)|aaa)b)|b(b(a(ba|ab)|bab)|a(a(bb|aa)|b(bb|ab))))|b(((abb|(aa|(b|a)b)a)a|(a(bb|aa)|b(bb|ab))b)a|(baab|((ba|bb)a|bbb)a)b))b)|b((a(a(((ba|aa)a|(bb|ab)b)b|((ab|b(b|a))b|(aa|(b|a)b)a)a)|b(a(b(ab|b(b|a))|a(b|a)(b|a))|b(b(bb|aa)|a(ab|aa))))|b(a((abb|b(bb|ab))b|(b|a)(ba|ab)a)|b(a(a(aa|(b|a)b)|b(ba|ab))|b(b(ba|aa)|aba))))a|(b((a(b(bb|aa)|aaa)|b(b(aa|(b|a)b)|a(b|a)(b|a)))b|(((ba|bb)a|bbb)b|(b(bb|aa)|a(ab|aa))a)a)|a(a((ab|b(b|a))b|baa)a|b(a(b(ba|aa)|a(aa|(b|a)b))|b(a(aa|(b|a)b)|b(ab|b(b|a))))))b))(a((((b((ba|a(b|a))a|(bb|aa)b)|ab(bb|ab))a|((b(ab|aa)|a(ba|ab))b|((bb|ab)a|(aa|(b|a)b)b)a)b)b|((b(b(ba|ab)|a(b|a)(b|a))|a(bbb|a(bb|ab)))a|(b((aa|(b|a)b)a|(ba|ab)b)|a(aba|bbb))b)a)a|(a(a((b(b|a)(b|a)|a(ba|bb))a|(b(ba|aa)|aaa)b)|b(b(a(ba|ab)|bab)|a(a(bb|aa)|b(bb|ab))))|b(((abb|(aa|(b|a)b)a)a|(a(bb|aa)|b(bb|ab))b)a|(baab|((ba|bb)a|bbb)a)b))b)|b((a(a(((ba|aa)a|(bb|ab)b)b|((ab|b(b|a))b|(aa|(b|a)b)a)a)|b(a(b(ab|b(b|a))|a(b|a)(b|a))|b(b(bb|aa)|a(ab|aa))))|b(a((abb|b(bb|ab))b|(b|a)(ba|ab)a)|b(a(a(aa|(b|a)b)|b(ba|ab))|b(b(ba|aa)|aba))))a|(b((a(b(bb|aa)|aaa)|b(b(aa|(b|a)b)|a(b|a)(b|a)))b|(((ba|bb)a|bbb)b|(b(bb|aa)|a(ab|aa))a)a)|a(a((ab|b(b|a))b|baa)a|b(a(b(ba|aa)|a(aa|(b|a)b))|b(a(aa|(b|a)b)|b(ab|b(b|a))))))b))(a(((a(b(b(bb|aa)|aaa)|a(a(b|a)(b|a)|b(ba|aa)))|b(b((ba|ab)a|(bb|ab)b)|a(bba|(ba|aa)b)))b|(((bba|a(b|a)(b|a))a|(aba|bbb)b)a|((abb|(aa|(b|a)b)a)a|((ba|a(b|a))a|(bb|aa)b)b)b)a)a|(((b(b(ab|b(b|a))|a(b|a)(b|a))|a(a(b|a)(b|a)|b(ba|bb)))b|((b(ba|a(b|a))|aaa)b|(aba|b(ab|aa))a)a)b|((b(b(aa|(b|a)b)|aab)|a(b(ab|b(b|a))|aaa))a|((baa|a(ba|bb))b|((aa|(b|a)b)a|(ba|bb)b)a)b)a)b)|b(a((((bbb|a(bb|ab))a|(bba|a(ba|aa))b)b|((bbb|(bb|aa)a)b|(aba|bab)a)a)a|((a(a(ba|a(b|a))|bba)|b(bba|(ba|aa)b))a|(a(b|a)(b|a)|bab)(b|a)b)b)|b((((b(ab|b(b|a))|aaa)b|(a(b|a)(b|a)|b(ba|bb))a)b|(a((ba|aa)a|(bb|ab)b)|b(a(ba|ab)|baa))a)a|(((a(b|a)(b|a)|bab)b|(b(ba|ab)|a(ba|bb))a)a|((bbb|a(bb|ab))a|(aab|(ab|aa)a)b)b)b)))
    

    I just run that fucker on every line of my data set. And if they behave I count them. Now for the actual code

    import re
    
    #rules, data, = test_data.split('\n\n')
    rules, data, = input_data.split('\n\n')
    rules = rules.split('\n')
    data = data.split('\n')
    
    rule_dict = {}
    
    pattern_read_rules = re.compile('(\d+): (.+)')
    pattern_read_char = re.compile('"(\w+)"')
    
    message_nodes = {}
    class message_node(object):
        def __init__(self, name, val):
            self.name = name
            self.children = val
        def find_paths(self):
            if type(self.children) == str:
                return self.children
            if len(self.children) > 1:
                path_list = []
                for child in self.children:
                    path = ''
                    for rule in child.split(' '):
                        path = path + message_nodes[rule].find_paths()
                    path_list.append(path)
                path_str = '(' + '|'.join(path_list) + ')'
                return path_str
            else:
                path = ''
                for rule in self.children[0].split(' '):
                    path = path + message_nodes[rule].find_paths()
                return path
    for row in rules:
        match = pattern_read_rules.search(row)
        index = match.group(1)
        options = match.group(2)
        if '"' in options:
            #base rule
            base = pattern_read_char.search(options)
            val = base.group(1)
        else:
            val = options.split(' | ')
        rule_dict[index] = val
        message_nodes[index] = message_node(index, val)
    
    pattern_zero = re.compile('^' + message_nodes['0'].find_paths() + '$')
    matches = []
    for row in data:
        match = pattern_zero.search(row)
        if match is not None:
            matches.append(match.group(0))
    print(len(matches))
    
    Day 19 Part B – Python

    The rule change for Part B is simple, but add a great deal of brain-fuckery to the problem. It took several hours of wrestling regex into submission until I finally had a breakthrough.

    Based on the new rules, I just count how many copies of rule 42 and rule 31 are at the beginning and end of the code. Based staring at the math for a bit, as long as these are all true:

    count of matches of rule 42 >= 2
    count of matches of rule 31 >= 1
    (count of matches of rule 42 - count of matches of rule 31) >= 1
    

    Which is to say, I need at least 2 of rule 42, at least 1 of 31 AND I need rule 42 to occur more times than rule 31. That behavior should be standard for everyone, since it's part of the problem description

    Knowing all that, I count and remove extra copies of pattern 42 from the beginning and pattern 31 from the end.

    Then I do one final pass to see if the new, shortened message follows the original rule 0. For some reason I was having trouble dealing with messages that followed the beginning and end, but had other stuff in the middle

    import re
    
    #rules, data, = test_data_2.split('\n\n')
    rules, data, = input_data.split('\n\n')
    rules = rules.split('\n')
    data = data.split('\n')
    
    rule_dict = {}
    
    pattern_read_rules = re.compile('(\d+): (.+)')
    pattern_read_char = re.compile('"(\w+)"')
    
    message_nodes = {}
    class message_node(object):
        def __init__(self, name, val):
            self.name = name
            self.children = val
        def find_paths(self):
            if type(self.children) == str:
                return self.children
            if len(self.children) > 1:
                path_list = []
                for child in self.children:
                    path = ''
                    for rule in child.split(' '):
                        path = path + message_nodes[rule].find_paths()
                    path_list.append(path)
                path_str = '(' + '|'.join(path_list) + ')'
                return path_str
            else:
                path = ''
                for rule in self.children[0].split(' '):
                    path = path + message_nodes[rule].find_paths()
                return path
    for row in rules:
        match = pattern_read_rules.search(row)
        index = match.group(1)
        options = match.group(2)
        if '"' in options:
            #base rule
            base = pattern_read_char.search(options)
            val = base.group(1)
        else:
            val = options.split(' | ')
        rule_dict[index] = val
        message_nodes[index] = message_node(index, val)
    
    pattern_42_start = re.compile('^(' + message_nodes['42'].find_paths() + ')' + message_nodes['42'].find_paths())
    pattern_31_end = re.compile('(' + message_nodes['31'].find_paths() + ')$')
    pattern_zero = re.compile('^' + message_nodes['0'].find_paths() + '$')
    
    matches = []
    for row in data:
        matches_42 = pattern_42_start.search(row)
        matches_31 = pattern_31_end.search(row)
        
        count_42 = 1
        count_31 = 0
        modified_row = row
        reduced_row = row
        while matches_42:
            count_42 = count_42 + 1
            reduced_row = modified_row
            modified_row = modified_row[len(matches_42.group(1)):]
            matches_42 = pattern_42_start.search(modified_row)
        modified_row = reduced_row
        while matches_31:
            count_31 = count_31 + 1
            reduced_row = modified_row
            modified_row = modified_row[:-len(matches_31.group(1))]
            matches_31 = pattern_31_end.search(modified_row)
        if count_42 >= 2 and count_31 >= 1 and count_42-count_31 >= 1:
            match = pattern_zero.search(reduced_row)
            if match is not None:
                matches.append(match.group(0))
        
    print(len(matches))
    
    Tips and Commentary
    • This one is hard, like really hard. Don't be discouraged if you can't figure it out. But still, it's an interesting challenge. And if you find yourself screaming 'Fuck... FUCK... fuck... what the fuck? FFFFFFUUUUUUCCCCCCCCKKKKKK!' don't worry, that's a normal reaction to this problem.

    • This is a great opportunity to use some of what you've learned from previous days. In particular your solution to day 7 may be helpful.

    • Your program can auto-generate regex patterns that you can then use. But you really need to know your way around regex if you want to try that. Might be worth reading up on some of the documentation for what regex commands are available in your preferred language

    • For part B don't try to build a general usage solution to this problem unless you're truly a masochist. I would suggest studying the specific rule chances that occurred and seeing how you can work with them

    • I ended up having 3 different regex patterns that I used for each pass. One to deal with the beginning, one to deal with the end, and one to deal with the middle.

    3 votes
  8. Comment on Day 18: Operation Order in ~comp.advent_of_code

    PapaNachos
    (edited )
    Link
    I ran into a bug that only effected 4 of my equations and was very hard to track down. I had to run someone else's per-equation answers through diffchecker to even figure out why the fuck my...

    I ran into a bug that only effected 4 of my equations and was very hard to track down. I had to run someone else's per-equation answers through diffchecker to even figure out why the fuck my answer wasn't correct. More on the specifics in the spoilery parts.

    I'm legitimately impressed with how small some people can make these solvers.

    Tonight's eurobeat is DEJA VU

    Day 17 Part A – Python

    Basically I run my parenthesis method on every row. What parenthesis does is it looks for pairs of parenthesis and then sends the internals over to my string_solve method, which handles reading through a string using the new order of operations. Then it swaps in the the solution in place of the old parenthesis and checks again. It keeps going like that until there are no more parenthesis.

    Once all the parenthesis have been dealt with, it sends all the remaining parts to string_solve one last time, and that's the answer

    Basically everything finds a small portion of the equation that it knows how to deal with, the it simplifies it and runs replace to swap the more complex version out for the simpler version

    The bug that was taking forever to solve was that my 'replace' functions didn't have the 'DO THIS ONLY ONCE' command, so in some rare cases (4, I counted). It would swap out multiple portions of the equation... which fucked up the result but didn't cause any errors.

    import re
    
    #data = test_data.split('\n')
    data = input_data.split('\n')
    
    pattern = re.compile('\((.+)\)')
    pattern2 = re.compile('\(([\d+*\s]+)\)')
    pattern3 = re.compile('(\d+)\s([+*])\s(\d+)')
    
    def parenthesis(equation):
        while True:
            matches = pattern2.findall(equation)
            if len(matches) > 0:
                for match in matches:
                    equation = equation.replace('(' + match + ')', string_solve(match),1)
            else:
                equation = string_solve(equation)
                break
        return equation
    
    def string_solve(equation): 
        while True:
            match = pattern3.search(equation)
            if match is not None:
                left = match.group(1)
                right = match.group(3)
                symbol = match.group(2)
                if symbol == '+':
                    equation = equation.replace(str(match.group(0)), str(int(left) + int(right)),1)
                elif symbol == '*':
                    equation = equation.replace(str(match.group(0)), str(int(left) * int(right)),1)
                else:
                    print('Something fucked up')
            else:
                break
        return equation
        
    running_sum = 0
    for row in data:
        running_sum = running_sum + int(parenthesis(row))
    print(running_sum)
    
    Day 17 Part B – Python

    This basically works the same way as part A, but I add a new regex pattern and do all the '+'s before the '*'s in string_solve

    import re
    
    #data = test_data.split('\n')
    data = input_data.split('\n')
    
    pattern = re.compile('\((.+)\)')
    pattern2 = re.compile('\(([\d+*\s]+)\)')
    pattern3 = re.compile('(\d+)\s([+])\s(\d+)')
    pattern4 = re.compile('(\d+)\s([*])\s(\d+)')
    
    def parenthesis(equation):
        while True:
            matches = pattern2.findall(equation)
            if len(matches) > 0:
                for match in matches:
                    equation = equation.replace('(' + match + ')', string_solve(match),1)
            else:
                equation = string_solve(equation)
                break
        return equation
    
    def string_solve(equation): 
        while True:
            match = pattern3.search(equation)
            if match is not None:
                left = match.group(1)
                right = match.group(3)
                equation = equation.replace(str(match.group(0)), str(int(left) + int(right)),1)
            else:
                break
        while True:
            match = pattern4.search(equation)
            if match is not None:
                left = match.group(1)
                right = match.group(3)
                equation = equation.replace(str(match.group(0)), str(int(left) * int(right)),1)
            else:
                break
        return equation
        
    running_sum = 0
    for row in data:
        #print(int(parenthesis(row)), '=', row)
        running_sum = running_sum + int(parenthesis(row))
    print(running_sum)
    
    Tips and Commentary
    • There are very simple (minimal code required, as opposed to easy) ways to solve this one, if you take advantage of some of the built in functions for your preferred language

    • You may have an easier time if you subdivide the tasks of 'dealing with parenthesis' and 'doing the actual math'

    • If you're having trouble with your results, it may help to compare to a 'known good'. For example, some of my equations weren't active properly, but I couldn't figure out which ones, because all the ones I validated by hand came back correct. So I used someone else's method to calculate all the individual equations and compared it to my results. That helped me isolate the few equations that were giving me trouble. This method can be extremely viable in the 'real world' but that depends on the nature of the work you're doing

    • If you end up swapping around portions of your equations using something like python's replace method, be careful that you're only swapping out what you want to, and not grabbing anything extra by mistake. For example, one of my equations looked like this:

    2 + 2 + 8 * 4 + (4 + 4) * 5
    2 + 2 + 8 * 4 + 8 * 5
    4 + 8 * 4 + 8 * 5
    12 * 12 * 5
    144 * 5
    720
    

    When it should have looked like this:

    2 + 2 + 8 * 4 + (4 + 4) * 5
    2 + 2 + 8 * 4 + 8 * 5
    4 + 8 * 4 + 8 * 5
    12 * 4 + 8 * 5
    48 + 8 * 5
    56 * 5
    280
    
    • When you're in the testing phase, you may find it helpful to print out the trace of your equation, to help make sure it's behaving properly
    5 votes
  9. Comment on ‘This is the reality’: Far-right Newsmax and One America channels grapple uneasily with Biden’s electoral college victory in ~news

    PapaNachos
    Link Parent
    Eventually it's just going to be a Markov chain that generates headlines about how owned the libs are.

    Eventually it's just going to be a Markov chain that generates headlines about how owned the libs are.

    3 votes
  10. Comment on Day 17: Conway Cubes in ~comp.advent_of_code

    PapaNachos
    (edited )
    Link
    Part B took a bit to calculate, I'm not really sure how I would even go about speeding it up. Maybe preallocating space? Or an improved search method? As always, listening to eurobeat helps you be...

    Part B took a bit to calculate, I'm not really sure how I would even go about speeding it up. Maybe preallocating space? Or an improved search method?

    As always, listening to eurobeat helps you be better at both code and video games. It's a scientifically proven fact

    Day 17 Part A – Python

    First I give myself 2 methods that help me deal with the neighbors. I only ever store the active cubes, since I didn't want to deal with a resizing grid.

    Each generation of the simulation I go through each of the active cubes to find which of the neighbors I should care about checking. Then, I look at each of them and check their current status and how many active neighbors they have and update the new grid generation accordingly.

    Once I'm done, the length of the active cube list is my answer

    data = input_data.split('\n')
    
    def gen_neighbors(x,y,z):
        x_list = [x-1, x, x+1]
        y_list = [y-1, y, y+1]
        z_list = [z-1, z, z+1]
        output = []
        for h in x_list:
            for j in y_list:
                for k in z_list:
                    output.append((h,j,k))
        output.remove((x,y,z))
        return output
    def check_active_neightbors(x,y,z):
        neighbors = gen_neighbors(x,y,z)
        active_count = 0
        for neighbor in neighbors:
            if neighbor in active_cubes:
                #print(f'Cube ({x}, {y}, {z}) is bordered by ({neighbor[0], neighbor[1], neighbor[2]})')
                active_count = active_count + 1
        return active_count
    
    
    active_cubes = []
    
    for indeY,row in enumerate(data):
        for indeX, val in enumerate(list(row)):
            if val == '#':
                active_cubes.append((indeX, indeY, 0))
    
    round_count = 6
    for i in range(round_count):
        potential_cubes = list(active_cubes)
        for cube in active_cubes:
            potential_cubes = potential_cubes + gen_neighbors(cube[0], cube[1], cube[2])
        potential_cubes = list(set(potential_cubes))
        
        new_active_cubes = []
        for cube in potential_cubes:
            active_neighbors = check_active_neightbors(cube[0], cube[1], cube[2])
            if cube in active_cubes:
                if active_neighbors == 2 or active_neighbors == 3:
                    new_active_cubes.append(cube)
            else:
                if active_neighbors == 3:
                    new_active_cubes.append(cube)
        active_cubes = new_active_cubes
    print(len(active_cubes))
    
    Day 17 Part B – Python

    This is almost identical, except I added a 4th dimension 'w' per the instructions. And I added a nicer output at the end so that I could see the progress of the calculations. I was getting a bit nervous sitting ther waiting. I wasn't sure if it was going to take minutes or days.

    data = input_data.split('\n')
    
    def gen_neighbors(x,y,z,w):
        x_list = [x-1, x, x+1]
        y_list = [y-1, y, y+1]
        z_list = [z-1, z, z+1]
        w_list = [w-1, w, w+1]
        output = []
        for h in x_list:
            for j in y_list:
                for k in z_list:
                    for l in w_list:
                        output.append((h,j,k,l))
        output.remove((x,y,z,w))
        return output
    def check_active_neightbors(x,y,z,w):
        neighbors = gen_neighbors(x,y,z,w)
        active_count = 0
        for neighbor in neighbors:
            if neighbor in active_cubes:
                #print(f'Cube ({x}, {y}, {z}) is bordered by ({neighbor[0], neighbor[1], neighbor[2]})')
                active_count = active_count + 1
        return active_count
    
    
    active_cubes = []
    
    for indeY,row in enumerate(data):
        for indeX, val in enumerate(list(row)):
            if val == '#':
                active_cubes.append((indeX, indeY, 0, 0))
    
    round_count = 6
    for i in range(round_count):
        potential_cubes = list(active_cubes)
        for cube in active_cubes:
            potential_cubes = potential_cubes + gen_neighbors(cube[0], cube[1], cube[2], cube[3])
        potential_cubes = list(set(potential_cubes))
        
        new_active_cubes = []
        for cube in potential_cubes:
            active_neighbors = check_active_neightbors(cube[0], cube[1], cube[2], cube[3])
            if cube in active_cubes:
                if active_neighbors == 2 or active_neighbors == 3:
                    new_active_cubes.append(cube)
            else:
                if active_neighbors == 3:
                    new_active_cubes.append(cube)
        active_cubes = new_active_cubes
        print(f'Generation {i} Complete')
        print(f'{len(active_cubes)} cubes are currently active')
    
    Tips and Commentary
    • This has some similarities to Day 11. You might be able to use some of what you learned there. The rules for this are simpler, but the grid itself is much more complex

    • Part of the difficulty of this one is that you don't know how big your grid is going to be. Either you need to make it big enough to account for the entirety of the simulation's 6-step lifespan, or you need to come up with a way to resize it. Or you need to make it so you don't have a real grid at all. But the method you choose may dramatically affect your performance, which is especially important in part B

    • I was anxious about how long part B was taking, so I went ahead and gave myself periodic status updates. That helped me know my performance was at least good enough to wait for without burning my code to the ground in pursuit of better optimization. I'm afraid of how long gen 7 would take though.

    • This is based off of Conway's Game of Life which is a fairly cellular automata. Basically it simulates how various patterns propagate. People do really neat stuff with it

    • The example initial state is known as a 'glider' and it's a basic pattern that can be generated in Conway's Game of Life. When using the default rules of the game in a 2D space, that pattern will repeat forever, slowly moving forward as it does. It's also used sometimes as an unofficial symbol for hackers.

    4 votes
  11. Comment on Day 16: Ticket Translation in ~comp.advent_of_code

    PapaNachos
    Link
    That was fun! Unfortunately, I didn't get a chance to get to it until the morning after. Day 16 Part A – Python For this one I just do some regex to build a list of all possible valid numbers as I...

    That was fun! Unfortunately, I didn't get a chance to get to it until the morning after.

    Day 16 Part A – Python

    For this one I just do some regex to build a list of all possible valid numbers as I read in the rules. Then I compare each ticket against the full list of all valid numbers, keeping track of the error scanning rate as it goes

    import re
    #data = test_data
    data = input_data
    fields, my_ticket, nearby_tickets = data.split('\n\n')
    
    valid_nums = []
    pattern = re.compile('(\d+)-(\d+) or (\d+)-(\d+)')
    for row in fields.split('\n'):
        matches = pattern.search(row)
        valid_nums = valid_nums + list(range(int(matches.group(1)),int(matches.group(2))+1))
        valid_nums = valid_nums + list(range(int(matches.group(3)),int(matches.group(4))+1))
    valid_nums = list(set(valid_nums))
    
    error_scanning_rate = 0
    for row in nearby_tickets.split('\n')[1:]:
        vals = map(int,row.split(','))
        for val in vals:
            if val in valid_nums:
                pass
            else:
                error_scanning_rate = error_scanning_rate + val
    print(error_scanning_rate)
    
    Day 16 Part B – Python

    Part B was a fair bit more complicated.

    First I adjusted my regex a bit to grab the field data, I probably should have done this in part A, but whatever.

    Then I assigned a big grid (dictionary full of lists) called 'unknown fields' to help me track what all the possibilities could be.

    Then I pass through all the data, eliminating any errors or impossibilities from my grid.

    Finally, I continue looping through my unknown data and checking particular field is down to one possibility. If it is, then I declare it known and remove it as a possibility from other areas. Which hopefully will reduce at least one other combination down to one. I keep doing that until I have everything locked in, at which point I calculate the answer.

    My removal and validation process are definitely not as clean and efficient as I would like them to be, but they work well enough for a data set of this size. I'm still somewhat ashamed of my loopy mess.

    import re
    #data = test_data
    data = input_data
    fields, my_ticket, nearby_tickets = data.split('\n\n')
    
    valid_nums = []
    pattern = re.compile('(.+): (\d+)-(\d+) or (\d+)-(\d+)')
    field_info = {}
    for index,row in enumerate(fields.split('\n')):
        matches = pattern.search(row)
        valid_vals = []
        valid_vals = valid_vals + list(range(int(matches.group(2)),int(matches.group(3))+1))
        valid_vals = valid_vals + list(range(int(matches.group(4)),int(matches.group(5))+1))
        field_info[matches.group(1)] = list(set(valid_vals))
        valid_nums = valid_nums + valid_vals
    valid_nums = list(set(valid_nums))
    
    valid_tickets = []
    my_ticket = my_ticket.split('\n')[1:]
    for row in my_ticket + nearby_tickets.split('\n')[1:]:
        vals = list(map(int,row.split(',')))
        valid = True
        for val in vals:
            if val in valid_nums:
                pass
            else:
                valid = False
        if valid:
            valid_tickets.append(vals)
    unknown_fields = {}
    known_fields = {}
    field_list = field_info.keys()
    for field in range(len(field_list)):
        unknown_fields[field] = list(field_list)
    
    for row in valid_tickets:
        for index, val in enumerate(row):
            for field in unknown_fields[index]:
                if val in field_info[field]:
                    pass
                else:
                    unknown_fields[index].remove(field)
    count = 0
    while len(unknown_fields) > 0 and count < 100:
        count = count + 1
        for field in unknown_fields.keys():
            if len(unknown_fields[field]) == 1:
                known_fields[field] = unknown_fields[field][0]
        for field in known_fields.values():
            for field_list in unknown_fields.values():
                if field in field_list:
                    field_list.remove(field)
        for key in known_fields.keys():
            if key in unknown_fields.keys():
                unknown_fields.pop(key)
    
    answer = 1
    my_ticket = list(map(int,my_ticket[0].split(',')))
    for key in known_fields.keys():
        if 'departure' in known_fields[key]:
            answer = answer * my_ticket[key]
            
    print(answer)
    
    Tips and Commentary
    • Don't forget that the ranges are inclusive. You might make an off-by-1 error if you're not careful

    • Parsing the data in the first place might be difficult, if you separate it by looking for \n\n that will help you identify the groups of data so that you can deal with the different sections

    • My test data was too small of a group to fully validate my algorithm. It might be necessary to move to the full data set earlier than you normally would so that you can have more entries to work with

    • Depending on what your real data looks like, you'll likely need to use the process of elimination. Once you know that a field must be a certain thing, then you know that other fields AREN'T that thing.

    4 votes
  12. Comment on Day 15: Rambunctious Recitation in ~comp.advent_of_code

    PapaNachos
    (edited )
    Link
    I got stuck in debugging hell for a bit, but eventually got it to work. My solution to part B is almost identical to part A, the only parameter I changed is how far it goes. Tonight's eurobeat...

    I got stuck in debugging hell for a bit, but eventually got it to work. My solution to part B is almost identical to part A, the only parameter I changed is how far it goes.

    Tonight's eurobeat selection. It helped me distract myself from the pain of dropping a weight on my foot earlier.

    Day 15 Part A – Python

    First think I do is preallocate an area to work with. Then I can load the numbers that I already know of in there. I also create a memory dictionary which will be used to track when any given number was last spoken.

    The first few passes deal with numbers already loaded in, so they're not a big deal. I just pass over them. But still dump them into memory and keep track of the 'spoken'

    For all new numbers I first check if the 'last spoken' number is in my memory. If not I know it's new, so we get a '0'. Otherwise I do the subtraction as described. Then just update my history, memory, and last spoken.

    #data = test_data.split(',')
    data = input_data.split(',')
    
    distance = 2020
    memory = {}
    history = [None] * distance
    
    last_spoken = None
    for index, num in enumerate(data):
        history[index] = int(num)
    
    for index in range(distance):
        
        num = history[index]
        first_time = True
        if last_spoken in memory.keys():
            first_time = False
        if num is not None:
            new_val = num
        else:
            if not first_time:
                new_val = index - memory[last_spoken]
            else:
                new_val = 0
        history[index] = new_val
        memory[last_spoken] = index
        last_spoken = new_val
    print(history[-1])
    
    Day 15 Part B – Python

    This is the exact same as part A. The only thing I changed is the value loaded into the 'distance' variable. It takes slightly longer (maybe 30s?), but it's still fine. I suspect if I had designed it differently, it would take much longer.

    #data = test_data.split(',')
    data = input_data.split(',')
    
    distance = 30000000
    memory = {}
    history = [None] * distance
    
    last_spoken = None
    for index, num in enumerate(data):
        history[index] = int(num)
    
    for index in range(distance):
        
        num = history[index]
        first_time = True
        if last_spoken in memory.keys():
            first_time = False
        if num is not None:
            new_val = num
        else:
            if not first_time:
                new_val = index - memory[last_spoken]
            else:
                new_val = 0
        history[index] = new_val
        memory[last_spoken] = index
        last_spoken = new_val
    print(history[-1])
    
    Tips and Commentary
    • If you're planning to store the whole history, I highly recommend pre-allocating it since you know exactly how many numbers you're going to care about. If you keep resizing your storage area (for example, through python list's append method) you're going to waste a lot of time unnecessarily. I don't think you actually have to store the history beyond the first few though.

    • You'll need a way to tell how recently a given number was spoken. You can search for it each time, or you can find a way to store that information so that it can be retrieved more quickly. Searching each time will get slower as your history gets bigger

    • Make sure you're careful at what point you make your checks and comparisons. It's very important, but the correct order might be non-obvious.

    4 votes
  13. Comment on What games have you been playing, and what's your opinion on them? in ~games

    PapaNachos
    Link
    Been playing a lot of Cyberpunk 2077. I'm having a lot of fun despite the drama surrounding it. Sure, it's a buggy mess and is in desperate need of a few good patches. But I was expecting that...

    Been playing a lot of Cyberpunk 2077. I'm having a lot of fun despite the drama surrounding it. Sure, it's a buggy mess and is in desperate need of a few good patches. But I was expecting that already because CDPR launches are always like that.

    That being said, I'm enjoying myself so far. I'm quite a ways in and thoroughly enjoying the story. Without spoilers, I've particularly enjoyed the interactions between V and Johnny Silverhand (Keanu Reeves). But other characters like Jackie and Judy are great as well.

    The bugginess only really bothers me when it occurs during otherwise serious scenes, were it's somewhat immersion breaking. Like if someone is supposed to be pointing a gun at you, but the gun is rotated 90 degrees and clipping through their hand. Kinda kills the tension.

    6 votes
  14. Comment on What were the best games you played this year? in ~games

    PapaNachos
    Link
    I mentioned this about a week ago, but Eclipse: Second Dawn for the Galaxy is fantastic. It's a big, complex board game with all sorts of interconnected shit to manage, but it's so elegantly...

    I mentioned this about a week ago, but Eclipse: Second Dawn for the Galaxy is fantastic.

    It's a big, complex board game with all sorts of interconnected shit to manage, but it's so elegantly designed that, at least to us, it didn't feel burdensome in it's complexity.

    But it's the sort of game that costs like $150+ and a single match will likely take 5 hours, or more with more people. But it is absolutely excellent if everyone at your table is into big, brain-burny games.

    This was the review that sold me

    3 votes
  15. Comment on Day 14: Docking Data in ~comp.advent_of_code

    PapaNachos
    (edited )
    Link
    That one was fun! It felt like a good mix of stuff. My solution used some string replacement, more than a little type conversion, a touch of regex, and even some recursion! Have some more eurobeat...

    That one was fun! It felt like a good mix of stuff. My solution used some string replacement, more than a little type conversion, a touch of regex, and even some recursion!

    Have some more eurobeat

    Day 14 Part A – Python

    For this first one I pre-allocated some memory and then turned my value into a list of characters so that I could play with the digits and override them as necessary. The mask just stores which bits get replaced in a key, value pair. Then when I was done with the processing, just turn into an int and dump it into memory

    I also googled a decimal to binary method, because I didn't feel like writing one myself.

    I also padded my value all the way up to 36 bits, so that I wouldn't to worry about indexing and what-not. It would always be 36 bits long. I had originally though about reversing the order of the list instead, but I though the zero-padding was a more elegant solution for the index problem. Less of a headache too.

    import re
    data = test_data.split('\n')
    #data = input_data.split('\n')
    current_mask = None
    
    pattern = re.compile('mem\[(\d+)\] = (\d+)')
    
    memory = [None] * 1000000
    mask_data = []
    
    def decimalToBinary(n):  
        return bin(n).replace("0b", "") 
    
    for row in data:
        if 'mask' in row:
            bitmask = row.split(' = ')[1]
            #bitmask = bitmask[::-1]
            mask_data = []
            for index,char in enumerate(bitmask):
                if char == '1' or char == '0':
                    mask_data.append([index,char])
                
                    #print(index, char)
            #print(mask_data)
        else:
            captured = pattern.search(row)
            address = int(captured.group(1))
            val = int(captured.group(2))
            val = str(decimalToBinary(val))
            val = list(val.zfill(36))
            #val = val[::-1]
            
            for mask_bit in mask_data:
                val[mask_bit[0]] = mask_bit[1]
            val = ''.join(val)
            memory[address] = int(val,2)
    running_sum = 0
    for val in memory:
        if val is not None:
            running_sum = running_sum + val
            
    print(running_sum)
    
    Day 14 Part B – Python

    This one was a bit more complicated. My type conversion worked well enough, but I had to do it on the address, instead of the value. And I copied over the entire mask, instead of just the 0's and 1's. Also I switched memory over to a dict, because 2^36 is a LOT of addresses to try to hold in my RAM.

    I decided to go with a recursive solution to split my addresses. It would walk through a given address. If it ever ran into an 'X' it would make 2 copies of the address, one with a 1 and the other with a 0. And it would call itself on both of them. The tricky part was making sure that I had my data storage correct when passing between levels of the recursion. Which is to say, the raw address vs a list containing multiple addresses

    I realized later that another method that could work would be to generate a list of all X -> 0/1 combinations when you process the mask. Then apply that over each address the same way you did in part 1. It's probably slightly more efficient too.

    import re
    #data = test_data_2.split('\n')
    data = input_data.split('\n')
    current_mask = None
    
    pattern = re.compile('mem\[(\d+)\] = (\d+)')
    
    memory = {}
    mask_data = []
    
    def decimalToBinary(n):  
        return bin(n).replace("0b", "") 
    def address_split(address):
        #print(f'Attempting to split: {"".join(address)}')
        addresses_to_return = []
        if 'X' not in address:
            #print(f'Splitting Not Necessary')
            return [address]
        for index,char in enumerate(address):
            if char == 'X':
                high = list(address)
                high[index] = '1'
                low = list(address)
                low[index] = '0'
                #print(f'Split into: {"".join(high)}')
                #print(f'Split into: {"".join(low)}')
                
                addresses_to_return = addresses_to_return + address_split(high) + address_split(low)
                return addresses_to_return
                
    
    for row in data:
        if 'mask' in row:
            bitmask = row.split(' = ')[1]
            #bitmask = bitmask[::-1]
            mask_data = []
            for index,char in enumerate(bitmask):
                mask_data.append([index,char])
                    #print(index, char)
            #print(mask_data)
        else:
            captured = pattern.search(row)
            address = int(captured.group(1))
            val = int(captured.group(2))
            address = str(decimalToBinary(address))
            address = list(address.zfill(36))
            
            for mask_bit in mask_data:
                if mask_bit[1] == '1':
                    address[mask_bit[0]] = mask_bit[1]
                if mask_bit[1] == 'X':
                    address[mask_bit[0]] = 'X'
            addresses = address_split(address)
            for address in addresses:
                memory[int(''.join(address))] = val
    running_sum = 0
    for val in memory.values():
        if val is not None:
            running_sum = running_sum + val
            
    print(running_sum)
    
    Tips and Commentary
    • I've mentioned regex a few times now, but I don't think I ever explained what it is. Regex is shorthand for 'regular expressions'. They're a way of searching for specific patterns within text. So for example, if you needed to find something formatted like a street address, you could use a regular expression to find that. They tend to look like a bunch of arcane symbols until you learn to read them, but they can be extremely useful. I highly recommend https://regexone.com/ for learning how to actually use regex. And https://regexr.com/ is great for testing them out. I probably should have mentioned that like a week ago

    • When working with the mask, it's going to be important to know what index you're working with. Especially since some of your numbers will be shorter than the relevant digits in your mask. There are a number of ways to deal with this such as converting to binary and then left-padding it.

    • I ended up using a lot of type conversion for this one. Some of the different component problems were much easier in different forms, so I swapped between them as was appropriate for the given task

    • You're going to need to generate a bunch of addresses. There are a few ways to do it, for example, I used recursion. But interestingly, a given mask should always generate the same number of addresses and configuration of bits that need to be overwritten.

    • The memory addresses get much bigger in Part B. Pre-allocating a fixed area works fine for part A, but don't try it for part B unless you're running doing AoC on a supercomputer.

    3 votes
  16. Comment on Cyberpunk 2077: What do you think? in ~games

    PapaNachos
    (edited )
    Link Parent
    I've had a similar experience. I'm still running a 980 and it's fine. I haven't looked into complaints about what it looks like on consoles, but I have a lot of trouble believing the people that...

    I've had a similar experience. I'm still running a 980 and it's fine. I haven't looked into complaints about what it looks like on consoles, but I have a lot of trouble believing the people that say it's unplayable on PC without top of the line hardware. Just turn your fucking settings down.

    I went into it fully expecting it to be a buggy mess on release, because cdpr games are always a buggy mess on the release. But they always go back and iron out the wrinkles, unlike many other studios.

    I agree with all of your complaints, especially the crime system given it's a game about being criminals.

    And I would also like to add that driving feels like someone coated your wheels in butter. It's fine if you want to take every turn drifting at high speed, but if you want to take a regular turn at low speed, it's a mess

    4 votes
  17. Comment on Day 13: Shuttle Search in ~comp.advent_of_code

    PapaNachos
    (edited )
    Link
    I'm just going to post part A for now, I'm still wrapping my head around the math for part B. This might take a bit. Edit: After ramming my head against it for a while, I finally figured it out. I...

    I'm just going to post part A for now, I'm still wrapping my head around the math for part B. This might take a bit.

    Edit: After ramming my head against it for a while, I finally figured it out. I ALMOST had to turn off the eurobeat to help myself concentrate, but thankfully that wasn't necessary.

    Day 13 Part A – Python

    This one was pretty easy for me. I just just had each bus loop until I could see the first value AFTER the earliest arrival time, then kept the 'best' answer from there.

    #data = input_data.split('\n')
    data = test_data.split('\n')
    
    earliest_departure = int(data[0])
    
    busses_raw = data[1].split(',')
    busses = data[1].split(',')
    
    while 'x' in busses:
        busses.remove('x')
        
    busses = list(map(int, busses))
    
    best_bus = None
    best_bus_time = None
    for bus in busses:
        time = 0
        while time < earliest_departure:
            time = time + bus
        if not best_bus_time or time < best_bus_time:
            best_bus = bus
            best_bus_time = time
        
    print(earliest_departure)
    print(busses)
    
    print(best_bus)
    print(best_bus_time)
    print(best_bus_time - earliest_departure)
    
    Day 13 Part B – Python

    Figuring out how to do it in the first place was relatively simple. Figuring out how to get the computation done before the end of the month was trickier. I spend a while chasing a fancy mathematical solution, and that was definitely helpful, but it would have taken several hours to understand and implement. If I'm being honest, it's still a bit hard to understand why exactly it works, but here goes:

    When I have bus A and B, and they have no offset, I KNOW that they sync up every A * B steps. If they have an offset, then that offset will be exactly the same every A * B steps.

    I start with a group of synced busses. It starts empty (or with a ghost bus with an interval of 1).
    For each bus, I move the synced bus group until the new bus is at the appropriate offset.
    When I find that offset, I add that bus to the synced bus group, and increase the 'speed' of all the synced busses by that busses speed.
    So after bus 1 it synced busses just has bus A in it
    After bus 2 it is moving at speed A * B
    After bus 3 it's moving at speed A * B * C
    Etc...

    Once all the busses are synced, I have the answer

    data = input_data.split('\n')
    #data = test_data_2.split('\n')
    
    earliest_departure = int(data[0])
    
    busses_raw = data[1].split(',')
    busses = data[1].split(',')
    busses_indicies = []
    
    while 'x' in busses:
        busses.remove('x')
    
    for bus in busses:
        busses_indicies.append(busses_raw.index(bus))
        
    busses = list(map(int, busses))
    
    synced_busses = 1
    time = 0
    for bus, index in zip(busses, busses_indicies):
        print(f'Syncing with {bus}, {index}')
        synced = False
        while not synced:
            time = time + synced_busses
            if (time + index) % bus == 0:
                synced = True
                print(f'Successfully synced at {time}')
                synced_busses = synced_busses * bus
        
    
    Tips and Commentary
    • This one might be really difficult, depending on your approach

    • For the love of god DO NOT BRUTE FORCE part B. My answer was on the order of 700 trillion

    • There are fancy fancy mathy ways to solve it. I couldn't wrap my head around the equations after midnight though, but reading up on the concepts did help me eventually develop my solution

    • If you don't go for a purely math solution, figuring out how to speed up your search is extremely important.

    • Try breaking the problem up into smaller, more manageable portions. For example, can you solve the problem for just 2 busses at a time? And can you think of a way to treat 2 busses as 1 bus?

    • All my busses were prime numbers. I have reason to believe that everyone else's will be as well. Knowing they were primes was sort of helpful, but looking at the prime factorization was mostly a dead end for me.

    5 votes
  18. Comment on Day 10: Adapter Array in ~comp.advent_of_code

    PapaNachos
    Link Parent
    That's both terrifying and impressive. I'm curious to know how long it will take

    That's both terrifying and impressive. I'm curious to know how long it will take

    3 votes
  19. Comment on Day 12: Rain Risk in ~comp.advent_of_code

    PapaNachos
    Link
    Got distracted by Cyberpunk again. Oops. This way pretty fun though. I got to reuse some of the techniques I used on earlier days And once again EUROBEAT MAKES YOU GOOD AT CODE Day 12 Part A –...

    Got distracted by Cyberpunk again. Oops. This way pretty fun though. I got to reuse some of the techniques I used on earlier days

    And once again EUROBEAT MAKES YOU GOOD AT CODE

    Day 12 Part A – Python

    For this one I handled direction by making an array that I would push and pop from to rotate. Sort of like the buffer on day 9. Then, if I ran into a forward command, I would just use the value at
    the front of the buffer.

    import re
    
    #directions = test_data.split('\n')
    directions = input_data.split('\n')
    
    facing = ['E','S','W','N']
    x_loc = 0
    y_loc = 0
    
    pattern = re.compile('([\w])(\d+)')
    
    for row in directions:
        match = pattern.search(row)
        letter = match[1]
        number = int(match[2])
        if letter == 'F':
            letter = facing[0]
        if letter == 'N':
            y_loc = y_loc + number
        elif letter == 'S':
            y_loc = y_loc - number
        elif letter == 'E':
            x_loc = x_loc + number
        elif letter == 'W':
            x_loc = x_loc - number
        elif letter == 'R':
            while number > 0:
                number = number - 90
                facing.append(facing.pop(0))
        elif letter == 'L':
            while number > 0:
                number = number - 90
                facing.insert(0,facing.pop(-1))
    print(x_loc, y_loc)
    
    Day 12 Part B – Python

    This wasn't too bad either. I just had to make some minor modifications to track the waypoint separate from the ship. The trickiest part was just figuring out how to rotate the waypoint, but I wrote down the coordinates and was able to figure out how to move in each direction.

    import re
    
    #directions = test_data.split('\n')
    directions = input_data.split('\n')
    
    
    x_loc = 0
    y_loc = 0
    
    waypoint_x = 10
    waypoint_y = 1
    
    pattern = re.compile('([\w])(\d+)')
    
    for row in directions:
        match = pattern.search(row)
        letter = match[1]
        number = int(match[2])
        if letter == 'F':
            x_loc = x_loc + (waypoint_x * number)
            y_loc = y_loc + (waypoint_y * number)
        if letter == 'N':
            waypoint_y = waypoint_y + number
        elif letter == 'S':
            waypoint_y = waypoint_y - number
        elif letter == 'E':
            waypoint_x = waypoint_x + number
        elif letter == 'W':
            waypoint_x = waypoint_x - number
        elif letter == 'R':
            while number > 0:
                number = number - 90
                temp = waypoint_x
                waypoint_x = waypoint_y
                waypoint_y = temp * -1
        elif letter == 'L':
            while number > 0:
                number = number - 90
                temp = waypoint_y
                waypoint_y = waypoint_x
                waypoint_x = temp * -1
    print(x_loc, y_loc)
    
    Tips and Commentary
    • I assumed 90 degree turns and that worked, it might help to simplify it. It also helps if you make it so that you can handle multiple magnitudes of turn in one smooth motion

    • Grabbing a pen and paper (or an excel spreadsheet) and drawing out how turning changes things may help. Especially for part B

    • You don't actually need a whole-ass grid, you can just track the relative positions and facing

    4 votes
  20. Comment on Day 11: Seating System in ~comp.advent_of_code

    PapaNachos
    (edited )
    Link
    This one ended up being really annoying. Mostly because I missed one critical part of the rules in part B, so I spend like an hour tracking down a bug that wasn't there. FML. Oh well, more time...

    This one ended up being really annoying. Mostly because I missed one critical part of the rules in part B, so I spend like an hour tracking down a bug that wasn't there. FML.

    Oh well, more time spent writing code means more time listening to eurobeat.

    And it was an interesting problem, I just wish I had read the instructions more carefully.

    Day 11 Part A – Python

    For this one I made a list that stored the vector information for one step in each direction. I just stepped through it to find if the seat in each direction was occupied or empty (or out of bounds. It's also important to change the entire grid at the same time. If you overwrite it while you're still working on it, you'll fuck everything up. Rather than checking if my seat arrangement was the same, I just made sure I was using a hashable type and stored a list of hashes which I could compare against

    #seats = tuple(test_input.split('\n'))
    seats = tuple(data_input.split('\n'))
    
    width = len(seats)
    height = len(seats[0])
    
    print(width)
    print(height)
    
    one_step_from_seatden = [(0,1),(1,0),(1,1),(0,-1),(-1,0),(-1,-1),(-1,1),(1,-1)]
    
    def check_surrounding(x,y,seats):
        count = 0
        for step in one_step_from_seatden:
            x_step = x + step[0]
            y_step = y + step[1]
            if x_step < width and y_step < height and x_step >= 0 and y_step >= 0:
                if seats[x_step][y_step] == '#':
                    count = count + 1
        return count
    
    def iterate_seats(seats):
        new_seats = []
        for x in range(width):
            new_row = []
            for y in range(height):
                seat_count = check_surrounding(x,y,seats)
                if seats[x][y] == 'L' and seat_count == 0:
                    new_row.append('#')
                elif seats[x][y] == '#' and seat_count >= 4:
                    new_row.append('L')
                else:
                    new_row.append(seats[x][y])
            new_seats.append(tuple(new_row))
        return new_seats
    
    hash_list = []
    while True:
        seat_hash = hash(seats)
        if seat_hash in hash_list:
            break
        else:
            seats = tuple(iterate_seats(seats))
            hash_list.append(seat_hash)
    print(len(hash_list))
    
    seat_count = 0
    for row in seats:
        seat_count = seat_count + row.count('#')
    print(seat_count)
    
    Day 11 Part B – Python For this one I converted my check surroundings method to keep stepping in a direction until it found a seat or exited a grid.... then after an hour of searching I added a check to make it stop if found an empty seat. I missed that portion of the rules. FFFFFUUUUCCCCCKKKK.

    I also shoved a couple of debugging tools in there that I made while searching for why my shit wasn't working.

    #seats = tuple(test_input.split('\n'))
    seats = tuple(data_input.split('\n'))
    
    width = len(seats)
    height = len(seats[0])
    
    print(width)
    print(height)
    
    one_step_from_seatden = [(0,1),(1,0),(1,1),(0,-1),(-1,0),(-1,-1),(-1,1),(1,-1)]
    
    def check_surrounding(x,y,seats):
        count = 0
        for step in one_step_from_seatden:
            seat_found = False
            x_step = x + step[0]
            y_step = y + step[1]
            while x_step < width and y_step < height and x_step >= 0 and y_step >= 0 and seat_found == False:
                if seats[x_step][y_step] == '#':
                    seat_found = True
                    count = count + 1
                elif seats[x_step][y_step] == 'L':
                    seat_found = True
                else:
                    x_step = x_step + step[0]
                    y_step = y_step + step[1]
        return count
    def print_seats(seats):
        for row in seats:
            print(''.join(row))
    
    def iterate_seats(seats):
        new_seats = []
        #count_grid = []
        for x in range(width):
            new_row = []
            count_row = []
            for y in range(height):
                seat_count = check_surrounding(x,y,seats)
                #count_row.append(str(seat_count))
                if seats[x][y] == 'L' and seat_count == 0:
                    new_row.append('#')
                elif seats[x][y] == '#' and seat_count >= 5:
                    new_row.append('L')
                else:
                    new_row.append(seats[x][y])
            new_seats.append(tuple(new_row))
            #count_grid.append(count_row)
        #print_seats(count_grid)
        return new_seats
    
    hash_list = []
    while True:
        #print_seats(seats)
        #print(check_surrounding(0,0,seats))
        seat_hash = hash(seats)
        #print(seat_hash)
        if seat_hash in hash_list:
            break
        else:
            seats = tuple(iterate_seats(seats))
            hash_list.append(seat_hash)
    print(len(hash_list))
    
    seat_count = 0
    for row in seats:
        seat_count = seat_count + row.count('#')
    print(seat_count)
    
    Tips and Commentary
    • Read the instructions carefully. Read them again. One more time for good measure. If you get stuck, go read the instructions again. Seriously, I wasted like an hour because I misunderstood one of the rules

    • You can deal with this grid in the same way you dealt with your grid on day 3, reuse your own code shamelessly

    • Hashing is a technique of converting anything into a unique* number called a hash. If you convert something into a hash and store the hash, you can then compare other objects to that hash. If the hashes match you have the same thing
      *Not technically unique, but a good hash function will rarely have duplicates when it shouldn't. These false duplicates are called collisions

    • Make sure not to update your seat arrangement until you're finished doing all your calculations. If you make changes mid-operation, the incoming or outgoing people will mess up your results

    • Make sure you don't have your x and y coordinates swapped

    4 votes