Пример #1
0
    def __init__(self, master, infoPanel, intervalBar):
        """Constructor

        Parameters:
            master (tk.Tk|tk.Frame): The parent widget
        """
        self._master = master
        self._infoPanel = infoPanel
        self._infoPanel.init_window(master, self)
        self._intervalBar = intervalBar

        self._playing = True

        self._image_manager = ImageManager('images/dots/', loader=load_image)

        # Game
        counts = [10, 15, 25, 25]
        random.shuffle(counts)
        # randomly pair counts with each kind of dot
        objectives = zip(
            [BasicDot(1), BasicDot(2),
             BasicDot(4), BasicDot(3)], counts)

        self._objectives = ObjectiveManager(objectives)
        self._infoPanel.set_objectives(
            [self._objectives.get_status()[i][1] for i in range(4)])

        # Game
        dead_cells = {(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2),
                      (4, 3), (4, 4), (0, 7), (1, 7), (6, 7), (7, 7)}
        self._game = DotGame({ButterflyDot: 1},
                             objectives=self._objectives,
                             kinds=(1, 2, 3, 4),
                             size=(8, 8),
                             dead_cells=dead_cells)

        # The following code may be useful when you are implementing task 2:
        for i in range(0, 4):
            for j in range(0, 2):
                position = i, j
                self._game.grid[position].set_dot(ButterflyDot(3))
        self._game.grid[(7, 3)].set_dot(ButterflyDot(1))

        # Grid View
        self._grid_view = GridView(master,
                                   size=self._game.grid.size(),
                                   image_manager=self._image_manager)
        self._grid_view.pack()
        self._grid_view.draw(self._game.grid)
        self.draw_grid_borders()

        # Events
        self.bind_events()

        # Set initial score again to trigger view update automatically
        self._refresh_status()
Пример #2
0
    def reset_with_com(self):
        # initialize pygame
        pygame.init()
        # load background music
        pygame.mixer.music.load('bgm2.ogg')
        pygame.mixer.music.play(-1, 0.0)
        pygame.mixer.music.set_volume(0.3)

        counts = [10, 15, 25, 25]
        random.shuffle(counts)
        # randomly pair counts with each kind of dot
        objectives = zip(
            [BasicDot(1), BasicDot(2),
             BasicDot(4), BasicDot(3)], counts)
        self._objectives = ObjectiveManager(list(objectives))

        # reset the objectives
        self._obj.draw(self._objectives.get_status())

        dead_cells = {(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2),
                      (4, 3), (4, 4), (0, 7), (1, 7), (6, 7), (7, 7)}
        self._game = CompanionGame({
            BasicDot: 1,
            CompanionDot: 1
        },
                                   companion=EskimoCompanion(),
                                   objectives=self._objectives,
                                   kinds=(1, 2, 3, 4),
                                   size=(8, 8),
                                   dead_cells=dead_cells)
        # reset the game(score, move)
        self._game.reset()

        # reset the grid
        self.draw_grid()

        # reset the score
        scores = self.info_panel.set_scores()
        scores.config(text=str(self._game.get_score()))
        # reset the move
        moves = self.info_panel.remain_moves()
        moves.config(text=str(self._game.get_moves()))

        # reset the interval bar
        self.interval_bar.progress_bar(0)
        self._game.companion.reset()
        # reset the companion bar
        self.interval_bar.com_charge_bar_reset()
        # reset the companion charge button
        self.action_bar.companion_charge().config(state='normal')
        # reset the color remover button
        self.action_bar.colour_remove().config(state='normal')
Пример #3
0
    def generate_objectives(cls, data):
        """(ObjectiveManager) Create the new objectives from data

        Parameters:
            data (list<list>): The list of objective dot, kind and count
        """
        objectives = []
        for name, kind, count in data:
            objective = (cls.generate_dot(name, kind), count)
            objectives.append(objective)
        return ObjectiveManager(objectives)
Пример #4
0
    def __init__(self,
                 dot_weights,
                 kinds=(1, 2, 3),
                 size=(6, 6),
                 dead_cells=None,
                 min_group=2,
                 animation=True):

        self.companion = UselessCompanion(max_charge=0)

        super().__init__(dot_weights,
                         companion=self.companion,
                         kinds=kinds,
                         size=size,
                         dead_cells=dead_cells,
                         min_group=min_group,
                         moves=0,
                         animation=animation)

        self.objectives = ObjectiveManager(())
Пример #5
0
    def __init__(self, master, icon=None, switch=False):
        """Constructor

        Parameters:
            master (tk.Tk|tk.Frame): The parent widget
        """
        self._master = master
        master.title(self.DEFAULT_TITLE)

        self.flag = False

        self.default_icon = tk.PhotoImage(file='images/companions/useless.gif')

        self.extra_icon = tk.PhotoImage(file='images/companions/penguin.gif')

        self.icon = icon if icon else self.default_icon

        self.default_step = 1

        self.score = 0

        self._playing = True

        self._image_manager = ImageManager('images/dots/', loader=load_image)

        menu_bar = tk.Menu(master)
        master.config(menu=menu_bar)

        file_menu = tk.Menu(menu_bar)
        menu_bar.add_cascade(label='File', menu=file_menu)
        file_menu.add_command(label='New Game', command=self.reset)
        file_menu.add_command(label='Exit Game', command=self.close)

        master.protocol('WM_DELETE_WINDOW', self.close)

        # Game
        counts = [10, 15, 25, 25]
        random.shuffle(counts)
        # randomly pair counts with each kind of dot
        objectives = zip(
            [BasicDot(1), BasicDot(2),
             BasicDot(4), BasicDot(3)], counts)

        self._objectives = ObjectiveManager(objectives)

        dead_cells = {(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2),
                      (4, 3), (4, 4), (0, 7), (1, 7), (6, 7), (7, 7)}
        self.dot_game = DotGame({BasicDot: 1},
                                objectives=self._objectives,
                                kinds=(1, 2, 3, 4),
                                size=(8, 8),
                                dead_cells=dead_cells)
        self.companion_game = CompanionGame(
            {
                BasicDot: 43,
                CompanionDot: 9,
                WildcardDot: 6
            },
            BuffaloCompanion(),
            objectives=self._objectives,
            kinds=(1, 2, 3, 4),
            size=(8, 8),
            dead_cells=dead_cells)

        # Game
        self._game = self.companion_game if switch else self.dot_game
        if self.FIRST:
            reply = askquestion(
                type=messagebox.YESNO,
                title='Select Model',
                message=
                'Would you like to start a new game with "Companion Dot"?')
            if reply == messagebox.YES:
                showinfo('"Companion Dot" Model',
                         'Enjoy the game with "Companion Dot"!')
                self.icon = self.extra_icon
                self._game = self.companion_game
                self._master.title('Dots & Co - "Companion Dot"')
            else:
                showinfo('New Game', 'Enjoy the basic game!')
                self._game = self.dot_game
            self.FIRST = False

        # The following code may be useful when you are implementing task 2:
        # for i in range(0, 4):
        #     for j in range(0, 2):
        #         position = i, j
        #         self._game.grid[position].set_dot(BasicDot(3))
        # self._game.grid[(7, 3)].set_dot(BasicDot(1))

        # InfoPanel
        self.info_panel = InfoPanel(master)
        self.info_panel.set_default_icon(self.icon)
        self.info_panel.decrease_remaining_moves_and_increase_score(
            self._game.get_moves(), self._game.get_score())
        self.info_panel.set_objectives(self._image_manager,
                                       self._objectives.get_status())
        self.info_panel.pack()

        # IntervalBar
        self.interval_bar = IntervalBar(master)
        self.interval_bar.draw_step(self.default_step)
        self.interval_bar.pack()

        # Grid View
        self._grid_view = GridView(master,
                                   size=self._game.grid.size(),
                                   image_manager=self._image_manager)
        self._grid_view.pack()
        self._grid_view.draw(self._game.grid)
        self.draw_grid_borders()

        # Events
        self.bind_events()

        # Set initial score again to trigger view update automatically
        self._refresh_status()
Пример #6
0
class DotsApp:
    """Top level GUI class for simple Dots & Co game"""
    DEFAULT_TITLE = 'Dots & Co'
    FIRST = True

    def __init__(self, master, icon=None, switch=False):
        """Constructor

        Parameters:
            master (tk.Tk|tk.Frame): The parent widget
        """
        self._master = master
        master.title(self.DEFAULT_TITLE)

        self.flag = False

        self.default_icon = tk.PhotoImage(file='images/companions/useless.gif')

        self.extra_icon = tk.PhotoImage(file='images/companions/penguin.gif')

        self.icon = icon if icon else self.default_icon

        self.default_step = 1

        self.score = 0

        self._playing = True

        self._image_manager = ImageManager('images/dots/', loader=load_image)

        menu_bar = tk.Menu(master)
        master.config(menu=menu_bar)

        file_menu = tk.Menu(menu_bar)
        menu_bar.add_cascade(label='File', menu=file_menu)
        file_menu.add_command(label='New Game', command=self.reset)
        file_menu.add_command(label='Exit Game', command=self.close)

        master.protocol('WM_DELETE_WINDOW', self.close)

        # Game
        counts = [10, 15, 25, 25]
        random.shuffle(counts)
        # randomly pair counts with each kind of dot
        objectives = zip(
            [BasicDot(1), BasicDot(2),
             BasicDot(4), BasicDot(3)], counts)

        self._objectives = ObjectiveManager(objectives)

        dead_cells = {(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2),
                      (4, 3), (4, 4), (0, 7), (1, 7), (6, 7), (7, 7)}
        self.dot_game = DotGame({BasicDot: 1},
                                objectives=self._objectives,
                                kinds=(1, 2, 3, 4),
                                size=(8, 8),
                                dead_cells=dead_cells)
        self.companion_game = CompanionGame(
            {
                BasicDot: 43,
                CompanionDot: 9,
                WildcardDot: 6
            },
            BuffaloCompanion(),
            objectives=self._objectives,
            kinds=(1, 2, 3, 4),
            size=(8, 8),
            dead_cells=dead_cells)

        # Game
        self._game = self.companion_game if switch else self.dot_game
        if self.FIRST:
            reply = askquestion(
                type=messagebox.YESNO,
                title='Select Model',
                message=
                'Would you like to start a new game with "Companion Dot"?')
            if reply == messagebox.YES:
                showinfo('"Companion Dot" Model',
                         'Enjoy the game with "Companion Dot"!')
                self.icon = self.extra_icon
                self._game = self.companion_game
                self._master.title('Dots & Co - "Companion Dot"')
            else:
                showinfo('New Game', 'Enjoy the basic game!')
                self._game = self.dot_game
            self.FIRST = False

        # The following code may be useful when you are implementing task 2:
        # for i in range(0, 4):
        #     for j in range(0, 2):
        #         position = i, j
        #         self._game.grid[position].set_dot(BasicDot(3))
        # self._game.grid[(7, 3)].set_dot(BasicDot(1))

        # InfoPanel
        self.info_panel = InfoPanel(master)
        self.info_panel.set_default_icon(self.icon)
        self.info_panel.decrease_remaining_moves_and_increase_score(
            self._game.get_moves(), self._game.get_score())
        self.info_panel.set_objectives(self._image_manager,
                                       self._objectives.get_status())
        self.info_panel.pack()

        # IntervalBar
        self.interval_bar = IntervalBar(master)
        self.interval_bar.draw_step(self.default_step)
        self.interval_bar.pack()

        # Grid View
        self._grid_view = GridView(master,
                                   size=self._game.grid.size(),
                                   image_manager=self._image_manager)
        self._grid_view.pack()
        self._grid_view.draw(self._game.grid)
        self.draw_grid_borders()

        # Events
        self.bind_events()

        # Set initial score again to trigger view update automatically
        self._refresh_status()

    def close(self):
        if self.can_close():
            self._master.destroy()

    def can_close(self):
        reply = askquestion(type=messagebox.YESNO,
                            title='Confirm Exit',
                            message='Are you sure you want to quit?')
        if reply == messagebox.YES:
            return True
        else:
            return False

    def draw_grid_borders(self):
        """Draws borders around the game grid"""

        borders = list(self._game.grid.get_borders())

        # this is a hack that won't work well for multiple separate clusters
        outside = max(borders, key=lambda border: len(set(border)))

        for border in borders:
            self._grid_view.draw_border(border, fill=border != outside)

    def bind_events(self):
        """Binds relevant events"""
        self._grid_view.on('start_connection', self._drag)
        self._grid_view.on('move_connection', self._drag)
        self._grid_view.on('end_connection', self._drop)

        self._game.on('reset', self._refresh_status)
        self._game.on('complete', self._drop_complete)

        self._game.on('connect', self._connect)
        self._game.on('undo', self._undo)

    def _animation_step(self, step_name):
        """Runs for each step of an animation

        Parameters:
            step_name (str): The name (type) of the step
        """
        print(step_name)
        self._refresh_status()
        self.draw_grid()

    def animate(self, steps, callback=lambda: None):
        """Animates some steps (i.e. from selecting some dots, activating companion, etc.

        Parameters:
            steps (generator): Generator which yields step_name (str) for each step in the animation
        """

        if steps is None:
            steps = (None for _ in range(1))

        animation = create_animation(self._master,
                                     steps,
                                     delays=ANIMATION_DELAYS,
                                     delay=DEFAULT_ANIMATION_DELAY,
                                     step=self._animation_step,
                                     callback=callback)
        animation()

    def _drop(self, position):  # pylint: disable=unused-argument
        """Handles the dropping of the dragged connection

        Parameters:
            position (tuple<int, int>): The position where the connection was
                                        dropped
        """
        if not self._playing:
            return

        if self._game.is_resolving():
            return

        self._grid_view.clear_dragged_connections()
        self._grid_view.clear_connections()

        self.animate(self._game.drop())

    def _connect(self, start, end):
        """Draws a connection from the start point to the end point

        Parameters:
            start (tuple<int, int>): The position of the starting dot
            end (tuple<int, int>): The position of the ending dot
        """

        if self._game.is_resolving():
            return
        if not self._playing:
            return
        self._grid_view.draw_connection(
            start, end, self._game.grid[start].get_dot().get_kind())

    def _undo(self, positions):
        """Removes all the given dot connections from the grid view

        Parameters:
            positions (list<tuple<int, int>>): The dot connects to remove
        """
        for _ in positions:
            self._grid_view.undo_connection()

    def _drag(self, position):
        """Attempts to connect to the given position, otherwise draws a dragged
        line from the start

        Parameters:
            position (tuple<int, int>): The position to drag to
        """

        if self._game.is_resolving():
            return
        if not self._playing:
            return

        tile_position = self._grid_view.xy_to_rc(position)

        if tile_position is not None:
            cell = self._game.grid[tile_position]
            dot = cell.get_dot()

            if dot and self._game.connect(tile_position):
                self._grid_view.clear_dragged_connections()
                return

        kind = self._game.get_connection_kind()

        if not len(self._game.get_connection_path()):
            return

        start = self._game.get_connection_path()[-1]

        if start:
            self._grid_view.draw_dragged_connection(start, position, kind)

    @staticmethod
    def remove(*_):
        """Deprecated in 1.1.0"""
        raise DeprecationWarning("Deprecated in 1.1.0")

    def draw_grid(self):
        """Draws the grid"""
        self._grid_view.draw(self._game.grid)

    def reset(self):
        """Resets the game"""
        # raise NotImplementedError()
        if self._playing:
            reply = askquestion(type=messagebox.YESNO,
                                title='Now Playing',
                                message='Are you sure you want to restart?')
            if reply == messagebox.YES:
                self.select_model()
            else:
                pass
        else:
            self.select_model()

    def select_model(self):
        reply = askquestion(
            type=messagebox.YESNOCANCEL,
            title='Select Model',
            message='Would you like to start a new game with "Companion Dot"?')
        if reply == messagebox.YES:
            showinfo('"Companion Dot" Model',
                     'Enjoy the game with "Companion Dot"!')
            self.restart(True)
            # self.restart_o()
            self._master.title('Dots & Co - "Companion Dot"')
        elif reply == messagebox.NO:
            showinfo('New Game', 'Enjoy the basic game!')
            self.restart()
        else:
            pass

    def restart(self, flag=False):
        self.info_panel.destroy()
        self.interval_bar.destroy()
        self._grid_view.destroy()
        if flag:
            self.__init__(self._master, self.extra_icon, True)
        else:
            self.__init__(self._master)

    def check_game_over(self):
        """Checks whether the game is over and shows an appropriate message box if so"""
        state = self._game.get_game_state()

        if state == self._game.GameState.WON:
            showinfo("Game Over!", "You won!!!")
            self._playing = False
        elif state == self._game.GameState.LOST:
            showinfo(
                "Game Over!",
                f"You didn't reach the objective(s) in time. You connected {self._game.get_score()} points"
            )
            self._playing = False

    def _drop_complete(self):
        """Handles the end of a drop animation"""

        # Useful for when implementing a companion
        # if self._game.companion.is_fully_charged():
        #     self._game.companion.reset()
        #     steps = self._game.companion.activate(self._game)
        #     self._refresh_status()
        #
        #     return self.animate(steps)

        # Need to check whether the game is over
        # raise NotImplementedError()  # no mercy for stooges
        print(self._game.get_game_state())
        if self.flag:
            if self._game.companion.is_fully_charged():
                self._game.companion.reset()
                showinfo('Ultimate Skill!!!!', 'BOOM SHAKALAKA!!!!!!!')
                self._game.companion.activate(self._game)
            self.interval_bar.draw_step(self._game.companion.get_charge() + 1)
            self.flag = False
        self.check_game_over()
        if not self._playing:
            print('ok')

    def _refresh_status(self):
        """Handles change in game status"""

        # Normally, this should raise the following error:
        # raise NotImplementedError()
        # But so that the game can work prior to this method being implemented,
        # we'll just print some information
        # Sometimes I believe Python ignores all my comments :(
        score = self._game.get_score()
        print("Score is now {}.".format(score))
        if self.score < score:
            if type(self._game) is type(self.dot_game):
                self.basic_progress()
            else:
                self.companion_progress()
            self.score = score
            self.info_panel.decrease_remaining_moves_and_increase_score(
                self._game.get_moves(), score)
            self.info_panel.refresh_objectives(self._objectives.get_status())

    def basic_progress(self):
        self.default_step += 1
        if self.default_step > 6:
            self.default_step = 1
        self.interval_bar.draw_step(self.default_step)

    def companion_progress(self):
        if self._game.companion.get_charge() < 6:
            self.interval_bar.draw_step(self._game.companion.get_charge() + 1)
        else:
            self.interval_bar.draw_step(self._game.companion.get_charge())
            self.flag = True
Пример #7
0
    def __init__(self, master):
        """Constructor

        Parameters:
            master (tk.Tk|tk.Frame): The parent widget
        """
        pygame.init()
        self._master = master
        master.title('Dots')
        self._playing = True
        self._over = None
        self._player = 'None'
        self._scores = {}
        self._steps = 0
        self._image_manager = ImageManager('images/dots/', loader=load_image)

        # InfoPanel
        self._info = InfoPanel(master)
        self._info.pack(fill=tk.BOTH, expand=1)

        # Login
        top = tk.Toplevel()
        top.title('Login')
        tk.Label(top, text='Welcome to the Dots & Co game!').pack()
        frame = tk.Frame(top)
        frame.pack(side=tk.BOTTOM)
        tk.Label(frame, text="Name: ").pack(side=tk.LEFT)
        entry = tk.Entry(frame, width=20)
        entry.pack(side=tk.LEFT)

        def record(*args):
            self._player = entry.get()
            if self.read_score() == None:
                self.save_score()
            top.destroy()

        tk.Button(frame, text="Start!", command=record).pack(side=tk.RIGHT)
        top.bind('<Return>', record)

        # Menu
        menubar = tk.Menu(master)
        master.config(menu=menubar)
        filemenu = tk.Menu(menubar)
        menubar.add_cascade(label='File', menu=filemenu)
        newgame = tk.Menu(menubar)
        filemenu.add_cascade(label='New Game', menu=newgame)
        newgame.add_command(label='With a Companion',
                            command=self.with_companion)
        newgame.add_command(label='Without a Companion',
                            command=self.without_companion)
        filemenu.add_command(label='Exit', command=self.exit)
        master.protocol("WM_DELETE_WINDOW", self.exit)

        # Game
        counts = [10, 15, 25, 25]
        random.shuffle(counts)
        # randomly pair counts with each kind of dot
        objectives = zip(
            [BasicDot(1), BasicDot(2),
             BasicDot(4), BasicDot(3)], counts)

        self._objectives = ObjectiveManager(list(objectives))
        self._info.set_objectives(list(objectives))

        self._dead_cells = {(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4),
                            (4, 2), (4, 3), (4, 4), (0, 7), (1, 7), (6, 7),
                            (7, 7)}
        self._companion = EskimoCompanion()
        self._game = CompanionGame(
            {
                TurtleDot: 1,
                CompanionDot: 4,
                BasicDot: 11
            },
            objectives=self._objectives,
            companion=self._companion,
            kinds=(1, 2, 3, 4),
            size=(8, 8),
            dead_cells=self._dead_cells)

        # Grid View
        self._grid_view = GridView(self._master,
                                   size=self._game.grid.size(),
                                   image_manager=self._image_manager)
        self._grid_view.pack()
        self._grid_view.draw(self._game.grid)
        self.draw_grid_borders()

        # Events
        self.bind_events()

        # Set initial score again to trigger view update automatically
        self._refresh_status()

        self.read_score()
Пример #8
0
class DotsApp(object):
    """Top level GUI class for simple Dots & Co game"""
    def __init__(self, master):
        """Constructor

        Parameters:
            master (tk.Tk|tk.Frame): The parent widget
        """
        pygame.init()
        self._master = master
        master.title('Dots')
        self._playing = True
        self._over = None
        self._player = 'None'
        self._scores = {}
        self._steps = 0
        self._image_manager = ImageManager('images/dots/', loader=load_image)

        # InfoPanel
        self._info = InfoPanel(master)
        self._info.pack(fill=tk.BOTH, expand=1)

        # Login
        top = tk.Toplevel()
        top.title('Login')
        tk.Label(top, text='Welcome to the Dots & Co game!').pack()
        frame = tk.Frame(top)
        frame.pack(side=tk.BOTTOM)
        tk.Label(frame, text="Name: ").pack(side=tk.LEFT)
        entry = tk.Entry(frame, width=20)
        entry.pack(side=tk.LEFT)

        def record(*args):
            self._player = entry.get()
            if self.read_score() == None:
                self.save_score()
            top.destroy()

        tk.Button(frame, text="Start!", command=record).pack(side=tk.RIGHT)
        top.bind('<Return>', record)

        # Menu
        menubar = tk.Menu(master)
        master.config(menu=menubar)
        filemenu = tk.Menu(menubar)
        menubar.add_cascade(label='File', menu=filemenu)
        newgame = tk.Menu(menubar)
        filemenu.add_cascade(label='New Game', menu=newgame)
        newgame.add_command(label='With a Companion',
                            command=self.with_companion)
        newgame.add_command(label='Without a Companion',
                            command=self.without_companion)
        filemenu.add_command(label='Exit', command=self.exit)
        master.protocol("WM_DELETE_WINDOW", self.exit)

        # Game
        counts = [10, 15, 25, 25]
        random.shuffle(counts)
        # randomly pair counts with each kind of dot
        objectives = zip(
            [BasicDot(1), BasicDot(2),
             BasicDot(4), BasicDot(3)], counts)

        self._objectives = ObjectiveManager(list(objectives))
        self._info.set_objectives(list(objectives))

        self._dead_cells = {(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4),
                            (4, 2), (4, 3), (4, 4), (0, 7), (1, 7), (6, 7),
                            (7, 7)}
        self._companion = EskimoCompanion()
        self._game = CompanionGame(
            {
                TurtleDot: 1,
                CompanionDot: 4,
                BasicDot: 11
            },
            objectives=self._objectives,
            companion=self._companion,
            kinds=(1, 2, 3, 4),
            size=(8, 8),
            dead_cells=self._dead_cells)

        # Grid View
        self._grid_view = GridView(self._master,
                                   size=self._game.grid.size(),
                                   image_manager=self._image_manager)
        self._grid_view.pack()
        self._grid_view.draw(self._game.grid)
        self.draw_grid_borders()

        # Events
        self.bind_events()

        # Set initial score again to trigger view update automatically
        self._refresh_status()

        self.read_score()
        # mixer not working under Ubuntu
        # pygame.mixer.Sound('Sounds/start.wav').play()

    def draw_grid_borders(self):
        """Draws borders around the game grid"""

        borders = list(self._game.grid.get_borders())

        # this is a hack that won't work well for multiple separate clusters
        outside = max(borders, key=lambda border: len(set(border)))

        for border in borders:
            self._grid_view.draw_border(border, fill=border != outside)

    def bind_events(self):
        """Binds relevant events"""
        self._grid_view.on('start_connection', self._drag)
        self._grid_view.on('move_connection', self._drag)
        self._grid_view.on('end_connection', self._drop)

        self._game.on('reset', self._refresh_status)
        self._game.on('complete', self._drop_complete)

        self._game.on('connect', self._connect)
        self._game.on('undo', self._undo)

    def _animation_step(self, step_name):
        """Runs for each step of an animation

        Parameters:
            step_name (str): The name (type) of the step
        """
        self._refresh_status()
        self.draw_grid()

    def animate(self, steps, callback=lambda: None):
        """Animates some steps (i.e. from selecting some dots, activating companion, etc.
        
        Parameters:
            steps (generator): Generator which yields step_name (str) for each step in the animation
        """
        if steps is None:
            steps = (None for _ in range(1))

        animation = create_animation(self._master,
                                     steps,
                                     delays=ANIMATION_DELAYS,
                                     delay=DEFAULT_ANIMATION_DELAY,
                                     step=self._animation_step,
                                     callback=callback)
        animation()

    def _drop(self, position):  # pylint: disable=unused-argument
        """Handles the dropping of the dragged connection

        Parameters:
            position (tuple<int, int>): The position where the connection was
                                        dropped
        """
        if not self._playing:
            return

        if self._game.is_resolving():
            return

        self._grid_view.clear_dragged_connections()
        self._grid_view.clear_connections()

        self.animate(self._game.drop())

        # drop_sound = pygame.mixer.Sound('Sounds/drop.wav')
        # drop_sound.play()

    def _connect(self, start, end):
        """Draws a connection from the start point to the end point

        Parameters:
            start (tuple<int, int>): The position of the starting dot
            end (tuple<int, int>): The position of the ending dot
        """
        if self._game.is_resolving():
            return
        if not self._playing:
            return
        self._grid_view.draw_connection(
            start, end, self._game.grid[start].get_dot().get_kind())

    def _undo(self, positions):
        """Removes all the given dot connections from the grid view

        Parameters:
            positions (list<tuple<int, int>>): The dot connects to remove
        """
        for _ in positions:
            self._grid_view.undo_connection()

    def _drag(self, position):
        """Attempts to connect to the given position, otherwise draws a dragged
        line from the start

        Parameters:
            position (tuple<int, int>): The position to drag to
        """
        if self._game.is_resolving():
            return
        if not self._playing:
            return

        tile_position = self._grid_view.xy_to_rc(position)

        if tile_position is not None:
            cell = self._game.grid[tile_position]
            dot = cell.get_dot()

            if dot and self._game.connect(tile_position):
                self._grid_view.clear_dragged_connections()
                return

        kind = self._game.get_connection_kind()

        if not len(self._game.get_connection_path()):
            return

        start = self._game.get_connection_path()[-1]

        if start:
            self._grid_view.draw_dragged_connection(start, position, kind)

    def draw_grid(self):
        """Draws the grid"""
        self._grid_view.draw(self._game.grid)

    def with_companion(self):
        """Sets the companion for the new game with a companion and resets"""
        self._companion = EskimoCompanion()
        self.reset()

    def without_companion(self):
        """Cancels the companion for the new game witout a companion and resets"""
        self._companion = None
        self.reset()

    def reset(self):
        """Resets the game"""
        self._playing = True
        self._steps = 0
        self._game.reset()
        self._objectives.reset()
        self._refresh_status()
        self._info.reset_bar()
        # start_sound = pygame.mixer.Sound('Sounds/start.wav')
        # start_sound.play()
        if self._over is not None:
            self._info.after_cancel(self._animation)

        if self._companion:
            img = tk.PhotoImage(file='images/companions/eskimo.png')
            self._info.set_companion(img)
            self._game = CompanionGame(
                {
                    TurtleDot: 1,
                    CompanionDot: 4,
                    BasicDot: 11
                },
                objectives=self._objectives,
                companion=self._companion,
                kinds=(1, 2, 3, 4),
                size=(8, 8),
                dead_cells=self._dead_cells)
        if not self._companion:
            img = tk.PhotoImage(file='images/companions/useless.gif')
            self._info.set_companion(img)
            self._game = DotGame({BasicDot: 1},
                                 objectives=self._objectives,
                                 kinds=(1, 2, 3, 4),
                                 size=(8, 8),
                                 dead_cells=self._dead_cells)
            self._info.set_bar(0)

        self._grid_view.draw(self._game.grid)
        self.bind_events()

    def exit(self):
        """Checks if the user wants to exit and closes the application if so"""
        reply = messagebox.askokcancel('Verify exit', 'Really quit?')
        if reply:
            self._master.destroy()

    def change_win(self):
        """Shows an animation when the user wins"""
        self._over = not self._over
        if self._over:
            img = tk.PhotoImage(file='images/win1.gif')
            self._info.set_companion(img)
        else:
            img = tk.PhotoImage(file='images/win2.gif')
            self._info.set_companion(img)
        self._animation = self._info.after(800, self.change_win)

    def change_lose(self):
        """Shows an animation when the user loses"""
        self._over = not self._over
        if self._over:
            img = tk.PhotoImage(file='images/gameover1.png')
            self._info.set_companion(img)
        else:
            img = tk.PhotoImage(file='images/gameover2.png')
            self._info.set_companion(img)
        self._animation = self._info.after(800, self.change_lose)

    def check_game_over(self):
        """Checks whether the game is over and shows an appropriate message box if so"""
        state = self._game.get_game_state()

        if state == self._game.GameState.WON:
            # Shows the animation and plays sound effects
            self._over = True
            self.change_win()
            # pygame.mixer.Sound('Sounds/success.wav').play()
            # Saves the best score
            if self._player == 'None':
                self.save_score()
            elif self._game.get_score() > self.read_score():
                self.save_score()
            # Shows the user's highest ranking and the top 3 players
            ranking = sorted(self._scores,
                             key=self._scores.__getitem__,
                             reverse=True)
            position = ranking.index(self._player) + 1
            top = ''
            for name in ranking[:3]:
                top += "{0}--{1}   ".format(name, self._scores.get(name))
            messagebox.showinfo(
                "Game Over!",
                "You won!!!\nYour best socre: {0}. Your highest ranking: {1}.\n Top 3: "
                .format(self.read_score(), position) + top)
            self._playing = False

        elif state == self._game.GameState.LOST:
            self._over = True
            self.change_lose()
            # pygame.mixer.Sound('Sounds/lose.wav').play()
            messagebox.showinfo(
                "Game Over!",
                f"You didn't reach the objective(s) in time. You connected {self._game.get_score()} points"
            )
            self._playing = False

    def read_score(self):
        """(int) Returns the user's former best score.
        Returns None if the user plays the game for the first time

        Precondition:
            The user's name does not consist ','
        """
        f = open('score.txt', 'r')
        lines = f.readlines()
        for l in lines:
            if l:
                s = l.split(',')
                self._scores[s[0]] = int(s[1])
        score = self._scores.get(self._player)
        f.close
        return score

    def save_score(self):
        """Saves the user's score"""
        self._scores[self._player] = self._game.get_score()
        result = ''
        for n in self._scores:
            line = str(n) + "," + str(self._scores[n]) + "\n"
            result += line
        f = open('score.txt', 'w')
        f.write(result)
        f.close

    def _drop_complete(self):
        """Handles the end of a drop animation"""
        if not self._companion:
            self._steps += 1
            step = self._steps % 6
            if step == 0:
                self._info.reset_bar()
            self._info.set_bar(step)

        if self._companion:
            charge = self._game.companion.get_charge()
            for i in range(charge):
                self._info.set_bar(i)
            if self._game.companion.is_fully_charged():
                # pygame.mixer.Sound('Sounds/companion.wav').play()
                steps = self._game.companion.activate(self._game)
                self._grid_view.draw(self._game.grid)
                self._game.companion.reset()
                self._info.reset_bar()
                self.check_game_over()
                return self.animate(steps)

        self._grid_view.draw(self._game.grid)
        self.check_game_over()

    def _refresh_status(self):
        """Handles change in objectives, remaining moves, and score."""
        status = self._objectives.get_status()
        self._info.set_objectives(status)

        moves = self._game.get_moves()
        self._info.set_moves(moves)

        score = self._game.get_score()
        self._info.set_score(score)
Пример #9
0
    def __init__(self, master):
        """Constructor

        Parameters:
            master (tk.Tk|tk.Frame): The parent widget
        """
        self._master = master
        self._master.title('Dot')
        self._playing = True
        self._image_manager = ImageManager('images/dots/', loader=load_image)

        # initialize pygame
        pygame.init()

        # load background music
        pygame.mixer.music.load('bgm2.ogg')
        pygame.mixer.music.play(-1, 0.0)
        pygame.mixer.music.set_volume(0.3)

        # create an instance of InfoPanel
        self.info_panel = InfoPanel(master)
        self.info_panel.pack()

        # create an instance of IntervalBar
        self.interval_bar = IntervalBar(master)
        self.interval_bar.pack()

        # create an instance of ActionBar
        self.action_bar = ActionBar(master)
        self.action_bar.pack(side=tk.BOTTOM)

        # add command to two button
        self.action_bar.companion_charge().config(command=self.com_button)
        self.action_bar.colour_remove().config(command=self.colour_activate)

        # File menu
        menubar = tk.Menu(self._master)
        # tell master what it's menu is
        self._master.config(menu=menubar)
        filemenu = tk.Menu(menubar)
        menubar.add_cascade(label="File", menu=filemenu)
        filemenu.add_command(label="New Game(companion)",
                             command=self.reset_with_com)
        filemenu.add_command(label="New Game(no-companion)",
                             command=self.reset_without_com)
        filemenu.add_command(label="Exit", command=self.exit)

        # Game
        counts = [10, 15, 25, 25]
        random.shuffle(counts)
        # randomly pair counts with each kind of dot
        objectives = zip(
            [BasicDot(1), BasicDot(2),
             BasicDot(4), BasicDot(3)], counts)
        self._objectives = ObjectiveManager(list(objectives))
        # show the objectives
        self._obj = self.info_panel.set_object()
        self._obj.draw(self._objectives.get_status())

        # Game
        dead_cells = {(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2),
                      (4, 3), (4, 4), (0, 7), (1, 7), (6, 7), (7, 7)}
        self._game = CompanionGame({
            BasicDot: 1,
            CompanionDot: 1
        },
                                   companion=EskimoCompanion(),
                                   objectives=self._objectives,
                                   kinds=(1, 2, 3, 4),
                                   size=(8, 8),
                                   dead_cells=dead_cells)

        # show the remaining moves
        moves = self.info_panel.remain_moves()
        moves.config(text=str(self._game.get_moves()))
        # show the scores
        scores = self.info_panel.set_scores()
        scores.config(text=str(self._game.get_score()))

        # control the reset type(with/without companion)
        self._play_with_com = True

        # Grid View
        self._grid_view = GridView(master,
                                   size=self._game.grid.size(),
                                   image_manager=self._image_manager)
        self._grid_view.pack()
        self._grid_view.draw(self._game.grid)
        self.draw_grid_borders()

        # Events
        self.bind_events()

        # Set initial score again to trigger view update automatically
        self._refresh_status()
Пример #10
0
class DotsApp:
    """Top level GUI class for simple Dots & Co game"""
    def __init__(self, master):
        """Constructor

        Parameters:
            master (tk.Tk|tk.Frame): The parent widget
        """
        self._master = master
        self._master.title('Dot')
        self._playing = True
        self._image_manager = ImageManager('images/dots/', loader=load_image)

        # initialize pygame
        pygame.init()

        # load background music
        pygame.mixer.music.load('bgm2.ogg')
        pygame.mixer.music.play(-1, 0.0)
        pygame.mixer.music.set_volume(0.3)

        # create an instance of InfoPanel
        self.info_panel = InfoPanel(master)
        self.info_panel.pack()

        # create an instance of IntervalBar
        self.interval_bar = IntervalBar(master)
        self.interval_bar.pack()

        # create an instance of ActionBar
        self.action_bar = ActionBar(master)
        self.action_bar.pack(side=tk.BOTTOM)

        # add command to two button
        self.action_bar.companion_charge().config(command=self.com_button)
        self.action_bar.colour_remove().config(command=self.colour_activate)

        # File menu
        menubar = tk.Menu(self._master)
        # tell master what it's menu is
        self._master.config(menu=menubar)
        filemenu = tk.Menu(menubar)
        menubar.add_cascade(label="File", menu=filemenu)
        filemenu.add_command(label="New Game(companion)",
                             command=self.reset_with_com)
        filemenu.add_command(label="New Game(no-companion)",
                             command=self.reset_without_com)
        filemenu.add_command(label="Exit", command=self.exit)

        # Game
        counts = [10, 15, 25, 25]
        random.shuffle(counts)
        # randomly pair counts with each kind of dot
        objectives = zip(
            [BasicDot(1), BasicDot(2),
             BasicDot(4), BasicDot(3)], counts)
        self._objectives = ObjectiveManager(list(objectives))
        # show the objectives
        self._obj = self.info_panel.set_object()
        self._obj.draw(self._objectives.get_status())

        # Game
        dead_cells = {(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2),
                      (4, 3), (4, 4), (0, 7), (1, 7), (6, 7), (7, 7)}
        self._game = CompanionGame({
            BasicDot: 1,
            CompanionDot: 1
        },
                                   companion=EskimoCompanion(),
                                   objectives=self._objectives,
                                   kinds=(1, 2, 3, 4),
                                   size=(8, 8),
                                   dead_cells=dead_cells)

        # show the remaining moves
        moves = self.info_panel.remain_moves()
        moves.config(text=str(self._game.get_moves()))
        # show the scores
        scores = self.info_panel.set_scores()
        scores.config(text=str(self._game.get_score()))

        # control the reset type(with/without companion)
        self._play_with_com = True

        # Grid View
        self._grid_view = GridView(master,
                                   size=self._game.grid.size(),
                                   image_manager=self._image_manager)
        self._grid_view.pack()
        self._grid_view.draw(self._game.grid)
        self.draw_grid_borders()

        # Events
        self.bind_events()

        # Set initial score again to trigger view update automatically
        self._refresh_status()

    def draw_grid_borders(self):
        """Draws borders around the game grid"""

        borders = list(self._game.grid.get_borders())

        # this is a hack that won't work well for multiple separate clusters
        outside = max(borders, key=lambda border: len(set(border)))

        for border in borders:
            self._grid_view.draw_border(border, fill=border != outside)

    def bind_events(self):
        """Binds relevant events"""
        self._grid_view.on('start_connection', self._drag)
        self._grid_view.on('move_connection', self._drag)
        self._grid_view.on('end_connection', self._drop)

        self._game.on('reset', self._refresh_status)
        self._game.on('complete', self._drop_complete)

        self._game.on('connect', self._connect)
        self._game.on('undo', self._undo)

    def _animation_step(self, step_name):
        """Runs for each step of an animation
        
        Parameters:
            step_name (str): The name (type) of the step    
        """

        # add sound effect
        sound = pygame.mixer.Sound('boble1.wav')
        sound.play()

        print(step_name)
        self._refresh_status()
        self.draw_grid()

    def animate(self, steps, callback=lambda: None):
        """Animates some steps (i.e. from selecting some dots, activating companion, etc.
        
        Parameters:
            steps (generator): Generator which yields step_name (str) for each step in the animation
        """

        if steps is None:
            steps = (None for _ in range(1))

        animation = create_animation(self._master,
                                     steps,
                                     delays=ANIMATION_DELAYS,
                                     delay=DEFAULT_ANIMATION_DELAY,
                                     step=self._animation_step,
                                     callback=callback)
        animation()

    def _drop(self, position):  # pylint: disable=unused-argument
        """Handles the dropping of the dragged connection

        Parameters:
            position (tuple<int, int>): The position where the connection was
                                        dropped
        """
        if not self._playing:
            return

        if self._game.is_resolving():
            return

        self._grid_view.clear_dragged_connections()
        self._grid_view.clear_connections()

        self.animate(self._game.drop())

    def _connect(self, start, end):
        """Draws a connection from the start point to the end point

        Parameters:
            start (tuple<int, int>): The position of the starting dot
            end (tuple<int, int>): The position of the ending dot
        """

        # add sound effect when two connects
        sound = pygame.mixer.Sound('cartoon-boing.ogg')
        sound.play()

        if self._game.is_resolving():
            return
        if not self._playing:
            return
        self._grid_view.draw_connection(
            start, end, self._game.grid[start].get_dot().get_kind())

    def _undo(self, positions):
        """Removes all the given dot connections from the grid view

        Parameters:
            positions (list<tuple<int, int>>): The dot connects to remove
        """
        for _ in positions:
            self._grid_view.undo_connection()

    def _drag(self, position):
        """Attempts to connect to the given position, otherwise draws a dragged
        line from the start

        Parameters:
            position (tuple<int, int>): The position to drag to
        """

        if self._game.is_resolving():
            return
        if not self._playing:
            return

        tile_position = self._grid_view.xy_to_rc(position)

        if tile_position is not None:
            cell = self._game.grid[tile_position]
            dot = cell.get_dot()

            if dot and self._game.connect(tile_position):
                self._grid_view.clear_dragged_connections()
                return

        kind = self._game.get_connection_kind()

        if not len(self._game.get_connection_path()):
            return

        start = self._game.get_connection_path()[-1]

        if start:
            self._grid_view.draw_dragged_connection(start, position, kind)

    @staticmethod
    def remove(*_):
        """Deprecated in 1.1.0"""
        raise DeprecationWarning("Deprecated in 1.1.0")

    def draw_grid(self):
        """Draws the grid"""
        self._grid_view.draw(self._game.grid)

    def reset_without_com(self):
        """Resets the game"""

        # initialize pygame
        pygame.init()
        # load background music
        pygame.mixer.music.load('bgm2.ogg')
        pygame.mixer.music.play(-1, 0.0)
        pygame.mixer.music.set_volume(0.3)

        counts = [10, 15, 25, 25]
        random.shuffle(counts)
        # randomly pair counts with each kind of dot
        objectives = zip(
            [BasicDot(1), BasicDot(2),
             BasicDot(4), BasicDot(3)], counts)
        self._objectives = ObjectiveManager(list(objectives))

        dead_cells = {(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2),
                      (4, 3), (4, 4), (0, 7), (1, 7), (6, 7), (7, 7)}
        self._game = DotGame({BasicDot: 1},
                             objectives=self._objectives,
                             kinds=(1, 2, 3, 4),
                             size=(8, 8),
                             dead_cells=dead_cells)

        # reset the game(score, move)
        self._game.reset()
        # reset the score
        scores = self.info_panel.set_scores()
        scores.config(text=str(self._game.get_score()))
        # reset the move
        moves = self.info_panel.remain_moves()
        moves.config(text=str(self._game.get_moves()))

        # reset the objectives
        self._obj.draw(self._objectives.get_status())

        # reset the grid
        self.draw_grid()

        # reset the interval bar
        self.interval_bar.progress_bar(0)
        self.interval_bar.com_charge_bar_reset()

        # player choose to play without companion
        self._play_with_com = False
        # reset the companion charge button as disable
        self.action_bar.companion_charge().config(state='disable')
        # reset the color remover button
        self.action_bar.colour_remove().config(state='normal')

    def reset_with_com(self):
        # initialize pygame
        pygame.init()
        # load background music
        pygame.mixer.music.load('bgm2.ogg')
        pygame.mixer.music.play(-1, 0.0)
        pygame.mixer.music.set_volume(0.3)

        counts = [10, 15, 25, 25]
        random.shuffle(counts)
        # randomly pair counts with each kind of dot
        objectives = zip(
            [BasicDot(1), BasicDot(2),
             BasicDot(4), BasicDot(3)], counts)
        self._objectives = ObjectiveManager(list(objectives))

        # reset the objectives
        self._obj.draw(self._objectives.get_status())

        dead_cells = {(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2),
                      (4, 3), (4, 4), (0, 7), (1, 7), (6, 7), (7, 7)}
        self._game = CompanionGame({
            BasicDot: 1,
            CompanionDot: 1
        },
                                   companion=EskimoCompanion(),
                                   objectives=self._objectives,
                                   kinds=(1, 2, 3, 4),
                                   size=(8, 8),
                                   dead_cells=dead_cells)
        # reset the game(score, move)
        self._game.reset()

        # reset the grid
        self.draw_grid()

        # reset the score
        scores = self.info_panel.set_scores()
        scores.config(text=str(self._game.get_score()))
        # reset the move
        moves = self.info_panel.remain_moves()
        moves.config(text=str(self._game.get_moves()))

        # reset the interval bar
        self.interval_bar.progress_bar(0)
        self._game.companion.reset()
        # reset the companion bar
        self.interval_bar.com_charge_bar_reset()
        # reset the companion charge button
        self.action_bar.companion_charge().config(state='normal')
        # reset the color remover button
        self.action_bar.colour_remove().config(state='normal')

    def check_game_over(self):
        """Checks whether the game is over and shows an appropriate message box if so"""
        state = self._game.get_game_state()

        if state == self._game.GameState.WON:
            # load the gamewin music
            pygame.mixer.music.load('breaking slient.ogg')
            pygame.mixer.music.play()
            showinfo("Game Over!", "You won!!!")
            self._playing = False
        elif state == self._game.GameState.LOST:
            # load the gameover music
            pygame.mixer.music.load('gameOver.wav')
            pygame.mixer.music.play()
            showinfo(
                "Game Over!",
                f"You didn't reach the objective(s) in time. You connected {self._game.get_score()} points"
            )

            self._playing = False

    def _drop_complete(self):
        """Handles the end of a drop animation"""

        # Need to check whether the game is over
        # check whether the game is over
        self.check_game_over()

    def _refresh_status(self):
        """Handles change in score"""

        # Normally, this should raise the following error:
        # raise NotImplementedError()
        # But so that the game can work prior to this method being implemented,
        # we'll just print some information
        # Sometimes I believe Python ignores all my comments :(

        # show the remaining objectives using get_status() function
        self._obj.draw(self._objectives.get_status())

        # show the interval bar(max==6)
        bar_num = (20 - self._game.get_moves()) % 6
        self.interval_bar.progress_bar(bar_num)

        # show the current score
        scores = self.info_panel.set_scores()
        scores.config(text=str(self._game.get_score()))

        # show the remaining moves
        moves = self.info_panel.remain_moves()
        moves.config(text=str(self._game.get_moves()))

        # if player choose to play without companion, then undo the following part
        if self._play_with_com == True:
            charge_num = self._game.companion.get_charge()
            for i in range(charge_num):
                self.interval_bar.com_charge_bar(i)
            # reset the companion when fully-charged
            if self._game.companion.is_fully_charged():
                self._game.companion.reset()
                steps = self._game.companion.activate(self._game)
                self._refresh_status()
                return self.animate(steps)

        score = self._game.get_score()
        print("Score is now {}.".format(score))

    # exit function
    def exit(self):
        """Ask exit or not when click exit button"""

        ans = messagebox.askokcancel('Verify exit', 'Really exit?')
        if ans:
            # close the game window
            self._master.destroy()

    def com_button(self):
        """Immediately activates the companion"""

        # set the button disabled after first use
        self.action_bar.companion_charge().config(state='disable')
        # charge the companion dot for 6 times to activate the companion
        self._game.companion.charge(6)
        charge_num = self._game.companion.get_charge()
        # change the interval bar
        for i in range(charge_num):
            self.interval_bar.com_charge_bar(i)
        self._game.companion.reset()
        steps = self._game.companion.activate(self._game)
        self._refresh_status()
        return self.animate(steps)

    def colour_activate(self):
        """Immediately activates (& removes) all dots of a random kind (colour)"""

        # set the button disabled after first use
        self.action_bar.colour_remove().config(state='disable')
        # generate a random kind
        kind = random.randint(1, 4)
        dead_cells = {(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2),
                      (4, 3), (4, 4), (0, 7), (1, 7), (6, 7), (7, 7)}
        # create a set to save all the positions to be activated
        to_activate = set()
        for i in range(0, 8):
            for j in range(0, 8):
                # generate a random position
                position = (i, j)
                # the position is not in dead_cells and its kind is the random kind
                if position not in dead_cells and self._game.grid[
                        position].get_dot().get_kind() == kind:
                    # add the position into set
                    to_activate.add(position)
        # activate all the position in set
        steps = self._game.activate_all(to_activate)
        return self.animate(steps)
Пример #11
0
    def __init__(self, master):
        """Constructor

        Parameters:
            master (tk.Tk|tk.Frame): The parent widget
        """
        self._master = master
        self._playing = True
        self._image_manager = ImageManager('images/dots/', loader=load_image)

        # Game
        self._size = (8, 8)
        counts = [20, 15, 20, 25]
        random.shuffle(counts)
        # Randomly pair counts with each kind of dot
        objectives = zip(
            [BasicDot(1), BasicDot(2),
             BasicDot(8), BasicDot(3)], counts)

        self._objectives = ObjectiveManager(objectives)

        # Define companions
        self._companions = {
            'penguin': PenguinCompanion(max_charge=6),
            'eskimo': EskimoCompanion(max_charge=6, dot_count=4),
            'buffalo': BuffaloCompanion(max_charge=5, dot_count=5),
            'captain': CaptainCompanion(max_charge=5, dot_count=4),
            'aristotle': AristotleCompanion(max_charge=4, dot_count=2)
        }

        # Define dead cell maps
        dead_cells = {
            'endless': {},
            'companion': {(2, 1), (2, 2), (3, 1), (3, 2), (5, 4), (5, 5),
                          (5, 6)},
        }

        # Define game types
        self._games = {
            'endless':
            EndlessGame({BasicDot: 1},
                        size=self._size,
                        dead_cells=dead_cells['endless'],
                        kinds=(1, 2, 3, 4, 8)),
            'companion':
            AdvancedGame(
                {
                    BasicDot: 1,
                    CompanionDot: COMPANION_DOT_RATE
                },
                size=self._size,
                dead_cells=dead_cells['companion'],
                kinds=(1, 2, 3, 8),
                companion=random.choice(list(self._companions.values())),
                objectives=self._objectives,
                moves=20,
            )
        }

        # Choose a initial game to play
        self._game_mode = 'companion'
        self._game = self._games[self._game_mode]

        # Info Panel
        self._info_panel = InfoPanel(master, image_manager=self._image_manager)
        self._info_panel.pack(expand=True, fill=tk.BOTH)
        self._info_panel.set_companion(self._game.companion.get_name())
        self._info_panel.set_interval(self._game.companion.get_max_charge(),
                                      self._game.companion.get_charge())

        # Grid View
        self._grid_view = AdvancedGridView(master,
                                           size=self._game.grid.size(),
                                           image_manager=self._image_manager)
        self._grid_view.pack()
        self.draw_grid()
        self.draw_grid_borders()

        # Action Panel
        self._action_bar = ActionBar(master)
        self._action_bar.pack()

        # Menu & Title
        self._main_menu = MainMenu(master)
        self._master.config(menu=self._main_menu)

        # Events
        self.bind_events()
        for game in self._games.values():
            self.bind_game_events(game)

        # Set initial score again to trigger view update automatically
        self._refresh_status()

        # Set windows position to center
        self._update_title()
        self._move_to_center()
Пример #12
0
class DotsApp:
    """Top level GUI class for simple Dots & Co game"""
    def __init__(self, master, infoPanel, intervalBar):
        """Constructor

        Parameters:
            master (tk.Tk|tk.Frame): The parent widget
        """
        self._master = master
        self._infoPanel = infoPanel
        self._infoPanel.init_window(master, self)
        self._intervalBar = intervalBar

        self._playing = True

        self._image_manager = ImageManager('images/dots/', loader=load_image)

        # Game
        counts = [10, 15, 25, 25]
        random.shuffle(counts)
        # randomly pair counts with each kind of dot
        objectives = zip(
            [BasicDot(1), BasicDot(2),
             BasicDot(4), BasicDot(3)], counts)

        self._objectives = ObjectiveManager(objectives)
        self._infoPanel.set_objectives(
            [self._objectives.get_status()[i][1] for i in range(4)])

        # Game
        dead_cells = {(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2),
                      (4, 3), (4, 4), (0, 7), (1, 7), (6, 7), (7, 7)}
        self._game = DotGame({ButterflyDot: 1},
                             objectives=self._objectives,
                             kinds=(1, 2, 3, 4),
                             size=(8, 8),
                             dead_cells=dead_cells)

        # The following code may be useful when you are implementing task 2:
        for i in range(0, 4):
            for j in range(0, 2):
                position = i, j
                self._game.grid[position].set_dot(ButterflyDot(3))
        self._game.grid[(7, 3)].set_dot(ButterflyDot(1))

        # Grid View
        self._grid_view = GridView(master,
                                   size=self._game.grid.size(),
                                   image_manager=self._image_manager)
        self._grid_view.pack()
        self._grid_view.draw(self._game.grid)
        self.draw_grid_borders()

        # Events
        self.bind_events()

        # Set initial score again to trigger view update automatically
        self._refresh_status()

    def draw_grid_borders(self):
        """Draws borders around the game grid"""

        borders = list(self._game.grid.get_borders())

        # this is a hack that won't work well for multiple separate clusters
        outside = max(borders, key=lambda border: len(set(border)))

        for border in borders:
            self._grid_view.draw_border(border, fill=border != outside)

    def bind_events(self):
        """Binds relevant events"""
        self._grid_view.on('start_connection', self._drag)
        self._grid_view.on('move_connection', self._drag)
        self._grid_view.on('end_connection', self._drop)

        self._game.on('reset', self._refresh_status)
        self._game.on('complete', self._drop_complete)

        self._game.on('connect', self._connect)
        self._game.on('undo', self._undo)

    def _animation_step(self, step_name):
        """Runs for each step of an animation
        
        Parameters:
            step_name (str): The name (type) of the step    
        """
        # print(step_name)
        self._refresh_status()
        self.draw_grid()

    def animate(self, steps, callback=lambda: None):
        """Animates some steps (i.e. from selecting some dots, activating companion, etc.
        
        Parameters:
            steps (generator): Generator which yields step_name (str) for each step in the animation
        """

        if steps is None:
            steps = (None for _ in range(1))

        animation = create_animation(self._master,
                                     steps,
                                     delays=ANIMATION_DELAYS,
                                     delay=DEFAULT_ANIMATION_DELAY,
                                     step=self._animation_step,
                                     callback=callback)
        animation()

    def _drop(self, position):  # pylint: disable=unused-argument
        """Handles the dropping of the dragged connection

        Parameters:
            position (tuple<int, int>): The position where the connection was
                                        dropped
        """
        if not self._playing:
            return

        if self._game.is_resolving():
            return

        self._grid_view.clear_dragged_connections()
        self._grid_view.clear_connections()

        self.animate(self._game.drop())

    def _connect(self, start, end):
        """Draws a connection from the start point to the end point

        Parameters:
            start (tuple<int, int>): The position of the starting dot
            end (tuple<int, int>): The position of the ending dot
        """

        if self._game.is_resolving():
            return
        if not self._playing:
            return
        self._grid_view.draw_connection(
            start, end, self._game.grid[start].get_dot().get_kind())

    def _undo(self, positions):
        """Removes all the given dot connections from the grid view

        Parameters:
            positions (list<tuple<int, int>>): The dot connects to remove
        """
        for _ in positions:
            self._grid_view.undo_connection()

    def _drag(self, position):
        """Attempts to connect to the given position, otherwise draws a dragged
        line from the start

        Parameters:
            position (tuple<int, int>): The position to drag to
        """

        if self._game.is_resolving():
            return
        if not self._playing:
            return

        tile_position = self._grid_view.xy_to_rc(position)

        if tile_position is not None:
            cell = self._game.grid[tile_position]
            dot = cell.get_dot()

            if dot and self._game.connect(tile_position):
                self._grid_view.clear_dragged_connections()
                return

        kind = self._game.get_connection_kind()

        if not len(self._game.get_connection_path()):
            return

        start = self._game.get_connection_path()[-1]

        if start:
            self._grid_view.draw_dragged_connection(start, position, kind)

    @staticmethod
    def remove(*_):
        """Deprecated in 1.1.0"""
        raise DeprecationWarning("Deprecated in 1.1.0")

    def draw_grid(self):
        """Draws the grid"""
        self._grid_view.draw(self._game.grid)

    def reset(self):
        """Resets the game"""
        print("hello,start reset ")
        self._game.reset()
        self._infoPanel.set_score(0)
        self._infoPanel.set_remaining_moves(20)
        self._infoPanel.set_objectives(
            [self._objectives.get_status()[i][1] for i in range(4)])
        self._playing = True

    def check_game_over(self):
        """Checks whether the game is over and shows an appropriate message box if so"""
        state = self._game.get_game_state()

        if state == self._game.GameState.WON:
            showinfo("Game Over!", "You won!!!")
            self._playing = False
        elif state == self._game.GameState.LOST:
            showinfo(
                "Game Over!",
                f"You didn't reach the objective(s) in time. You connected {self._game.get_score()} points"
            )
            self._playing = False

    def _drop_complete(self):
        """Handles the end of a drop animation"""
        self.check_game_over()
        self._infoPanel.set_objectives(
            ([self._objectives.get_status()[i][1] for i in range(4)]))
        remaining_steps = self._game.get_moves()
        if remaining_steps > 0:
            self._intervalBar.next_step()
            self._infoPanel.set_remaining_moves(remaining_steps)
            # Useful for when implementing a companion
            # if self._game.companion.is_fully_charged():
            #     self._game.companion.reset()
            #
            #     self._intervalBar.next_step()
            #     self._refresh_status()
            #
            #     return self.animate(steps)
            # Need to check whether the game is over
            # raise NotImplementedError()  # no mercy for stooges

    def _refresh_status(self):
        """Handles change in score"""

        # Normally, this should raise the following error:
        # raise NotImplementedError()
        # But so that the game can work prior to this method being implemented,
        # we'll just print some information
        # Sometimes I believe Python ignores all my comments :(
        score = self._game.get_score()
        self._infoPanel.set_score(score)