class TripleDoohickey(object): def __init__(self): self.first = Doohickey() self.second = Doohickey() self.third = Doohickey() self.a() def a(self): """Returns 'aba' and sets the Doohickies to a, b, and a. >>> doo = TripleDoohickey() >>> doo.a() 'aba' """ result = '' result += self.first.a() result += self.second.b() result += self.third.a() return result def b(self): """Returns 'bab' and sets the Doohickies to b, a and b. >>> doo = TripleDoohickey() >>> doo.b() 'bab' """ result = '' result += self.first.b() result += self.second.a() result += self.third.b() return result def other(self): """Returns the opposite of the last time, or 'bab' at first. >>> doo = TripleDoohickey() >>> doo.other() 'bab' >>> doo.other() 'aba' >>> doo.a() 'aba' >>> doo.other() 'bab' >>> doo.b() 'bab' >>> doo.other() 'aba' >>> doo.other() 'bab' >>> doo.other() 'aba' """ result = '' result += self.first.other() result += self.second.other() result += self.third.other() return result
class Fnooblatz1000(object): def __init__(self): self.reset_everything() def printable_display(self): printable = '\n' for row in self.display: for column in row: printable += column printable += '\n' return printable def print_curses(self,stdscr): for y in range(len(self.display)): for x in range(len(self.display[y])): stdscr.addch(y,x,ord(self.display[y][x])) stdscr.refresh() def set_width(self,width): if width < 1: return False while len(self.display[0]) > width: self.press_button(3) while len(self.display[0]) < width: self.press_button(1) assert(len(self.display[0]) == width) return True def set_height(self,height): if height < 1: return False while len(self.display) > height: self.press_button(4) while len(self.display) < height: self.press_button(2) assert(len(self.display) == height) return True def reset_everything(self): empty_row = collections.deque() empty_row.append('*') self.display = collections.deque() self.display.append(empty_row) self.buttons_pressed = collections.deque() self.cursor_stack = collections.deque() self.background_instructions = collections.deque() self.background_current_instruction = -1 # So it starts at zero. self.background_processing_on = False self.alternator = True self.cursor_row = 0 self.cursor_column = 0 self.rotation_direction = 1 self.doohickey = Doohickey() self.remembered_character = ' ' def check_cursor_bounds(self): if self.cursor_row < 0: self.cursor_row = 0 if self.cursor_column < 0: self.cursor_column = 0 while self.cursor_row >= len(self.display): self.cursor_row -= 1 while self.cursor_column >= len(self.display[0]): self.cursor_column -= 1 def execute_sequence(self,sequence): for instruction in sequence: self.press_button(instruction) def execute_background_instruction(self): if len(self.background_instructions) < 1: return # If there are no instructions, there's nothing to do. self.background_current_instruction += 1 if self.background_current_instruction >= len(self.background_instructions): self.background_current_instruction = 0 instruction = \ self.background_instructions[self.background_current_instruction] self.execute_single_instruction(instruction) def press_button(self,button_number): self.buttons_pressed.append(button_number) if len(self.buttons_pressed) > 1024: self.buttons_pressed.popleft() if self.alternator: self.alternator = False else: self.alternator = True # The alternator, as you can see, alternates # BEFORE each instruction is executed. Confusing # but true! self.execute_single_instruction(button_number) if self.background_processing_on: self.execute_background_instruction() def execute_single_instruction(self,button_number): if button_number == 0: self.reset_everything() return # Resets everything. # # This functionality was desperately necessary # of course on actual Fnooblatz 1000s, which if # given difficult computations would overheat and # smoke furiously. I don't think there's an FB1000 # would have made it through the first night without # this button. Hopefully on this simulator it # should be necessary slightly less desperately, # but it's still useful for getting out of any of # the confusing internal states a Fnooblatz is # prone to! if button_number == 1: for row in self.display: row.append('*') return if button_number == 2: new_row = collections.deque() for column in self.display[0]: new_row.append('*') self.display.append(new_row) return # The above commands to expand the Fnooblatz screen # are necessary because the display starts 1 pixel by # 1 pixel. But why does it? Well, besides the fact # that in the old days people would run their Fnooblatz # at as small a resolution as they actually needed, to # conserve energy, or that a cheap one-pixel display # would often be used to diagnose what's wrong with a # sick Fnooblatz (so it couldn't burn out a whole # expensive display), there's also the case of those # folks who would insist that they just didn't need # more than a one-pixel display to get by. And it's # true, you can read the news quite adequately one # character at a time, if you're patient. if button_number == 3: if len(self.display[0]) > 1: for row in self.display: row.pop() self.check_cursor_bounds() return if button_number == 4: if len(self.display) > 1: self.display.pop() self.check_cursor_bounds() return # If you can make the display bigger, it's convenient # to be able to make it smaller too, instead of just # having to start all over! if button_number == 5: for row in self.display: row.rotate(self.rotation_direction) return if button_number == 6: self.display.rotate(self.rotation_direction) return # Rotating the display, which gives us just barely # enough instructions to paint pictures! :D if button_number == 7: return # Button seven doesn't do anything, 'anything' that is # except for all the stuff the Fnooblatz does every # instruction (alternate the alternator, etc.)-- but, # like, nothing EXTRA. if button_number == 8: self.check_cursor_bounds() if self.alternator: self.display[self.cursor_row][self.cursor_column] = '|' else: self.display[self.cursor_row][self.cursor_column] = '-' return # This allows you to see the behavior of the alternator, # and combined with the following instructions to move the # cursor can be useful for drawing. if button_number == 9: if self.alternator: self.cursor_row += 1 else: self.cursor_row -= 1 self.check_cursor_bounds() return # The previous instruction allows you to go either up OR down! if button_number == 10: if self.alternator: self.cursor_column += 1 else: self.cursor_column -= 1 self.check_cursor_bounds() return # The previous instruction allows you to go either left OR right! if button_number == 11: self.check_cursor_bounds() self.display[self.cursor_row][self.cursor_column] = ' ' return # Spaces. Ah. Relaxing. if button_number == 12: self.display[self.cursor_row][self.cursor_column] = self.doohickey.other() return # The Fnooblatz1000 contains a simple interface # to access its contained Doohickey! This presses # the 'other' button on the Doohickey and prints # the result. if button_number == 13: self.display[self.cursor_row][self.cursor_column] = self.doohickey.a() return if button_number == 14: self.display[self.cursor_row][self.cursor_column] = self.doohickey.b() return # With these you can use the Doohickey to write 'a' and # 'b', which is useful for talking about ABBA! if button_number == 15: self.buttons_pressed.pop() # Otherwise it gets weird. self.background_instructions = self.buttons_pressed self.background_current_instruction = -1 # So we start at zero. self.buttons_pressed = collections.deque() return # Moves what you've done recently to the background, # where it will continue to happen as you go on doing # other things. Fun! if button_number == 16: self.buttons_pressed.pop() # Otherwise it gets weird. if self.background_processing_on: self.background_processing_on = False else: self.background_processing_on = True return # Toggles background processing. If it's turned # on it will start quite immediately, before the # next instruction. if button_number == 17: self.cursor_row = 0 self.cursor_column = 0 return # An orderly instruction for finding one's place. if button_number == 18: self.check_cursor_bounds() if self.display[self.cursor_row][self.cursor_column] != '|': self.cursor_column -= 1 self.check_cursor_bounds() return if button_number == 19: self.check_cursor_bounds() if self.display[self.cursor_row][self.cursor_column] != '|': self.cursor_column += 1 self.check_cursor_bounds() return if button_number == 20: self.check_cursor_bounds() if self.display[self.cursor_row][self.cursor_column] != '-': self.cursor_row -= 1 self.check_cursor_bounds() return if button_number == 21: self.check_cursor_bounds() if self.display[self.cursor_row][self.cursor_column] != '-': self.cursor_row += 1 self.check_cursor_bounds() return # The above instructions allow you to travel easily in # all four directions, while also allowing you to safely # contain yourself in a box-- or a maze perhaps! if button_number == 22: if self.rotation_direction == 1: self.rotation_direction = -1 else: self.rotation_direction = 1 return # Allows you to switch to rotating the display in the # opposite direction. This might make you less dizzy. if button_number == 23: self.check_cursor_bounds() self.remembered_character = self.display[self.cursor_row][self.cursor_column] return if button_number == 24: self.check_cursor_bounds() self.display[self.cursor_row][self.cursor_column] = self.remembered_character return # Allows you to remember a character for a little # while. Useful for smearing things around! if button_number == 25: self.check_cursor_bounds() self.display[self.cursor_row][self.cursor_column] = \ alphabetrotator.rotate(self.display[self.cursor_row][self.cursor_column]) return # Combined with the Doohickey, which prints out # 'a's and 'b's, this allows you to write any # letter of the alphabet! if button_number == 26: self.check_cursor_bounds() if self.display[self.cursor_row][self.cursor_column] == ' ': return # Do nothing to blank spaces. self.display[self.cursor_row][self.cursor_column] = 'g' return # This is useful for GIG. if button_number == 27: self.cursor_stack.append((self.cursor_row, self.cursor_column)) if len(self.cursor_stack) > 1024: self.cursor_stack.popleft() return if button_number == 28: if len(self.cursor_stack) > 0: (self.cursor_row, self.cursor_column) = self.cursor_stack.pop() return # Whereever you go, there you are, but it's also nice # to remember where else you've been recently. self.display[0][0] = '@'