Пример #1
0
class GameScreen(Screen):
    """
    Main screen of the program.
    Contains the game's grid, a frame to show flag numbers, a frame
    to show the timer of the match, a frame showing the match's state,
    one button to start over/play again and one to return to the SetUpScreen.
    """
    def __init__(self, window):
        super().__init__(window)

        #gridsize:fontsize for button based on grid's sizes
        self.gsize_fsize = {
            4: 100,
            5: 80,
            6: 65,
            7: 55,
            8: 45,
            9: 40,
            10: 35,
            11: 30,
            12: 25,
            13: 21,
            14: 18,
            15: 15,
            16: 12,
            17: 10,
            18: 8,
            19: 7,
            20: 6
        }
        #additional left pady for the col=0 buttons based on the grid's width
        #works fine with squared grids, however fails with rectangular ones
        self.w_padyleft = {
            4: 50,
            5: 40,
            6: 35,
            7: 30,
            8: 25,
            9: 15,
            10: 0,
            11: 0,
            12: 0,
            13: 0,
            14: 0,
            15: 0,
            16: 0,
            17: 0,
            18: 0,
            19: 0,
            20: 0
        }

        self.frm_grid = tk.Frame(self.master,
                                 width=self.master_w * 0.8,
                                 height=self.master_h,
                                 bg=self.master["bg"])

        self.frm_info = tk.Frame(self.master,
                                 width=self.master_w * 0.2,
                                 height=self.master_h,
                                 bg=self.master["bg"])

        self.frm_flag = tk.Frame(self.frm_info,
                                 width=self.master_w * 0.2,
                                 height=self.master_h * 0.2,
                                 bg=self.master["bg"])
        self.lab_flag = tk.Label(self.frm_flag,
                                 width=2,
                                 height=1,
                                 font=("Ubuntu Mono", 50),
                                 text="\u2691",
                                 bg=self.master["bg"],
                                 fg="black")
        self.lab_n_flags = tk.Label(self.frm_flag,
                                    width=7,
                                    height=1,
                                    font=("Ubuntu Mono", 40),
                                    text="000/000",
                                    bg=self.master["bg"])

        self.frm_clock = tk.Frame(self.frm_info,
                                  width=self.master_w * 0.2,
                                  height=self.master_h * 0.2,
                                  bg=self.master["bg"])
        self.lab_clock = tk.Label(self.frm_clock,
                                  width=2,
                                  height=1,
                                  font=("Ubuntu Mono", 55),
                                  text="\u231a",
                                  bg=self.master["bg"],
                                  fg="black")
        self.lab_time = tk.Label(self.frm_clock,
                                 width=5,
                                 height=1,
                                 font=("Ubuntu Mono", 40),
                                 text="00:00",
                                 bg=self.master["bg"])

        self.frm_match = tk.Frame(self.frm_info,
                                  width=self.master_w * 0.2,
                                  height=self.master_h * 0.1,
                                  bg=self.master["bg"])
        self.lab_match = tk.Label(self.frm_match,
                                  width=8,
                                  height=1,
                                  font=("Ubuntu Mono", 33, "bold"),
                                  text="WAITING",
                                  bg=self.master["bg"],
                                  fg="#f1c232",
                                  highlightbackground="black",
                                  highlightthickness=3)

        self.frm_options = tk.Frame(self.frm_info,
                                    width=self.master_w * 0.2,
                                    height=self.master_h * 0.2,
                                    bg=self.master["bg"])
        self.but_overagain = tk.Button(self.frm_options,
                                       width=10,
                                       height=1,
                                       state="disabled",
                                       disabledforeground="black",
                                       font=("Ubuntu Mono", 26, "bold"),
                                       bg="#ffd966",
                                       highlightbackground="black",
                                       highlightthickness=2)
        self.but_cancel = tk.Button(self.frm_options,
                                    width=10,
                                    height=1,
                                    font=("Ubuntu Mono", 26, "bold"),
                                    text="Cancel",
                                    bg="#c63d3d",
                                    fg="black",
                                    highlightbackground="black",
                                    highlightthickness=2,
                                    activebackground="#c67474")

        self.frames = [self.frm_grid, self.frm_info]

    def overAgain(self):
        self.destroy()
        self.setNewGame(self.grid.getHeight(), self.grid.getWidth(),
                        self.grid.getNMines())
        self.show()

    def setNewGame(self, height, width, n_mines):
        self.grid = Grid(height, width, n_mines)
        self.time_start = 0  #serve as a control variable too

        self.lab_n_flags["text"] = "0/{}".format(n_mines)
        self.lab_time["text"] = "00:00"
        self.lab_match.config(fg="#f1c232", text="WAITING")
        self.but_overagain.config(bg="#ffd966",
                                  text="Start over",
                                  state="disabled",
                                  disabledforeground="black",
                                  command=self.overAgain)

    def show(self):
        self.frm_grid.pack(side="left")
        self.frm_info.pack(side="right")

        fsize = min(self.gsize_fsize[self.grid.getHeight()],
                    +self.gsize_fsize[self.grid.getWidth()])
        for r in range(self.grid.getHeight()):
            for c in range(self.grid.getWidth()):
                b = tk.Button(self.frm_grid,
                              width=2,
                              height=1,
                              font=("Ubuntu Mono", fsize),
                              fg="black",
                              text=" ",
                              activeforeground="black",
                              bg="#6fa8dc",
                              activebackground="#9fc5e8",
                              highlightbackground="black",
                              highlightthickness=2,
                              state="normal")
                padx = (0, 0)
                pady = (0, 0)
                if r == 0:
                    pady = (20, 0)
                elif r == self.grid.getHeight() - 1:
                    pady = (0, 20)
                if c == 0:
                    padx = (20 + self.w_padyleft[self.grid.getWidth()], 0)
                elif c == self.grid.getWidth() - 1:
                    padx = (0, 20)
                b.grid(row=r, column=c, padx=padx, pady=pady)
                b.bind("<Button-1>", self.start)
                b.bind("<Button-3>", self.flag)
                self.grid.getSquare(r, c).setButton(b)
        self.frm_flag.pack(pady=(0, 15))
        self.frm_clock.pack(pady=(15, 30))
        self.frm_match.pack(pady=(30, 30))
        self.frm_options.pack(pady=(30, 0))

        self.lab_flag.pack()
        self.lab_n_flags.pack()
        self.lab_clock.pack()
        self.lab_time.pack()
        self.lab_match.pack(padx=2, ipadx=4, ipady=5)
        self.but_overagain.pack(padx=4, pady=(0, 2), ipady=5)
        self.but_cancel.pack(padx=4, pady=(2, 0), ipady=5)

    def updateTime(self):
        time_now = int(monotonic())
        passed = time_now - self.time_start
        m = passed // 60
        s = passed % 60
        m = str(m) if m > 9 else "0" + str(m)
        s = str(s) if s > 9 else "0" + str(s)
        self.lab_time["text"] = m + ":" + s
        self.id_time = self.lab_time.after(995, self.updateTime)

    def start(self, event):
        self.is_start = True

        self.time_start = int(monotonic())  #start time
        self.updateTime()

        self.lab_match.config(fg="black", text="IN GAME")
        self.but_overagain.config(state="normal",
                                  fg="black",
                                  bg="#f1c232",
                                  activebackground="#ffd966")
        row = event.widget.grid_info()["row"]
        col = event.widget.grid_info()["column"]
        self.grid.setMines(row, col)

        for r in self.grid.getGrid():
            for sq in r:
                sq.getButton().bind("<Button-1>", self.play)

        self.play(event)

    def play(self, event):
        """
        Called when user clicks the mouse's left button.
        If the square clicked is a flag or is revealed, does nothing.
        If the square is a mine, the player loses, the grid becomes
        disabled and the timer stops.
        If the square is anything else, the game expands the grid from
        this square. Check if, after the expansion, the player wins.
        """

        row = event.widget.grid_info()["row"]
        col = event.widget.grid_info()["column"]

        if self.grid.getSquare(row, col).getState() == 1 \
           or self.grid.getSquare(row, col).getFlag() == "\u2691" \
           and not self.is_start:
            return  #nothing happens

        if self.grid.getSquare(row, col).getValue() == "\u2620":  #mine
            self.lab_time.after_cancel(self.id_time)
            self.lab_match.config(fg="#c63d3d", text="YOU LOSE")
            self.but_overagain.config(bg="#17c651",
                                      activebackground="#65c680",
                                      text="Play again")
            self.grid.showAll(lose=True)
            self.grid.getSquare(
                row, col).getButton().config(disabledforeground="red")
            return
        else:  #normal play
            self.grid.expandPosition(row, col)
            self.updateNFlaggedSquares()
            if self.hadVictory():  #victory
                self.lab_time.after_cancel(self.id_time)
                self.time_start = 0
                self.grid.showAll(win=True)
                self.updateNFlaggedSquares()
                self.lab_match.config(fg="#17c651", text="YOU WIN")
                self.but_overagain.config(bg="#17c651",
                                          activebackground="#65c680",
                                          text="Play again")

        if self.is_start:
            self.is_start = False

    def updateNFlaggedSquares(self):
        self.lab_n_flags["text"] = "{}/{}".format(
            self.grid.getNFlaggedSquares(), self.grid.getNMines())

    def flag(self, event):
        """
        Called when player clicks mouse's right button.
        The square must be unrevealed to be flagged.
        If square has no flag, it gets a flag and the number of flagged
        squares increases.
        If square has a flag, it gets a question mark, and the number of
        flagged squares decreases.
        If square has a question mark, it has its flag removed.
        """

        row = event.widget.grid_info()["row"]
        col = event.widget.grid_info()["column"]
        if self.grid.getSquare(row, col).getState() == 0:
            f = self.grid.getNFlaggedSquares()
            n = self.grid.getNMines()

            if self.grid.getSquare(row, col).getFlag() == "":
                if f < n:
                    self.grid.getSquare(row, col).setFlag("\u2691")
                    self.grid.addFlaggedSquare()
                else:
                    self.grid.getSquare(row, col).setFlag("?")

            elif self.grid.getSquare(row, col).getFlag() == "\u2691":
                self.grid.getSquare(row, col).setFlag("?")
                self.grid.removeFlaggedSquare()

            elif self.grid.getSquare(row, col).getFlag() == "?":
                self.grid.getSquare(row, col).setFlag("")

            self.updateNFlaggedSquares()

    def hadVictory(self):
        for r in self.grid.getGrid():
            for sq in r:
                if sq.getState() == 0 and sq.getValue() == " ":
                    return False
        return True

    def destroy(self):
        if self.time_start != 0:  #game started
            self.lab_time.after_cancel(self.id_time)
        for r in self.grid.getGrid():
            for sq in r:
                sq.getButton().grid_forget()
        super().destroy()