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)
예제 #3
0
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)