def move(model: TOAHModel, origin: int, dest: int): ''' Module method to apply one move to the given model, and print any error message to the console. model - the TOAHModel that you want to modify origin - the stool number (indexing from 0!) of the cheese you want to move dest - the stool number that you want to move the top cheese on stool origin onto. >>> M = TOAHModel(4) >>> M.fill_first_stool(5) >>> M.move(0,2) >>> M.get_move_seq() MoveSequence([(0, 2)]) >>> M1 = TOAHModel(4) >>> M1.fill_first_stool(5) >>> M1.move(0,2) >>> M1.move(2,1) >>> M1.get_move_seq() MoveSequence([(0, 2), (2, 1)]) ''' has_cheese = False length_of_dest_stool = len(model.stools[dest]) if length_of_dest_stool > 0: has_cheese = True if has_cheese and model.top_cheese(origin).size > model.top_cheese(dest).size: raise IllegalMoveError('Illegal move, try again!') else: relocate = model.stools[origin].pop() model.stools[dest].append(relocate) model.move_count += 1 moves_tup = (origin,dest) model._move_seq.append(moves_tup)
class GUIController: def __init__(self: 'GUIController', number_of_cheeses: int, number_of_stools: int, content_width: float, content_height: float, cheese_scale: float): """ Initialize a new GUIView. number_of_cheeses - number of cheese to tower on the first stool number_of_stools - number of stools content_width - width in pixels of the working area content_height - height in pixels of the working area cheese_scale - height in pixels for showing cheese thicknesses, and to scale cheese diameters """ self._model = TOAHModel(number_of_stools) self._stools = [] self._cheese_to_move = None self._blinking = False self._number_of_stools = number_of_stools self.cheese_scale = cheese_scale self.root = TI.Tk() canvas = TI.Canvas(self.root, background="blue", width=content_width, height=content_height) canvas.pack(expand=True, fill=TI.BOTH) self.moves_label = TI.Label(self.root) self.show_number_of_moves() self.moves_label.pack() # the dimensions of a stool are the same as a cheese that's # one size bigger than the biggest of the number_of_cheeses cheeses. for stool_ind in range(number_of_stools): width = self.cheese_scale * (number_of_cheeses + 1) x_cent = content_width * (stool_ind + 1) / (number_of_stools + 1.0) y_cent = content_height - cheese_scale / 2 stool = StoolView(width, lambda s: self.stoolClicked(s), canvas, self.cheese_scale, x_cent, y_cent) self._stools.append(stool) # Can't use self._model.fill_first_stool because we need to # use CheeseView objects instead of just Cheese objects. total_size = self.cheese_scale for sizeparam in range(1, number_of_cheeses + 1): size = (number_of_cheeses + 1 - sizeparam) width = self.cheese_scale * size x_cent = content_width / (number_of_stools + 1.0) y_cent = content_height - cheese_scale / 2 - total_size cheese = CheeseView(size, width, lambda c: self.cheeseClicked(c), canvas, self.cheese_scale, x_cent, y_cent) self._model.add(0, cheese) total_size += self.cheese_scale def cheeseClicked(self: 'GUIController', cheese: 'CheeseView'): """React to cheese being clicked: if not in the middle of blinking then select cheese for moving, or for moving onto. cheese - clicked cheese """ if not self._blinking: self.select_cheese(cheese) def stoolClicked(self: 'GUIController', stool: 'StoolView'): """React to cheese being clicked: if not in the middle of blinking then select cheese for moving, or for moving onto. cheese - clicked cheese """ if not self._blinking: self.select_stool(stool) def select_cheese(self: 'GUIController', cheese: CheeseView): """ Called by cheeseClicked. If no cheese is selected to move, then select the cheese at top of clicked_cheese's stool (which may be clicked_cheese itself) and highlight it. If selected_cheese is already highlighted, then unhighlight it. Otherwise try to move self._cheese_to_move onto the stool that clicked_cheese is on. """ stool = self._stools[self._model.cheese_location(cheese)] stool_index = self.stool_index(stool) cheese = self._model.top_cheese(stool_index) #print(stool, stool_index, cheese) if self._cheese_to_move is None: self._cheese_to_move = cheese self._cheese_to_move.highlight(True) self.root.update() elif self._cheese_to_move is cheese: self._cheese_to_move.highlight(False) self._cheese_to_move = None self.root.update() else: self.select_platform_for_move(cheese, stool_index) def select_stool(self: 'GUIController', dest_stool: StoolView): """ Called by stoolClicked. Initiate a move if there is already some cheese highlighted (i.e. self._cheese_to_move is not None), unless self._cheese_to_move is on dest_stool, in which case do nothing. """ if self._cheese_to_move is not None: origin_stool = self._stools[ self._model.cheese_location(self._cheese_to_move)] dest_stool_index = self.stool_index(dest_stool) origin_stool_index = self.stool_index(origin_stool) if origin_stool_index != dest_stool_index: top_cheese = self._model.top_cheese(dest_stool_index) if top_cheese is None: self.select_platform_for_move(dest_stool, dest_stool_index) else: self.select_platform_for_move(top_cheese, dest_stool_index) def select_platform_for_move(self: 'GUIController', platform: PlatformView, stool_index: int): """ Actually responsible for showing the cheese move on the screen, and for telling the model to update itself. Change self._cheese_to_move's coordinates so that it's on top of platform. platform - the StoolView or CheeseView that we want to move self._cheese_to_move onto. stool_index - if platform is a stool, then this is its index, and if platform is a cheese then this is the index of the stool that it is on. """ if self._cheese_to_move is not None: try: from_stool = self._model.cheese_location( self._cheese_to_move) self._model.move(from_stool, stool_index) self._cheese_to_move.place(platform.x_center, platform.y_center - self.cheese_scale) self.show_number_of_moves() except IllegalMoveError as e: print(e) self._blinking = True for i in range(10): self._cheese_to_move.highlight(i % 2 != 0) self.root.update() time.sleep(0.1) self._blinking = False self._cheese_to_move.highlight(False) self._cheese_to_move = None self.root.update() def stool_index(self: 'GUIView', stool: 'StoolView') -> int: return self._stools.index(stool) def show_number_of_moves(self: 'GUIView'): """Show the number of moves so far.""" self.moves_label.config(text='Number of moves: ' + str(self._model.number_of_moves())) def get_stool(self: 'GUIController', i: int) -> 'StoolView': return self._stools[i] def top_cheese(self: 'GUIController', i: int) -> 'CheeseView': return self._model.top_cheese(i)
class GUIController: def __init__(self: 'GUIController', number_of_cheeses: int, number_of_stools: int, content_width: float, content_height: float, cheese_scale: float): """ Initialize a new GUIView. number_of_cheeses - number of cheese to tower on the first stool number_of_stools - number of stools content_width - width in pixels of the working area content_height - height in pixels of the working area cheese_scale - height in pixels for showing cheese thicknesses, and to scale cheese diameters """ self._model = TOAHModel(number_of_stools) self._stools = [] self._cheese_to_move = None self._blinking = False self._number_of_stools = number_of_stools self.cheese_scale = cheese_scale self.root = TI.Tk() canvas = TI.Canvas(self.root, background="blue", width=content_width, height=content_height) canvas.pack(expand=True, fill=TI.BOTH) self.moves_label = TI.Label(self.root) self.show_number_of_moves() self.moves_label.pack() # the dimensions of a stool are the same as a cheese that's # one size bigger than the biggest of the number_of_cheeses cheeses. for stool_ind in range(number_of_stools): width = self.cheese_scale * (number_of_cheeses + 1) x_cent = content_width * (stool_ind + 1) / (number_of_stools + 1.0) y_cent = content_height - cheese_scale / 2 stool = StoolView(width, lambda s: self.stoolClicked(s), canvas, self.cheese_scale, x_cent, y_cent) self._stools.append(stool) # Can't use self._model.fill_first_stool because we need to # use CheeseView objects instead of just Cheese objects. total_size = self.cheese_scale for sizeparam in range(1, number_of_cheeses + 1): size = (number_of_cheeses + 1 - sizeparam) width = self.cheese_scale * size x_cent = content_width / (number_of_stools + 1.0) y_cent = content_height - cheese_scale / 2 - total_size cheese = CheeseView(size, width, lambda c: self.cheeseClicked(c), canvas, self.cheese_scale, x_cent, y_cent) self._model.add(0, cheese) total_size += self.cheese_scale def cheeseClicked(self: 'GUIController', cheese: 'CheeseView'): """React to cheese being clicked: if not in the middle of blinking then select cheese for moving, or for moving onto. cheese - clicked cheese """ if not self._blinking: self.select_cheese(cheese) def stoolClicked(self: 'GUIController', stool: 'StoolView'): """React to cheese being clicked: if not in the middle of blinking then select cheese for moving, or for moving onto. cheese - clicked cheese """ if not self._blinking: self.select_stool(stool) def select_cheese(self: 'GUIController', cheese: CheeseView): """ Called by cheeseClicked. If no cheese is selected to move, then select the cheese at top of clicked_cheese's stool (which may be clicked_cheese itself) and highlight it. If selected_cheese is already highlighted, then unhighlight it. Otherwise try to move self._cheese_to_move onto the stool that clicked_cheese is on. """ stool = self._stools[self._model.cheese_location(cheese)] stool_index = self.stool_index(stool) cheese = self._model.top_cheese(stool_index) #print(stool, stool_index, cheese) if self._cheese_to_move is None: self._cheese_to_move = cheese self._cheese_to_move.highlight(True) self.root.update() elif self._cheese_to_move is cheese: self._cheese_to_move.highlight(False) self._cheese_to_move = None self.root.update() else: self.select_platform_for_move(cheese, stool_index) def select_stool(self: 'GUIController', dest_stool: StoolView): """ Called by stoolClicked. Initiate a move if there is already some cheese highlighted (i.e. self._cheese_to_move is not None), unless self._cheese_to_move is on dest_stool, in which case do nothing. """ if self._cheese_to_move is not None: origin_stool = self._stools[self._model.cheese_location( self._cheese_to_move)] dest_stool_index = self.stool_index(dest_stool) origin_stool_index = self.stool_index(origin_stool) if origin_stool_index != dest_stool_index: top_cheese = self._model.top_cheese(dest_stool_index) if top_cheese is None: self.select_platform_for_move(dest_stool, dest_stool_index) else: self.select_platform_for_move(top_cheese, dest_stool_index) def select_platform_for_move(self: 'GUIController', platform: PlatformView, stool_index: int): """ Actually responsible for showing the cheese move on the screen, and for telling the model to update itself. Change self._cheese_to_move's coordinates so that it's on top of platform. platform - the StoolView or CheeseView that we want to move self._cheese_to_move onto. stool_index - if platform is a stool, then this is its index, and if platform is a cheese then this is the index of the stool that it is on. """ if self._cheese_to_move is not None: try: from_stool = self._model.cheese_location(self._cheese_to_move) self._model.move(from_stool, stool_index) self._cheese_to_move.place( platform.x_center, platform.y_center - self.cheese_scale) self.show_number_of_moves() except IllegalMoveError as e: print(e) self._blinking = True for i in range(10): self._cheese_to_move.highlight(i % 2 != 0) self.root.update() time.sleep(0.1) self._blinking = False self._cheese_to_move.highlight(False) self._cheese_to_move = None def stool_index(self: 'GUIView', stool: 'StoolView') -> int: return self._stools.index(stool) def show_number_of_moves(self: 'GUIView'): """Show the number of moves so far.""" self.moves_label.config(text='Number of moves: ' + str(self._model.number_of_moves())) def get_stool(self: 'GUIController', i: int) -> 'StoolView': return self._stools[i] def top_cheese(self: 'GUIController', i: int) -> 'CheeseView': return self._model.top_cheese(i)