Gyrfalcon's recent activity

  1. Comment on Mount & Blade II: Bannerlord will be released in Early Access on March 31, 2020 in ~games

    Gyrfalcon
    Link Parent
    Same here! I've recently gone back to Warband to try and round out my achievements. My only concern is the devs said there may not be Linux support right off the bat, hopefully it works with Steam...

    Same here! I've recently gone back to Warband to try and round out my achievements. My only concern is the devs said there may not be Linux support right off the bat, hopefully it works with Steam Play at least.

    1 vote
  2. Comment on Rethinking Space Heating in ~enviro

    Gyrfalcon
    Link Parent
    In my home, we don't let ours vary quiet so much, but we do 66 F (~19 C) in the winter and 78 F (~26 C) in the summer. Anything in between we turn the climate control off until we feel...

    In my home, we don't let ours vary quiet so much, but we do 66 F (~19 C) in the winter and 78 F (~26 C) in the summer. Anything in between we turn the climate control off until we feel uncomfortable.

    One tip to consider for making a wider range of temperatures comfortable is to use ceiling fans, and just fans in general. During winter, we have the ceiling fans running to blow air upward. That keeps warm air from rising and getting stuck up near the ceiling, where there's no people. In the summer, we flip the switch on the fans to blow down, right on us to cool us down. We also have a box fan that we use to pull in cool air in the evenings and mornings before we try to seal out the heat of the day.

    3 votes
  3. Comment on Rethinking Space Heating in ~enviro

    Gyrfalcon
    Link Parent
    Oh I'm very familiar with that phenomenon, though for me it's usually my mouse hand at my desk. We don't have a nice thermostat in our apartment, so we just keep it at 66 F all the time. I'd...

    Oh I'm very familiar with that phenomenon, though for me it's usually my mouse hand at my desk. We don't have a nice thermostat in our apartment, so we just keep it at 66 F all the time. I'd probably be willing to let it go even lower at night, but I think my SO would freeze!

    3 votes
  4. Comment on Rethinking Space Heating in ~enviro

    Gyrfalcon
    Link
    I have been working on this article for my personal website and wanted to share! I have been looking at lower tech ways to reduce one's carbon footprint and heating was interesting to me right now...

    I have been working on this article for my personal website and wanted to share! I have been looking at lower tech ways to reduce one's carbon footprint and heating was interesting to me right now since it's cold where I live.

    8 votes
  5. Comment on Linux 5.6 is the most exciting kernel in years in ~comp

    Gyrfalcon
    Link Parent
    I think they are talking about this, which seems like it is drivers to allow code that needs to execute in the trusted areas of the processor to do so.

    I think they are talking about this, which seems like it is drivers to allow code that needs to execute in the trusted areas of the processor to do so.

    3 votes
  6. Comment on 1917 (2019) — Spoiler-free discussion thread in ~movies

    Gyrfalcon
    Link
    Okay so I really liked this movie and was almost in tears leaving the theatre. That said, I have been on a WWI kick for the last year and a half, watching The Great War on youtube, reading books...

    Okay so I really liked this movie and was almost in tears leaving the theatre. That said, I have been on a WWI kick for the last year and a half, watching The Great War on youtube, reading books about WWI (currently a biography of John Monash, commander of Australian forces in WWI at Gallipoli and on the Western Front), and listening to the relevant Sabaton album.

    If you enjoyed this film, I would highly recommend watching They Shall Not Grow Old. It's a film, directed by Peter Jackson, composed primarily of old WWI footage that was cleaned up, colorized, and with sound added. Most of the sound track is WWI stories as voice over relevant to the original footage being shown, though some is just the sound for the scene, where they went as far as hiring voice actors from the same region of the UK as the soldiers in the footage to get the accents right. Not as emotionally gripping as 1917, but certainly good and provides a broader picture of the war.

    I agree with some of the other comments that time moved weirdly, but that's just a requirement of fitting many hours of events into a 2 hour film, and because a lot of the stuff they left out being not that interesting.

    Time related spoilers

    The most jarring for me was when they were moving through the trenches actually. Those trench systems could be miles from front to back as the crow files, and further than that on foot by that late in the war. That they went from well behind the lines to the front line in 10 minutes or so in the British trenches, and then through the booby trapped dugouts and artillery areas in not much longer on the German side was a little surprising, but again I totally understand why they had to do it that way, since at the beginning they estimated it as an 8 or 9 hour mission without any delays.

    There were a couple of other things I was wondering if people could weigh in on, historical accuracy wise. These are a bit spoilery so I will hide them.

    History related spoilers

    When they are moving through the German trenches, they looked similar to video I have seen of modern soldiers or SWAT teams, rifle up and popping corners in a coordinated fashion. Does anyone know if this was an accurate style of moving through the trenches at the time? I certainly haven't seen anything about soldiers being trained to fight that way, since my understanding is that it became common after body armor became common, though I could be wrong. I suppose it's also possible that they learned to move that way as experienced trench combatants.

    Another question I had was on their volume of equipment. They had a few bags, but judging from the size and the way they moved I got the sense they were not terribly heavy. I know in early WWI, soldiers were given very heavy packs, sometimes in excess of 80 lbs, and to the point that soldiers who fell in the mud would drown. My guess is that either this changed later in the war, or that Blake and Schofield were equipped more lightly like trench raiders for their special mission. Either way, I would be interested in knowing more about the accuracy of their equipment.

    Music related spoiler

    I felt that the scene with the lone singing soldier was incredible, as a summary of the events until that point, and as a way of making peace with the death of Blake. Unfortunately, that version of the song, an old folk standard known as Wayfaring Stranger or Poor Wayfaring Stranger, is not available anywhere that I can find, so I have had to settle for adding a different but still good cover of it to my playlists.

    3 votes
  7. Comment on Ask Tildes: Design a spacecraft! You've been offered to submit a space exploration misson, with a cost cap of $1 billion. What is your proposal? in ~space

    Gyrfalcon
    Link
    Alright so I am hilariously late to this party BUT I am here and that is what counts. So with "only" a billion dollars, I think it's safe to rule out all but the most limited of manned missions,...

    Alright so I am hilariously late to this party BUT I am here and that is what counts.

    So with "only" a billion dollars, I think it's safe to rule out all but the most limited of manned missions, and definitely nothing that needs you to develop a new launch vehicle. However, I recently worked on a class project that I thought was interesting.

    The premise was to get a ground penetrating radar (GPR) instrument close enough to the Moon to get data on locations where GRAIL gravity data and topography patterns indicate that there might be lava tubes under the surface. The secondary goal is to characterize the dark rock of the Lunar mare in terms of surface coverage and overall volume. The lava tubes are especially interesting since they may provide spaces that could be good for future manned habitation of the Moon. Within the cover of a lava tube, radiation is significantly reduced, and the thermal extremes of the Lunar surface are mitigated. Lava tubes also exist on Mars (we think), and on the Earth (we know), though they are much smaller due to higher gravity and differing geology. The lava tubes on the Moon are thought to be on the scale of hundreds to thousands of meters in width, and kilometers in length, so there would be plenty of room inside if that turns out to be true.

    The project proposal we developed was aimed at NASA's SIMPLEx secondary mission program, with a cost cap of $55 million dollars (though we broke through that!). For this, I would want to do the mission for maximum science return, rather than on a budget.

    So this mission would still include an orbiter, but with a few changes. It would be in a low Lunar orbit similar to the LRO to get good data return from the GPR. Of course the location of the periapsis would be moved to be over the mare, maximizing the quality of the data in that region. The orbiter would be much larger than the ESPA class smallsat we used in class, and would include both a LIDAR instrument to get confirmation on the height of the surface and a camera to take high resolution photos of the surface. There would be additional communications hardware on the orbiter that can serve as a relay for surface missions, which will come in handy for the rover I have planned. The orbiter also serves as a command and service module for the rover during transit.

    Though the orbiter would be scaled up in this scenario, the rover and its new technology is where the bulk of the money would be spent. There are several locations on the Lunar surface where there are "skylights," or places where we think lava tubes have collapsed a little bit. One of these skylights would be the target for the rover. Using a skycrane style landing mechanism, a two part rover would land in the skylight.

    Part one would be the base station, and may not necessarily be mobile. This part would contain power generation, likely solar panels, as well as communications to relay back to orbit.

    Part two contains the science instruments and is definitely mobile. Its job is to take rock samples and photos of the lava tubes to gain more information about them. It has an umbilical back to the base station for power and communication, since if it goes into the lava tube there will be no sunlight, and carrying a RTG on a mission like this seems unlikely. Depending on final cost, this may get descoped to a solar powered lander and a rover which is powered by primary batteries, but I think with a billion dollars it should be possible to get an umbilical developed.

    In terms of propulsion and mission plan, things are pretty standard here. A direct, Apollo style transfer is in order, since we don't have any special mission requirements like GRAIL did to need one of those fancy ballistic lunar transfers. Hopefully we can get a Falcon Heavy type launch vehicle to toss us on a Lunar intercept trajectory, from there capture as well as station keeping for the orbiter and landing for the rover should be something that a conventional hypergolic propellant like hydrazine can handle. Might need to be a bipropellant depending on mass and cost constraints, but of course I'd have to do the engineering to know.

    And most importantly, every mission needs a name! I think Hestia would be a good name for the overall mission, since that is the Greek goddess of the home and hearth, appropriate for a mission that could be finding a Lunar home for humanity. Naming the individual spacecraft would probably use variations on hearth and fire, since those are Hestia's symbols.

    I think the precision landing and use of the umbilical are definitely the biggest technical and mission risks. However, because of the environment, I don't really think there is a cheaper or safer way to get inside the lava tubes and collect data. Whether or not doing that is strictly necessary is another question, but I think it is cool at the very least :)

    11 votes
  8. Comment on What are you using these days as an alternative to YouTube? in ~tech

    Gyrfalcon
    Link Parent
    I frequently watch LTT and I think I watched that video, but I have a question for you as a Floatplane user. Is it like Patreon, in that you get access only to the content of the creators you...

    I frequently watch LTT and I think I watched that video, but I have a question for you as a Floatplane user. Is it like Patreon, in that you get access only to the content of the creators you support directly, or can you also access the content of others on the platform?

    I ask because I watch a lot of YouTube (with an adblocker mostly) from many different channels. For my favorite channels I either buy for myself or ask for some merchandise as gifts. But if I had to give a dollar a month for each of those channels I'd be looking at $20+ monthly, which is much more than I think I could reasonably spend. I suppose that just means the trade to Google is worth it for me right now.

    3 votes
  9. Comment on Too Much Combustion, Too Little Fire in ~enviro

    Gyrfalcon
    Link Parent
    Yeah, I like the little thing on the side but the color is a bit distracting. Not sure what they could do to make it obvious but not hit you over the head with it, though.

    Yeah, I like the little thing on the side but the color is a bit distracting. Not sure what they could do to make it obvious but not hit you over the head with it, though.

    3 votes
  10. Comment on Too Much Combustion, Too Little Fire in ~enviro

    Gyrfalcon
    Link
    This is a neat article on comparing the energy usage of a modern household to that of a household that meets its energy needs using a fire. The link is to their solar website, so if it's powered...

    This is a neat article on comparing the energy usage of a modern household to that of a household that meets its energy needs using a fire. The link is to their solar website, so if it's powered down you can find the article here.

    1 vote
  11. Comment on Is anyone else here wondering when our overlord will open this place up a little? in ~tildes

    Gyrfalcon
    Link Parent
    If you don't mind, I might make a post in that thread now that I've seen it, if you are okay with me reviving it that is. It was posted when I was super busy so I missed it :(

    If you don't mind, I might make a post in that thread now that I've seen it, if you are okay with me reviving it that is. It was posted when I was super busy so I missed it :(

    2 votes
  12. Comment on Fortnightly Programming Q&A Thread in ~comp

    Gyrfalcon
    Link Parent
    Thanks for the advice! Advent of Code is definitely something I'm planning to keep working on. I was pleased with how my intcode stuff was turning out, though I only cleaned it up after I had to...

    Thanks for the advice! Advent of Code is definitely something I'm planning to keep working on. I was pleased with how my intcode stuff was turning out, though I only cleaned it up after I had to manage the amplifier challenge. Looking at issues completely slipped my mind when thinking about this, that's a great thing to consider too.

    1 vote
  13. Comment on Fortnightly Programming Q&A Thread in ~comp

    Gyrfalcon
    Link Parent
    Hmm probably my biggest non STEM interest is history, and I think I could make some of the more gamey ideas I've had fit in with that as teaching tools. I do think the advice to not major in CS is...

    Hmm probably my biggest non STEM interest is history, and I think I could make some of the more gamey ideas I've had fit in with that as teaching tools.

    I do think the advice to not major in CS is an interesting one. That said, my goal is to eventually apply CS to the technical field I have my degree in, so I'm not so sure the non-technical domain bit fits 100%, though I'm interested to try it out. Thanks for the perspective.

    3 votes
  14. Comment on Fortnightly Programming Q&A Thread in ~comp

    Gyrfalcon
    Link Parent
    I think I will probably start with that, since it's a project I've been meaning to do and have dabbled in a bit already. Thanks for the advice!

    I think I will probably start with that, since it's a project I've been meaning to do and have dabbled in a bit already. Thanks for the advice!

    1 vote
  15. Comment on Fortnightly Programming Q&A Thread in ~comp

    Gyrfalcon
    Link
    This isn't so much of a programming question, but a career question that involves programming. Things have been a bit slow on these threads so I'll put it here anyway. I recently graduated from...

    This isn't so much of a programming question, but a career question that involves programming. Things have been a bit slow on these threads so I'll put it here anyway.

    I recently graduated from college with a degree in an engineering field. Due to personal reasons, I'm in a bit of a job limbo, and will continue to be for a few months. I would like to use some of this time to build up a portfolio of work to try to convince people to hire me for a programming job in my field instead of someone with a CS or CompE degree. I'm not sure what to put in this kind of portfolio. I have written a good bit of code but most of it has been MATLAB scripts and the like for courses and I'm not really sure how impressive that is. I do have some ideas for what to put in a portfolio, such as:

    • I really enjoyed working on about half of this years Advent of Code before life caught up to me. I could finish that off and then go back and do all of the previous years as well. I like this because it gives me a steady framework, but I'm not sure if it would really impress anyone. I might do this for myself even if it's no good for a portfolio because I like puzzles :)

    • I have a bunch of random ideas for things I think would be cool:

      • A program that will solve Sudoku puzzles, and generate new puzzles and let you solve them
      • A little pomodoro timer applet, potentially combined with a time tracker and some kind of productivity gamification like Forest
      • Algorithmic trading on the foreign exchange markets. This is something I have already dabbled a little bit in. I think regulators don't like it so much if you make the code for it open source, so I'm not sure how easy it would be to show off.
      • A little simulation that shows the interplay between creating space for cars and walkability in an urban setting.
      • A model of a power grid with homes both consuming and producing power. Not really sure what to do with it once it has been modelled but it would be cool, I think.
      • A little WWI trench battle game, where you see how the technology of the time encouraged defensive warfare and led to the stagnation and entrenchment seen on the Western front.
      • An open source journaling application, because I saw a neat application and it was expensive and not for my platform.
      • Implementing more of my coursework in Kerbal Space Program. I have already pursued this a bit, sending a mission to the Mun and back autonomously. Next would be adding observation stations and determining spacecraft state from noisy measurements. It's really fun, but I don't think anyone really takes me seriously once I mention Kerbal, so ???
      • Something with peer to peer ebook lending, although copyright law makes this weird and maybe useless.
      • Some kind of distributed learning system using git, where the readme is the first lesson on how to get Python or something set up, and then the rest of the lessons teach you different things using the available source files.
    • I do have a website, but if has more of a sustainability focus. I might do some programming projects though, like taking worksheets from old solar power books and making them into command line or GUI programs.

    There are some ideas I have thought about but am kind of intimidated by right now:

    • Contributing to an existing open source project. Whenever I think about this I get pretty overwhelmed. If I have only ever gotten a few hundred lines to play nice together, how can I parse through thousands of lines to understand what's going on before even adding a feature of my own? I also find I have to comment my own code quite a bit more than others so I'm not sure if that's good or bad.
    • Convincing someone on a low price freelancing platform to hire me. This is good because I could make (a little) money, and someone else comes up with the ideas. I am concerned that if I dip my toe in too early and get bad reviews from early projects I may have a tough time getting more assignments.

    Anyway this has turned into a bit of a rant so if you happen to read some or all of it and/or provide any advice on the subject, thank you!

    6 votes
  16. Comment on Day 15: Oxygen System in ~comp

    Gyrfalcon
    Link
    I am catching up! If I can do two a day from now through Christmas I will only finish a day late heh. Anyway, I was lazy and did this in a very slow and clunky way, but it worked and I didn't have...

    I am catching up! If I can do two a day from now through Christmas I will only finish a day late heh. Anyway, I was lazy and did this in a very slow and clunky way, but it worked and I didn't have to think too hard about it.

    Intputer.py
    class Intputer(object):
    
        def __init__(self, instructions, pointer=0, relative_base=0, output_block=False):
    
            self.instructions = instructions
            self.pointer = pointer
            self.relative_base = relative_base
            self.curr_instruction = [int(num) for num in str(self.instructions[self.pointer])]
            self.output_block = output_block
            self.output_buffer = []
            self.input_buffer = []
            self.block = False
            self.isHalted = False
    
        def get_operands(self):
            for i in range(5 - len(self.curr_instruction)):
                self.curr_instruction.insert(0,0) # Pad to get the right number of 0s
    
            if self.curr_instruction[0] == 0:
                out_location = self.instructions[self.pointer + 3]
            elif self.curr_instruction[0] == 2:
                out_location = self.instructions[self.pointer + 3] + self.relative_base
            else:
                raise ValueError("Incorrect mode for output operand!")
    
            if self.curr_instruction[1] == 0:
                op_2_location = self.instructions[self.pointer+2]
            elif self.curr_instruction[1] == 1:
                op_2_location = self.pointer + 2
            elif self.curr_instruction[1] == 2:
                op_2_location = self.instructions[self.pointer+2] + self.relative_base
            else:
                raise ValueError("Incorrect mode for second operand!")
    
            if self.curr_instruction[2] == 0:
                op_1_location = self.instructions[self.pointer+1]
            elif self.curr_instruction[2] == 1:
                op_1_location = self.pointer + 1
            elif self.curr_instruction[2] == 2:
                op_1_location = self.instructions[self.pointer+1] + self.relative_base
            else:
                raise ValueError("Incorrect mode for first operand!")
    
            # We handle big memory on write, so this covers it on read
            try:
                op_1 = self.instructions[op_1_location]
            except IndexError:
                op_1 = 0
    
            try:
                op_2 = self.instructions[op_2_location]
            except IndexError:
                op_2 = 0
    
            return [op_1, op_2, out_location]
    
        def get_io_operand(self):
    
            for i in range(3 - len(self.curr_instruction)):
                self.curr_instruction.insert(0,0) # Pad to get the right number of 0s
    
            if self.curr_instruction[0] == 0:
                op = self.instructions[self.pointer + 1]
            elif self.curr_instruction[0] == 1:
                op = self.pointer + 1
            elif self.curr_instruction[0] == 2:
                op = self.instructions[self.pointer + 1] + self.relative_base
            else:
                raise ValueError("Incorrect mode for I/O operand!")
    
            return op
    
        def write_result(self, result, location):
    
            # Extend memory if needed
            try:
                self.instructions[location] = result
            except IndexError:
                self.instructions.extend([0] * ((location + 1) - len(self.instructions)))
                self.instructions[location] = result
    
        def add_instruction(self):
            operands = self.get_operands()
    
            result = operands[0] + operands[1]
            self.write_result(result, operands[2])
            self.pointer += 4
    
        def mult_instruction(self):
            operands = self.get_operands()
    
            result = operands[0] * operands[1]
            self.write_result(result, operands[2])
            self.pointer += 4
    
        def output_instruction(self):
    
            operand = self.get_io_operand()
    
            self.output_buffer.append(self.instructions[operand])
    
            if self.output_block:
                self.block = True
    
            self.pointer += 2
    
        def input_instruction(self):
    
            # Check for no input, and block if input is empty
            if len(self.input_buffer) < 1:
                self.block = True
                return
    
            operand = self.get_io_operand()
    
            self.write_result(self.input_buffer.pop(), operand)
            self.pointer += 2
    
        def jump_instruction(self, jump_type):
    
            operands = self.get_operands()
    
            if ((operands[0] and jump_type) or (not operands[0] and not jump_type)):
                self.pointer = operands[1]
            else:
                self.pointer += 3
    
        def less_than_instruction(self):
    
            operands = self.get_operands()
    
            if operands[0] < operands[1]:
                self.write_result(1, operands[2])
            else:
                self.write_result(0, operands[2])
    
            self.pointer += 4
    
        def equals_instruction(self):
    
            operands = self.get_operands()
    
            if operands[0] == operands[1]:
                self.write_result(1, operands[2])
            else:
                self.write_result(0, operands[2])
    
            self.pointer += 4
    
        def modify_base_instruction(self):
    
            operand = self.get_io_operand()
    
            self.relative_base += self.instructions[operand]
    
            self.pointer += 2
    
        def process_instructions(self, inputs=None):
    
            if inputs is not None:
                try:
                    for item in inputs:
                        self.input_buffer.insert(0, item)
                except TypeError: # Catch singular inputs here
                    self.input_buffer.insert(0,inputs)
            else:
                inputs = []
    
            while True:
    
                if self.pointer > (len(self.instructions) - 1):
                    break
                elif self.instructions[self.pointer] == 99:
                    self.isHalted = True
                    break
    
                self.curr_instruction = [int(num) for num in str(self.instructions[self.pointer])]
    
                if self.curr_instruction[-1] == 1:
                    self.add_instruction()
                elif self.curr_instruction[-1] == 2:
                    self.mult_instruction()
                elif self.curr_instruction[-1] == 3:
                    self.input_instruction()
                elif self.curr_instruction[-1] == 4:
                    self.output_instruction()
                elif self.curr_instruction[-1] == 5:
                    self.jump_instruction(True)
                elif self.curr_instruction[-1] == 6:
                    self.jump_instruction(False)
                elif self.curr_instruction[-1] == 7:
                    self.less_than_instruction()
                elif self.curr_instruction[-1] == 8:
                    self.equals_instruction()
                elif self.curr_instruction[-1] == 9:
                    self.modify_base_instruction()
    
                if self.block:
                    self.block = False
                    return
    
        def clear_output_buffer(self):
            self.output_buffer = []
    
    def load_instructions(input_file):
    
        with open(input_file, 'r') as fid:
            data = fid.readline()
    
        instructions = data.strip().split(',')
    
        for idx in range(len(instructions)):
            instructions[idx] = int(instructions[idx])
    
        return instructions
    
    RepairBot.py
    import random
    import copy
    import Intputer
    
    class RepairBot(object):
    
        def __init__(self, brain_instructions):
    
            self.brain = Intputer.Intputer(copy.copy(brain_instructions))
            self.initial_instructions = copy.copy(brain_instructions)
            self.location = [0, 0]
            self.map = {}
            self.map[tuple(self.location)] = 1
            self.move_history = []
    
        def reinitialize_brain(self):
    
            self.brain = Intputer.Intputer(copy.copy(self.initial_instructions))
    
        def move(self, direction):
    
            self.brain.process_instructions(direction)
            change_location = True
            if self.brain.output_buffer[-1] == 0:
                change_location = False
    
            new_location = copy.copy(self.location)
    
            if direction == 1:
                 new_location[0] += 1
            elif direction == 2:
                new_location[0] -= 1
            elif direction == 3:
                new_location[1] -= 1
            elif direction == 4:
                new_location[1] += 1
    
            self.map[tuple(new_location)] = self.brain.output_buffer[-1]
            self.brain.clear_output_buffer()
    
            if change_location:
                self.location = new_location
    
    
        def find_valid_moves(self):
            valids = []
            locations = [(self.location[0]+1, self.location[1]),
                         (self.location[0]-1, self.location[1]),
                         (self.location[0], self.location[1]-1),
                         (self.location[0], self.location[1]+1)]
    
            for idx in range(4):
    
                try:
                    if self.map[tuple(locations[idx])] == 1:
                        valids.append(idx + 1)
                except KeyError:
                    # We don't need to do things just if there isn't anything in
                    # there it's okay, but maybe we can move there?
                    valids.append(idx + 1)
    
            return valids
    
        # This is admittedly the stupid way but I guess I'm stupid today
        def find_ox_sys(self):
            preferred_direction = 1
    
            while True:
    
                print(len(self.map))
                if 2 in self.map.values() and len(self.map) > 1656:
                    return
    
                valid_moves = self.find_valid_moves()
    
                self.move(random.choice(valid_moves))
    
        def path_to_ox(self):
    
            wavefront_map = {}
            ox_location = list(self.map.keys())[list(self.map.values()).index(2)]
            wavefront_map[ox_location] = 0
    
            while (0,0) not in wavefront_map.keys():
    
                wavefront_keys = list(wavefront_map.keys())
    
                for curr_location in wavefront_keys:
                    possible_locations = [(curr_location[0]+1, curr_location[1]),
                                          (curr_location[0]-1, curr_location[1]),
                                          (curr_location[0], curr_location[1]-1),
                                          (curr_location[0], curr_location[1]+1)]
    
                    for possible_location in possible_locations:
                        if (possible_location in self.map.keys()
                            and self.map[possible_location] != 0
                            and possible_location not in wavefront_map.keys()):
                            wavefront_map[possible_location] = wavefront_map[curr_location] + 1
    
            return wavefront_map[(0,0)]
    
        def ox_time(self):
    
            wavefront_map = {}
            ox_location = list(self.map.keys())[list(self.map.values()).index(2)]
            wavefront_map[ox_location] = 0
    
            while True:
                old_len = len(wavefront_map)
    
                wavefront_keys = list(wavefront_map.keys())
    
                for curr_location in wavefront_keys:
                    possible_locations = [(curr_location[0]+1, curr_location[1]),
                                          (curr_location[0]-1, curr_location[1]),
                                          (curr_location[0], curr_location[1]-1),
                                          (curr_location[0], curr_location[1]+1)]
    
                    for possible_location in possible_locations:
                        if (possible_location in self.map.keys()
                            and self.map[possible_location] != 0
                            and possible_location not in wavefront_map.keys()):
                            wavefront_map[possible_location] = wavefront_map[curr_location] + 1
    
                new_len = len(wavefront_map)
    
                if new_len <= old_len:
                    break
    
            return max(wavefront_map.values())
    
    Oxygen.py
    import Intputer
    import RepairBot
    import copy
    
    input_file = "PuzzleInput.txt"
    instructions = Intputer.load_instructions(input_file)
    repair_bot = RepairBot.RepairBot(copy.copy(instructions))
    
    repair_bot.find_ox_sys()
    length = repair_bot.path_to_ox()
    print(length)
    time = repair_bot.ox_time()
    print(time)
    
    2 votes
  17. Comment on Day 14: Space Stoichiometry in ~comp

    Gyrfalcon
    Link
    Well I got distracted with the end of my semester and the start of the holiday season but I am trying to catch up if possible. Code below is for both parts in Python, split by file. Reaction.py...

    Well I got distracted with the end of my semester and the start of the holiday season but I am trying to catch up if possible. Code below is for both parts in Python, split by file.

    Reaction.py
    class Reaction(object):
    
        def __init__(self, output, inputs):
            self.output_id = output[1]
            self.output_num = int(output[0])
    
            self.input_ids = []
            self.input_nums = []
    
            for input in inputs:
                self.input_ids.append(input[1])
                self.input_nums.append(int(input[0]))
    
    Stoich.py
    import Reaction
    import math
    
    def parse_reactions(file_name):
    
        with open(file_name, 'r') as fid:
            data = fid.readlines()
    
        reactions = {}
    
        for line in data:
            inputs = line.split("=>")[0].strip()
            outputs = line.split("=>")[1].strip()
            inputs = inputs.split(",")
            inputs = [x.strip() for x in inputs]
            inputs = [x.split(" ") for x in inputs]
            outputs = outputs.split(" ")
            new_reaction = Reaction.Reaction(outputs, inputs)
            reactions[new_reaction.output_id] = new_reaction
    
        return reactions
    
    def produce_compound(reactions, output_id, num_needed):
        current_reaction = reactions[output_id]
        ore_total = 0
    
        try:
            current_extra = produce_compound.extra[current_reaction.output_id]
        except KeyError:
            produce_compound.extra[current_reaction.output_id] = 0
            current_extra = 0
    
        if num_needed <= current_extra:
            produce_compound.extra[current_reaction.output_id] -= num_needed
            return 0
        elif num_needed > current_extra:
            num_needed -= current_extra
            produce_compound.extra[current_reaction.output_id] = 0
    
        num_reactions = math.ceil(num_needed / current_reaction.output_num)
        produce_compound.extra[current_reaction.output_id] = num_reactions * current_reaction.output_num - num_needed
    
    
        for idx in range(len(current_reaction.input_ids)):
            if current_reaction.input_ids[idx] == 'ORE':
                ore_total += num_reactions*current_reaction.input_nums[idx]
            else:
                ore_total += produce_compound(reactions, current_reaction.input_ids[idx], num_reactions*current_reaction.input_nums[idx])
    
        return ore_total
    
    
    
    input_file = "PuzzleInput.txt"
    test_files = ["Test1_1.txt", "Test1_2.txt", "Test1_3.txt", "Test1_4.txt", "Test1_5.txt"]
    test_outputs = [31, 165, 13312, 180697, 2210736]
    
    for idx in range(len(test_files)):
    
        reactions = parse_reactions(test_files[idx])
        produce_compound.extra = {}
        ore = produce_compound(reactions, 'FUEL', 1)
        assert ore == test_outputs[idx]
    
    reactions = parse_reactions(input_file)
    produce_compound.extra = {}
    ore = produce_compound(reactions, 'FUEL', 1)
    print(ore)
    
    needed_ore = 0
    num_fuel = int(1e12) // ore
    
    for i in range(5):
        produce_compound.extra = {}
        needed_ore = produce_compound(reactions, 'FUEL', num_fuel)
        num_fuel = math.floor((int(1e12) / needed_ore)*num_fuel)
        print(num_fuel)
    
    while True:
    
        needed_ore_old = needed_ore
        produce_compound.extra = {}
        needed_ore = produce_compound(reactions, 'FUEL', num_fuel)
        print(needed_ore)
    
        if needed_ore > int(1e12) and needed_ore_old > int(1e12):
            num_fuel -= 1
            continue
        elif needed_ore > int(1e12) and needed_ore_old < int(1e12):
            num_fuel -= 1
            break
        elif needed_ore < int(1e12) and needed_ore_old > int(1e12):
            break
        elif needed_ore < int(1e12) and needed_ore_old < int(1e12):
            num_fuel += 1
            continue
    
    
    print(num_fuel)
    
    2 votes
  18. Comment on Day 13: Care Package in ~comp

    Gyrfalcon
    Link
    I liked this one. I maybe overcomplicated it in displaying the board but oh well. Code is in its happy little Python modules again for both parts 1 and 2. Intputer.py class Intputer(object): def...

    I liked this one. I maybe overcomplicated it in displaying the board but oh well.

    Code is in its happy little Python modules again for both parts 1 and 2.

    Intputer.py
    class Intputer(object):
    
        def __init__(self, instructions, pointer=0, relative_base=0, output_block=False):
    
            self.instructions = instructions
            self.pointer = pointer
            self.relative_base = relative_base
            self.curr_instruction = [int(num) for num in str(self.instructions[self.pointer])]
            self.output_block = output_block
            self.output_buffer = []
            self.input_buffer = []
            self.block = False
            self.isHalted = False
    
        def get_operands(self):
            for i in range(5 - len(self.curr_instruction)):
                self.curr_instruction.insert(0,0) # Pad to get the right number of 0s
    
            if self.curr_instruction[0] == 0:
                out_location = self.instructions[self.pointer + 3]
            elif self.curr_instruction[0] == 2:
                out_location = self.instructions[self.pointer + 3] + self.relative_base
            else:
                raise ValueError("Incorrect mode for output operand!")
    
            if self.curr_instruction[1] == 0:
                op_2_location = self.instructions[self.pointer+2]
            elif self.curr_instruction[1] == 1:
                op_2_location = self.pointer + 2
            elif self.curr_instruction[1] == 2:
                op_2_location = self.instructions[self.pointer+2] + self.relative_base
            else:
                raise ValueError("Incorrect mode for second operand!")
    
            if self.curr_instruction[2] == 0:
                op_1_location = self.instructions[self.pointer+1]
            elif self.curr_instruction[2] == 1:
                op_1_location = self.pointer + 1
            elif self.curr_instruction[2] == 2:
                op_1_location = self.instructions[self.pointer+1] + self.relative_base
            else:
                raise ValueError("Incorrect mode for first operand!")
    
            # We handle big memory on write, so this covers it on read
            try:
                op_1 = self.instructions[op_1_location]
            except IndexError:
                op_1 = 0
    
            try:
                op_2 = self.instructions[op_2_location]
            except IndexError:
                op_2 = 0
    
            return [op_1, op_2, out_location]
    
        def get_io_operand(self):
    
            for i in range(3 - len(self.curr_instruction)):
                self.curr_instruction.insert(0,0) # Pad to get the right number of 0s
    
            if self.curr_instruction[0] == 0:
                op = self.instructions[self.pointer + 1]
            elif self.curr_instruction[0] == 1:
                op = self.pointer + 1
            elif self.curr_instruction[0] == 2:
                op = self.instructions[self.pointer + 1] + self.relative_base
            else:
                raise ValueError("Incorrect mode for I/O operand!")
    
            return op
    
        def write_result(self, result, location):
    
            # Extend memory if needed
            try:
                self.instructions[location] = result
            except IndexError:
                self.instructions.extend([0] * ((location + 1) - len(self.instructions)))
                self.instructions[location] = result
    
        def add_instruction(self):
            operands = self.get_operands()
    
            result = operands[0] + operands[1]
            self.write_result(result, operands[2])
            self.pointer += 4
    
        def mult_instruction(self):
            operands = self.get_operands()
    
            result = operands[0] * operands[1]
            self.write_result(result, operands[2])
            self.pointer += 4
    
        def output_instruction(self):
    
            operand = self.get_io_operand()
    
            self.output_buffer.append(self.instructions[operand])
    
            if self.output_block:
                self.block = True
    
            self.pointer += 2
    
        def input_instruction(self):
    
            # Check for no input, and block if input is empty
            if len(self.input_buffer) < 1:
                self.block = True
                return
    
            operand = self.get_io_operand()
    
            self.write_result(self.input_buffer.pop(), operand)
            self.pointer += 2
    
        def jump_instruction(self, jump_type):
    
            operands = self.get_operands()
    
            if ((operands[0] and jump_type) or (not operands[0] and not jump_type)):
                self.pointer = operands[1]
            else:
                self.pointer += 3
    
        def less_than_instruction(self):
    
            operands = self.get_operands()
    
            if operands[0] < operands[1]:
                self.write_result(1, operands[2])
            else:
                self.write_result(0, operands[2])
    
            self.pointer += 4
    
        def equals_instruction(self):
    
            operands = self.get_operands()
    
            if operands[0] == operands[1]:
                self.write_result(1, operands[2])
            else:
                self.write_result(0, operands[2])
    
            self.pointer += 4
    
        def modify_base_instruction(self):
    
            operand = self.get_io_operand()
    
            self.relative_base += self.instructions[operand]
    
            self.pointer += 2
    
        def process_instructions(self, inputs=None):
    
            if inputs is not None:
                try:
                    for item in inputs:
                        self.input_buffer.insert(0, item)
                except TypeError: # Catch singular inputs here
                    self.input_buffer.insert(0,inputs)
            else:
                inputs = []
    
            while True:
    
                if self.pointer > (len(self.instructions) - 1):
                    break
                elif self.instructions[self.pointer] == 99:
                    self.isHalted = True
                    break
    
                self.curr_instruction = [int(num) for num in str(self.instructions[self.pointer])]
    
                if self.curr_instruction[-1] == 1:
                    self.add_instruction()
                elif self.curr_instruction[-1] == 2:
                    self.mult_instruction()
                elif self.curr_instruction[-1] == 3:
                    self.input_instruction()
                elif self.curr_instruction[-1] == 4:
                    self.output_instruction()
                elif self.curr_instruction[-1] == 5:
                    self.jump_instruction(True)
                elif self.curr_instruction[-1] == 6:
                    self.jump_instruction(False)
                elif self.curr_instruction[-1] == 7:
                    self.less_than_instruction()
                elif self.curr_instruction[-1] == 8:
                    self.equals_instruction()
                elif self.curr_instruction[-1] == 9:
                    self.modify_base_instruction()
    
                if self.block:
                    self.block = False
                    return
    
        def clear_output_buffer(self):
            self.output_buffer = []
    
    def load_instructions(input_file):
    
        with open(input_file, 'r') as fid:
            data = fid.readline()
    
        instructions = data.strip().split(',')
    
        for idx in range(len(instructions)):
            instructions[idx] = int(instructions[idx])
    
        return instructions
    
    CarePackage.py
    import Intputer
    import copy
    import random
    import time
    import os # To make a prettier display lol
    
    def update_play_field(play_field, updates):
    
        for idx in range(0, len(updates), 3):
    
            location = (updates[idx], updates[idx+1])
            item = updates[idx+2]
    
            play_field[location] = item
    
        return play_field
    
    def count_blocks(play_field):
    
        num_blocks = 0
        for key, value in play_field.items():
            if value == 2:
                num_blocks += 1
    
        return num_blocks
    
    def update_display(display, play_field):
    
        for idx in range(len(display)):
            for jdx in range(len(display[0])):
    
                display[idx][jdx] = play_field[(jdx, idx)]
    
    def show_display(display):
    
        for idx in range(len(display)):
            for jdx in range(len(display[0])):
    
                if display[idx][jdx] == 0:
                    print(" ", end='')
                elif display[idx][jdx] == 1:
                    print("W", end='')
                elif display[idx][jdx] == 2:
                    print("B", end='')
                elif display[idx][jdx] == 3:
                    print("~", end='')
                elif display[idx][jdx] == 4:
                    print("o", end='')
    
            print("")
    
    input_file = "PuzzleInput.txt"
    
    instructions = Intputer.load_instructions(input_file)
    
    play_field = {}
    
    arcade_computer = Intputer.Intputer(copy.copy(instructions))
    
    arcade_computer.process_instructions()
    
    play_field = update_play_field(play_field, arcade_computer.output_buffer)
    
    num_blocks = count_blocks(play_field)
    
    print(num_blocks)
    
    initial_blocks = num_blocks
    
    instructions[0] = 2 # Gotta put in those quarters!
    
    score = 0
    
    play_field = {}
    
    arcade_computer = Intputer.Intputer(copy.copy(instructions))
    
    arcade_computer.process_instructions()
    
    play_field = update_play_field(play_field, arcade_computer.output_buffer)
    
    # Have to fudge these over one for some reason
    x_max = max([key[0] for key in play_field.keys()]) + 1
    y_max = max([key[1] for key in play_field.keys()]) + 1
    
    display = [[0 for i in range(x_max)] for j in range(y_max)]
    
    update_display(display, play_field)
    
    num_blocks = count_blocks(play_field)
    
    while num_blocks != 0:
    
        arcade_computer.clear_output_buffer()
        os.system('clear')
        show_display(display)
    
        paddle_x = 0
        ball_x = 0
    
        for keys, items in play_field.items():
            if items == 4:
                ball_x = keys[0]
            elif items == 3:
                paddle_x = keys[0]
    
            if ball_x and paddle_x:
                break
    
        user_input = 0
    
        if ball_x < paddle_x:
            user_input = -1
        elif ball_x > paddle_x:
            user_input = 1
    
        arcade_computer.process_instructions(user_input)
    
        play_field = update_play_field(play_field, arcade_computer.output_buffer)
    
        update_display(display, play_field)
    
        num_blocks = count_blocks(play_field)
    
        if arcade_computer.isHalted:
            print("GAME OVER")
            break
    
        time.sleep(0.01)
    
    score = play_field[(-1, 0)]
    print(initial_blocks - num_blocks)
    
    print(score)
    
    Part 2 Spoilers

    I used a super simple bot for playing the game. All it does is move towards the ball, and that was enough for me to get it. I tried playing by hand but it was frustrating, and I figured doing all the work of a replay system like I saw discussed here and then having to play would be more effort than just making a simple bot. I did try to just have it move randomly but that did not really work so I gave up on it.

    1 vote