Ejemplo n.º 1
0
    def test_Game(self):
        game = Game(6, 6, 'X', 'O')

        game.board.move(1, 1, 'X')
        game.board.move(1, 5, 'O')
        game.board.move(4, 1, 'X')
        game.computer_move()

        self.assertEqual(len(game.board.number_empty_spaces), 0)
Ejemplo n.º 2
0
class GUI:
    """
    The design ideas we're taken from http://www.papg.com/show?2XMX
    """
    @staticmethod
    def insert_empty_space(frame, row, column):
        """
        This function is used to insert empty spaces to make the GUI prettier
        because I hate Tkinter
        :param frame: the frame in which the empty space is inserted
        :param row: the row on which we will grid
        :param column: the column on which we will grid
        """
        empty_label = Label(frame, text="")
        empty_label.grid(row=row, column=column)

    @staticmethod
    def open_database_connection():
        connection_string = psycopg2.connect(
            "host=localhost dbname=postgres user=postgres password=polopolo")
        current_connector = connection_string.cursor()
        return connection_string, current_connector

    @staticmethod
    def close_database_connection(connection_string, current_connector):
        connection_string.commit()
        connection_string.close()
        current_connector.close()

    def __init__(self):
        self.button_cells = [
        ]  # This will keep all the buttons in form of a matrix
        self.frame_gameplay = None  # This will be the gameplay frame which will be initialised in start_game function
        self.game = None  # This will be a Game type variable, It will be initialised in start_game because we need the rows/colums of the board
        self.user_score = 0  # This will keep the user score
        self.cpu_score = 0  # This will keep the cpu score
        self.row = 0  # This will keep the number of rows of the board
        self.column = 0  # This will keep the number of columns of the board
        self.user_symbol = None  # This will keep the user symbol
        self.cpu_symbol = None  # This will keep the cpu symbol
        self.human_turn = None  # This will be True if it is human's turn/False otherwise

        self.root = Tk()
        self.root.title("Play obstruction!")
        self.root.geometry("700x400")
        self.root.iconbitmap(
            'C:/Users/georg/OneDrive/Desktop/Informatica/Facultate/a11-912-Danicico-George/obstruction_icon.ico'
        )
        self.frame_game_entry = Frame(self.root,
                                      bd=4,
                                      highlightbackground="black",
                                      height=400,
                                      width=700)
        self.frame_game_entry.pack()
        # Implementation of the entry frame for the game
        welcome_label = Label(self.frame_game_entry, text="Play obstruction!")
        welcome_label.grid(row=0, column=0)

        GUI.insert_empty_space(self.frame_game_entry, 1, 0)

        name_label = Label(self.frame_game_entry, text="Your name: ")
        name_label.grid(row=2, column=0)
        # The Entry user_name will be needed when the user will press the New Game button in order to make
        # the connection with the data base
        self.user_name = Entry(self.frame_game_entry)
        self.user_name.grid(row=2, column=1)

        GUI.insert_empty_space(self.frame_game_entry, 3, 0)

        grid_label = Label(self.frame_game_entry, text="Size of grid: ")
        grid_label.grid(row=4, column=0)
        # Implementation for the checkbuttons for the first frame
        # Important! For the size of the grid only one checkbutton can be checked at a time.
        #            The same goes for the choice if the user wants to start the game.
        #           - This is done by the an auxiliary function, which check the variable for the checkbutton and deselect
        #           deselect its state if necessary

        self.var1 = IntVar()
        self.var2 = IntVar()
        self.var3 = IntVar()
        self.var4 = IntVar()

        self.size_one = Checkbutton(self.frame_game_entry,
                                    text="6x6",
                                    command=lambda x=1: self.check_button(x),
                                    variable=self.var1)
        self.size_one.deselect()
        self.size_one.grid(row=4, column=1)

        self.size_two = Checkbutton(self.frame_game_entry,
                                    text="7x6",
                                    command=lambda x=2: self.check_button(x),
                                    variable=self.var2)
        self.size_two.deselect()
        self.size_two.grid(row=4, column=2)

        self.size_three = Checkbutton(self.frame_game_entry,
                                      text="7x7",
                                      command=lambda x=3: self.check_button(x),
                                      variable=self.var3)
        self.size_three.deselect()
        self.size_three.grid(row=4, column=3)

        self.size_four = Checkbutton(self.frame_game_entry,
                                     text="8x8",
                                     command=lambda x=4: self.check_button(x),
                                     variable=self.var4)
        self.size_four.deselect()
        self.size_four.grid(row=4, column=4)
        # --------
        GUI.insert_empty_space(self.frame_game_entry, 5, 0)

        side_label = Label(self.frame_game_entry,
                           text="Do you want to play first? ")
        side_label.grid(row=6, column=0)
        # Set the checkboxes for YES/NO
        # These checkboxes will be needed in order to establish the symbol for the user/computer
        # Always in the game, 'O' will be the symbol for the one who starts, and 'X' for the other

        self.var_yes = IntVar()
        self.var_no = IntVar()

        self.yes_box = Checkbutton(self.frame_game_entry,
                                   text="Yes",
                                   command=lambda x=5: self.check_button(x),
                                   variable=self.var_yes)
        self.yes_box.deselect()
        self.yes_box.grid(row=6, column=1)

        self.no_box = Checkbutton(self.frame_game_entry,
                                  text="No",
                                  command=lambda x=6: self.check_button(x),
                                  variable=self.var_no)
        self.no_box.deselect()
        self.no_box.grid(row=6, column=2)

        GUI.insert_empty_space(self.frame_game_entry, 7, 0)

        self.new_game = Button(self.frame_game_entry,
                               text="New Game",
                               command=self.start_game)
        self.new_game.grid(row=8, column=0)

        self.root.mainloop()

    def check_button(self, button):
        """
        This is the auxiliary function we mentioned above. It checks, depending on the button value
        which correspond to a checkbutton, if it was selected to deselect the others.
        :param button: a certain value which is unique for every checkbutton
        """
        if button == 1:
            if int(self.var1.get()) == 1:
                self.size_two.deselect()
                self.size_three.deselect()
                self.size_four.deselect()
        if button == 2:
            if int(self.var2.get()) == 1:
                self.size_one.deselect()
                self.size_three.deselect()
                self.size_four.deselect()
        if button == 3:
            if int(self.var3.get()) == 1:
                self.size_two.deselect()
                self.size_one.deselect()
                self.size_four.deselect()
        if button == 4:
            if int(self.var4.get()) == 1:
                self.size_two.deselect()
                self.size_three.deselect()
                self.size_one.deselect()
        if button == 5:
            if int(self.var_yes.get()) == 1:
                self.no_box.deselect()
        if button == 6:
            if int(self.var_no.get()) == 1:
                self.yes_box.deselect()

    def get_size_of_grid(self):
        """
        Based on the value of the checkbuttons variables, we will return the row/columns for the board
        which will be generated in the gameplay Frame
        :return: row and column of the board
        """
        row = 0
        column = 0
        if int(self.var1.get()) == 1:
            row, column = 6, 6

        if int(self.var2.get()) == 1:
            row, column = 7, 6

        if int(self.var3.get()) == 1:
            row, column = 7, 7

        if int(self.var4.get()) == 1:
            row, column = 8, 8

        return row, column

    def get_symbols(self):
        """
        This function will return the symbols for the user and the cpu
        :return: user symbol and cpu symbol
        NB! Always 'O' does the first move.
        """
        user_symbol, cpu_symbol = None, None
        if int(self.var_yes.get()) == 1:
            user_symbol, cpu_symbol = 'O', 'X'
        elif int(self.var_no.get()) == 1:
            user_symbol, cpu_symbol = 'X', 'O'
        return user_symbol, cpu_symbol

    def start_game(self):
        """
        Below it is initialised the gameplay frame
            - We get from the entry frame:
                - the user name to make the connection with the database
                - the size of the board
                - the symbol for user and cpu / who starts first the game
        """
        user_name = self.user_name.get()
        self.row, self.column = self.get_size_of_grid()
        self.user_symbol, self.cpu_symbol = self.get_symbols()
        if user_name == "" or self.row == 0 or self.user_symbol is None:
            messagebox.showwarning("Warning!",
                                   "Please complete all the fields!")
            return
        # The connection to the database in order to rtetrieve the data is done.
        is_registered = False
        connection, cursor = GUI.open_database_connection()
        cursor.execute("select * from player")
        for row in cursor:
            if row[0] == user_name:
                is_registered = True
                self.user_score = int(row[1])
                self.cpu_score = int(row[2])

        if is_registered is False:
            cursor.execute("insert into player values (%s, %s, %s)",
                           (user_name, 0, 0))
        GUI.close_database_connection(connection, cursor)
        # After checking the case if the current user hadn't played the game before, it is added to the database

        self.frame_gameplay = Frame(self.root, bd=4)
        self.frame_game_entry.pack_forget()
        self.frame_gameplay.pack()

        GUI.insert_empty_space(self.frame_gameplay, 0, 10)
        # The purpose of the below labels is for "design
        score_label = Label(self.frame_gameplay, text="Score:")
        score_label.grid(row=0, column=11)

        label_user = Label(self.frame_gameplay, text=user_name)
        label_cpu = Label(self.frame_gameplay, text="CPU")
        label_cpu.grid(row=1, column=12)
        label_user.grid(row=0, column=12)

        lbl_user_score = Label(self.frame_gameplay, text=str(self.user_score))
        lbl_cpu_score = Label(self.frame_gameplay, text=str(self.cpu_score))
        lbl_user_score.grid(row=0, column=13)
        lbl_cpu_score.grid(row=1, column=13)

        funny_label = Label(self.frame_gameplay, text="Play Obstruction!")
        funny_label.grid(row=0, column=0, columnspan=3)

        GUI.insert_empty_space(self.frame_gameplay, 1, 0)
        # The true gameplay starts now!
        self.upload_board()

    def upload_board(self):
        # We clear the buttons list to prevent errors when the user will press the 'play again' button
        # Then, the program generates in form of a matrix with 'row' rows and 'column' colums the buttons
        self.button_cells.clear()
        for i in range(self.row):
            row_cells = []
            for j in range(self.column):
                cell = Button(self.frame_gameplay,
                              bg="white",
                              command=lambda x=i, y=j, z=self.user_symbol: self
                              .prepare_move(x, y, z),
                              padx=12,
                              pady=5)
                row_cells.append(cell)
                cell.grid(row=i + 2, column=j)
            self.button_cells.append(row_cells)

        GUI.insert_empty_space(self.frame_gameplay, self.row + 2, 0)
        # the Game variable is created in order to make the moves on the board
        # because behind the scenes lies a simple matrix with ' ', '-', 'X', 'O' symbols
        self.game = Game(self.row, self.column, self.user_symbol,
                         self.cpu_symbol)
        # Because always 'O' starts, we check who starts the game.
        self.human_turn = False if self.user_symbol == 'X' else True

        if self.human_turn is False:
            label_turn = Label(self.frame_gameplay, text="Thinking...")
            label_turn.grid(row=10, column=0, columnspan=2)
            self.prepare_move(None, None, self.user_symbol)
        else:
            label_turn = Label(self.frame_gameplay, text="Your turn...")
            label_turn.grid(row=10, column=0, columnspan=2)

    def prepare_move(self, row, column, symbol):
        """
        This function does a move for the cpu, if it is the cpu's turn
        Othewise it will check if the clicked cell is a valid one by calling "move" and "gui_move" function
        :param row: The row of the clicked cell if it is the human turn/ Otherwise None
        :param column: The column of the clicked cell if it is the human turn/ Otherwise None
        :param symbol: Always the symbol of the user
        """
        if self.human_turn is True:
            # It tries to make the move
            try:
                self.game.board.move(row, column, symbol)
                self.game.board.gui_move(row, column, symbol,
                                         self.button_cells)
                self.human_turn = False
                label_turn = Label(self.frame_gameplay, text="Thinking...")
                label_turn.grid(row=10, column=0, columnspan=2)
            except Exception:
                pass
        else:
            cpu_symbol = 'X' if symbol == 'O' else 'O'
            # it generates a random move for the cpu
            correct_move = False
            while not correct_move:
                try:
                    bot_row, bot_column = self.game.computer_move()
                    self.game.board.gui_move(bot_row, bot_column, cpu_symbol,
                                             self.button_cells)
                    correct_move = True
                    label_turn = Label(self.frame_gameplay,
                                       text="Your turn...")
                    label_turn.grid(row=10, column=0, columnspan=2)
                except Exception:
                    pass
            self.human_turn = True

        # If there are no empty space/ white buttons it means that the game has ended
        # And the winner is the user if human turn is False because the last move was done bu the user
        # Otherwise is the cpu.
        # And also the score labels are updated correspondingly
        if self.game.board.number_empty_spaces == 0:
            if self.human_turn is True:
                messagebox.showinfo("Sorry...", "You cannot defeat a god!")
                label_turn = Label(self.frame_gameplay, text="CPU wins...")
                label_turn.grid(row=10, column=0, columnspan=2)
                self.cpu_score += 1
            else:
                messagebox.showinfo("Congratulations!",
                                    "You just won against a god!")
                label_turn = Label(self.frame_gameplay, text="You win :)")
                label_turn.grid(row=10, column=0, columnspan=2)
                self.user_score += 1

            lbl_user_score = Label(self.frame_gameplay,
                                   text=str(self.user_score))
            lbl_cpu_score = Label(self.frame_gameplay,
                                  text=str(self.cpu_score))
            lbl_user_score.grid(row=0, column=13)
            lbl_cpu_score.grid(row=1, column=13)

            # The database must be with the new scores
            connection, cursor = GUI.open_database_connection()
            cursor.execute(
                "update player set player_score = %s where player_name = %s",
                (self.user_score, self.user_name.get()))
            cursor.execute(
                "update player set computer_score = %s where player_name = %s",
                (self.cpu_score, self.user_name.get()))
            GUI.close_database_connection(connection, cursor)
            # After updating the data base, we close the connection
            GUI.insert_empty_space(self.frame_gameplay, 11, 0)

            play_again_button = Button(self.frame_gameplay,
                                       text="Play again",
                                       command=self.upload_board)
            play_again_button.grid(row=12, column=0, columnspan=2)
        else:
            if self.human_turn is False:
                self.prepare_move(None, None, symbol)
Ejemplo n.º 3
0
class Console_UI:
    """
    when the game start, it asks the user for the dimensions of the board and the \
    symbol he want to take
    """
    def __init__(self):
        self._rows = None
        self._columns = None
        self._game_board = None
        self._user_sign = None
        self._computer_sign = None

    def read_starting_game_data(self):
        correct_data = False
        while not correct_data:
            try:
                rows = input(
                    "Please enter the number of rows of the board: ").strip()
                if not rows.isdigit():
                    raise Exception("Invalid character for rows!...")
                columns = input(
                    "Please enter the number of columns of the board: ").strip(
                    )
                if not columns.isdigit():
                    raise Exception("Invalid character for columns!...")
                if int(rows) > 8 or int(columns) > 8:
                    raise Exception(
                        "Numbers too big for the size of the board!...")

                user_sign = input(
                    "Please enter the sign you want to play with (X/O): "
                ).strip()
                if user_sign not in ['X', 'O']:
                    raise Exception("Invalid sign entered!...")

                self._rows = int(rows)
                self._columns = int(columns)
                self._user_sign = user_sign
                correct_data = True
            except Exception as message:
                print(str(message) + '\n')

        if self._user_sign == 'X':
            self._computer_sign = 'O'
        else:
            self._computer_sign = 'X'

        self._game_board = Game(self._rows, self._columns, self._user_sign,
                                self._computer_sign)

    def start(self):
        # The user should be able to play as many rounds against the bot as he wants
        in_game = True
        user_name = input("Welcome! Please enter your name: ").strip()
        user_score = 0
        computer_score = 0
        is_registered = False
        print('\n')
        # If the user has already played in the past, his score is recorded in the data base

        connection_string = psycopg2.connect(
            "host=localhost dbname=postgres user=postgres password=polopolo")
        current_connector = connection_string.cursor()
        current_connector.execute("select * from player")
        # If the user is present in the data base, is_registered will be True/False otherwise
        # This will help at the end of the game, because if is_regitered == False it means the user must be placed in the database
        # if is_registered == True it means that the user score must be updated
        for row in current_connector:
            if user_name == str(row[0]):
                is_registered = True
                user_score = int(row[1])
                computer_score = int(row[2])

        while in_game:

            self.read_starting_game_data()
            finished = False
            human_turn = False
            if self._user_sign == 'O':
                human_turn = True

            while not finished:
                # firstly, the computer does the move
                if human_turn:

                    correct_user_move = False
                    while not correct_user_move:
                        row_index = input("Enter the row: ").strip()
                        column_index = input("Enter the column: ").strip()

                        if row_index.isdigit() and column_index.isdigit():
                            try:
                                self._game_board.user_move(
                                    int(row_index), int(column_index))
                                correct_user_move = True
                            except Exception as error_message:
                                print(str(error_message) + '\n')
                    print(self._game_board.board)
                    human_turn = False
                else:
                    correct_move = False
                    while not correct_move:
                        try:
                            bot_row, bot_column = self._game_board.computer_move(
                            )
                            print(
                                f"The computers move is: [{bot_row}, {bot_column}]"
                            )
                            correct_move = True
                        except Exception:
                            pass
                    print(self._game_board.board)
                    human_turn = True

                print("\n")

                if self._game_board.board.number_empty_spaces == 0:
                    """
                    this means that the game ended
                        - if the human_turn = True, it means that the last move was done by the computer
                        and it means that the computer won.
                        - if the human_turn = False, it means that the last move was done by the 
                        user and it means that the user won.
                    """
                    finished = True

            if human_turn is False:
                print("Congratulation, you won!")
                user_score += 1
            else:
                print("Unfortunately, you lost...")
                computer_score += 1

            if is_registered is False:
                current_connector.execute("Insert into player values (%s, %s)",
                                          (user_name, user_score))
                is_registered = True
            else:
                current_connector.execute(
                    "update player set player_score = %s where player_name = %s",
                    (user_score, user_name))
                current_connector.execute(
                    "update player set computer_score = %s where player_name = %s",
                    (computer_score, user_name))

            connection_string.commit()

            print(f"CPU: {computer_score} |  {user_name}: {user_score}\n")
            play_again = input("Do you want to play again? ").strip()
            print("\n")
            if play_again.lower() != "yes":
                in_game = False

        current_connector.close()
        connection_string.close()