Exemple #1
0
class GUI:
    """
    Designed to handle the GUI aspects (creating a window, buttons and
    pop-ups. Also initializes the communicator object.
    """

    MESSAGE_DISPLAY_TIMEOUT = 250

    def __init__(self, parent, port, ip=None):
        """
        Initializes the GUI and connects the communicator.
        :param parent: the tkinter root.
        :param ip: the ip to connect to.
        :param port: the port to connect to.
        :param server: true if the communicator is a server, otherwise false.
        """
        self._parent = parent
        self._canvas = t.Canvas(self._parent, width=300, height=300)
        self._canvas.pack()
        self.__communicator = Communicator(parent, port, ip)
        self.__communicator.connect()
        self.__communicator.bind_message_to_action(self.__handle_message)
        self.__place_widgets()

    def __place_widgets(self):
        self.__button = t.Button(
            self._parent,
            text="YO",
            font=("Garamond", 20, "bold"),
            command=lambda: self.__communicator.send_message("YO"))
        self.__button.pack()
        self.__button.place(x=120, y=120)
        self.__label = t.Label(self._parent,
                               text="",
                               fg="red",
                               font=("Garamond", 40, "bold"))
        self.__label.pack()
        self.__label.place(x=109, y=200)

    def __handle_message(self, text=None):
        """
        Specifies the event handler for the message getting event in the
        communicator. Prints a message when invoked (and invoked by the
        communicator when a message is received). The message will
        automatically disappear after a fixed interval.
        :param text: the text to be printed.
        :return: None.
        """
        if text:
            self.__label["text"] = text
            self._parent.after(self.MESSAGE_DISPLAY_TIMEOUT,
                               self.__handle_message)
        else:
            self.__label["text"] = ""
class GUI:
    ELEMENT_SIZE = 50
    MESSAGE_DISPLAY_TIMEOUT = 250
    GRID_COLOR = "#AAA"
    PLAYER_1_COLOR = "blue"
    PLAYER_2_COLOR = "red"
    RED_WIN_COLOR = "#B22222"
    BLUE_WIN_COLOR = "#008080"
    DEFAULT_COLOR = "white"
    BACKGROUND_COLOR = "green"

    def __init__(self, root, game, human_or_ai, port=None, ip=None):
        """
        Initializes the GUI and connects the communicator.
        :param parent: the tkinter root.
        :param ip: the ip to connect to.
        :param port: the port to connect to.
        :param server: true if the communicator is a server, otherwise false.
        """
        self.game = game
        self.ai = AI()
        self.root = root
        if human_or_ai:
            self.ai_on = False
        else:
            self.ai_on = True
        self.ip = ip
        self.port = port
        self.my_turn = True
        """The top image in the gui"""
        image_path = r"intro2cs.gif"
        photo = PhotoImage(file=image_path)
        label = Label(image=photo)
        label.image = photo  # keep a reference
        label.grid(row=0, column=0, pady=10)

        self.canvas = Canvas(
            root,
            width=200,
            height=50,
            background=self.BACKGROUND_COLOR,
            highlightthickness=0,
        )
        self.canvas.grid(row=2)

        self.current_player_var = StringVar(self.root, value="")
        self.currentPlayerLabel = Label(self.root,
                                        textvariable=self.current_player_var,
                                        anchor=W)
        self.currentPlayerLabel.grid(row=3)
        """when the user click on the canvas do action according to the
         _action_when_canvas_clicked function"""
        self.canvas.bind('<Button-1>', self._action_when_canvas_clicked)
        self.new_game()

        self.__communicator = Communicator(root, port, ip)
        self.__communicator.connect()
        self.__communicator.bind_action_to_message(self.__handle_message)

    def __handle_message(self, text=None):
        """
        Specifies the event handler for the message getting event in the
        communicator. Prints a message when invoked (and invoked by the
        communicator when a message is received). The message will
        automatically disappear after a fixed interval.
        :param text: the text to be printed.
        :return: None.
        """
        """If got a text - column, do that move in the self board, so the
         opponent board and the self board would be synchronized"""
        if text:
            self.game.make_move(int(text[0]))
            """The enemy made his turn, and now self.my_turn should changed to
             true and so on the current_player indicator in the gui"""
            self.my_turn = not self.my_turn
            self.current_player_var.set('Current player: ' + "Your Turn")
        if self.ai_on and self.game.game_over != self.game.YES:
            self.make_ai_move()
        # draw the board again after all the changes has been made
        self.draw()

    def draw(self):
        """Draw all the board with the disks and there's color"""
        """The two for loop runs on all the game.board and create disks
         according to the situation in the board"""
        for c in range(self.game.cols, -1, -1):
            """changes the board direction so the disks would be at the
             bottom and not at the top"""
            self.game.board = self.game.board[::-1]
            for r in range(self.game.rows, -1, -1):
                if r >= self.game.cols:
                    continue

                x0 = c * self.ELEMENT_SIZE
                y0 = r * self.ELEMENT_SIZE
                x1 = (c + 1) * self.ELEMENT_SIZE
                y1 = (r + 1) * self.ELEMENT_SIZE
                """Create each disk according to the game board, if at the
                 location the board is empty, then create disk with the
                  default color - white, else - create the disk red/blue if
                  the board at that location is red/blue """
                if self.game.board[r][c] == self.game.BLUE:
                    fill = self.PLAYER_1_COLOR
                elif self.game.board[r][c] == self.game.RED:
                    fill = self.PLAYER_2_COLOR
                elif self.game.board[r][c] == self.game.RED + self.game.RED:
                    fill = self.RED_WIN_COLOR
                elif self.game.board[r][c] == self.game.BLUE + self.game.BLUE:
                    fill = self.BLUE_WIN_COLOR
                else:
                    fill = self.DEFAULT_COLOR
                self.canvas.create_oval(x0 + 2,
                                        self.canvas.winfo_height() - (y0 + 2),
                                        x1 - 2,
                                        self.canvas.winfo_height() - (y1 - 2),
                                        fill=fill,
                                        outline=self.GRID_COLOR)
            self.game.board = self.game.board[::-1]

        if self.game.game_over == self.game.YES:
            """If the game is over, checks who won/lose/draw and print to the
             canvas message that says that"""
            text_width = text_height = self.canvas.winfo_height() / 2
            # giving default win msg
            win_msg = ""
            # checks if there was a draw
            if self.game.get_winner() == self.game.DRAW:
                win_msg = "There Was a :" + "\n" + "Draw"
                """if when the game is over, the gui current var is
                 "your turn" that mean the you lost the game """
            elif self.current_player_var.get() == "Current player: Your Turn":
                win_msg = "You Lost" + "\n" + "The Game"
            elif self.current_player_var.get() ==\
                    "Current player: The Enemy Turn":
                win_msg = "You Won" + "\n" + "The Game"
            self.canvas.create_text(text_height,
                                    text_width,
                                    text=win_msg,
                                    font=("Helvetica", 32),
                                    fill="black")

    def draw_grid(self):
        """Draw the grid"""
        x0, x1 = 0, self.canvas.winfo_width()
        for r in range(1, self.game.rows):
            y = r * self.ELEMENT_SIZE
            self.canvas.create_line(x0, y, x1, y, fill=self.GRID_COLOR)

        y0, y1 = 0, self.canvas.winfo_height()
        for c in range(1, self.game.cols + 1):
            x = c * self.ELEMENT_SIZE
            self.canvas.create_line(x, y0, x, y1, fill=self.GRID_COLOR)

    def drop(self, column):
        """Make the move with on the game board with the given column
        and send a message to the opponent about the move that he just made,
        so the opponent gui board would be updated and update the current turn
         in the gui"""
        if self.game.board[0][column] == self.game.EMPTY:
            self.current_player_var.set('Current player: ' + "The Enemy Turn")
            self.__communicator.send_message(str(column))
            return self.game.make_move(column)
        else:
            return

    def new_game(self):
        """Create the new game"""
        self.game = Game()

        self.canvas.delete(ALL)
        self.canvas.config(width=self.ELEMENT_SIZE * self.game.rows,
                           height=self.ELEMENT_SIZE * self.game.cols)
        self.root.update()
        self.draw_grid()
        self.draw()

    def _action_when_canvas_clicked(self, event):
        """This function responsible for the action that happens, when the user
         click the board (the canvas)"""
        # do something only if its my_turn
        if self.my_turn:
            if self.game.game_over:
                # when the game is over, click would do nothing
                return

            c = event.x // self.ELEMENT_SIZE
            """if it the client/server my_turn is true, and the client/server
             clicked on a column then put the disk in that column"""
            if 0 <= c < self.game.rows:
                if not self.ai_on:
                    self.drop(c)
                """After the client/server did his turn, his turn expired and
                 changed to false, because after he did his move, the enemy
                 move has come"""
                self.my_turn = not self.my_turn
                # draw the board after the move has been made
                self.draw()
        return

    def make_ai_move(self):
        """Do the ai move """
        if self.game.game_over == self.game.YES:
            return
        column = self.ai.find_legal_move(self.game, self.game.make_move)
        self.__communicator.send_message(str(column))
        # update the turn on display (on the GUI)
        self.current_player_var.set('Current player: ' + "The Enemy Turn")
        self.draw()
Exemple #3
0
class GUI():
    """
    A class representing the game Graphical User Interface including all the
    graphics of the game and the visual parts, including popping messages,
    and painting ovals
    """

    HUMAN = "human"
    COMPUTER = "ai"
    OPENING_MSG = "WELCOME!"
    GAME_INSTRUCTIONS = "Please press at the button of the column you would " \
                        "like to put your disc in it."
    EXIT_MSG = "Exit"
    CANVAS_BACKGROUND = "MediumPurple1"
    EMPTY_OVAL = "white"
    SERVER_COLOR = "blue"
    CLIENT_COLOR = "red"
    WINNER_MARK = "gold"
    TITLE_MSG = "MESSAGE"
    WIN_MSG = "Congratulation, you won!"
    LOSE_MSG = "That's too bad, you lost!"
    DRAW_MSG = "It's a draw!"
    BOARD_HEIGHT = int(544)
    BOARD_WIDTH = int(634)
    FRAME_HEIGHT = int(1000)
    FRAME_WIDTH = int(1000)
    WIDGET_LOCATION = int(90)

    def __init__(self, player, port, server, ip=None):
        """
        a constructor of the graphics of the game
        :param player: either human of computer
        :param port: a number between 1000 and 65535
        :param server: a boolean value that is True for the server and False
        for the client.
        :param ip: None for the server and an ip for the client
        """
        self.game = Game()
        self.ai = ai.AI()
        self.root = tk.Tk()
        self.frame = tk.Frame(self.root,
                              height=self.FRAME_HEIGHT,
                              width=self.FRAME_WIDTH)
        self.game_board = tk.Canvas(self.frame,
                                    bg=self.CANVAS_BACKGROUND,
                                    height=self.BOARD_HEIGHT,
                                    width=self.BOARD_WIDTH)
        self.game_board.pack()
        self.game_board.place(x=200, y=50)
        self.__communicator = Communicator(self.root, port, ip)
        self.__communicator.connect()
        self.__communicator.bind_action_to_message(self.handle_message)
        self.player = player
        self.server = server

    def is_human(self):
        """
        this function update the player to be human or computer considering
        what was given at the args line.
        :return: True if the player is human and False if the player is ai
        """
        if sys.argv[1] == self.COMPUTER:
            self.player = self.COMPUTER
            return False
        if sys.argv[1] == self.HUMAN:
            self.player = self.HUMAN
            return True

    def show_message(self, title, msg):
        """
        This is a method used to show messages in the game.
        :param title: The title of the message box.
        :type title: str
        :param msg: The message to show in the message box.
        :type msg: str
        """
        tk.messagebox.showinfo(str(title), str(msg))

    def end_game(self):
        """
        This ends the current game.
        """
        self.root.destroy()
        self.root.quit()

    def paint_oval(self, col, color):
        """
        this function painting the ovals at the color of the current player
        :param col: get the col that the player chose to paint
        :param color: the color of the player(each player has his own color
        :return: an oval at the size of all the ovals and at the color given
        """
        row = self.game.make_move(col) + 1
        self.game_board.create_oval(5 + col * self.WIDGET_LOCATION,
                                    5 + row * self.WIDGET_LOCATION,
                                    col * self.WIDGET_LOCATION + 95,
                                    row * self.WIDGET_LOCATION + 95,
                                    fill=color)

    def paint_winner_oval(self, row, col):
        """
        this function painting a winning oval with a gold outline.
        :param row: the row of the winning oval
        :param col: the col of the winning oval
        :return: a gold outline for a winning oval
        """
        self.game_board.create_oval(5 + col * self.WIDGET_LOCATION,
                                    5 + row * self.WIDGET_LOCATION,
                                    col * self.WIDGET_LOCATION + 95,
                                    row * self.WIDGET_LOCATION + 95,
                                    outline=self.WINNER_MARK,
                                    width=5)

    def mark_win(self):
        """
        this function activates the paint_winner_oval(that marking the ovals
        with a gold outline) at each one of the winning ovals.
        """
        if self.game.check_col()[0] in [
                self.game.PLAYER_ONE, self.game.PLAYER_TWO
        ]:
            for row, col in self.game.check_col()[1]:
                self.paint_winner_oval(row, col)
        if self.game.check_row()[0] in [
                self.game.PLAYER_ONE, self.game.PLAYER_TWO
        ]:
            for row, col in self.game.check_row()[1]:
                col = col - 1
                self.paint_winner_oval(row, col)
        if self.game.check_diagonals()[0] in [
                self.game.PLAYER_ONE, self.game.PLAYER_TWO
        ]:
            for row, col in self.game.check_diagonals()[1]:
                self.paint_winner_oval(row, col)

    def win_step(self):
        """
        this function mark the winner discs, shows the winner the message
        that he won, send message to the other player that he lose and ends
        the game.
        """
        self.mark_win()
        self.show_message(self.TITLE_MSG, self.WIN_MSG)
        self.__communicator.send_message(self.LOSE_MSG)
        self.end_game()

    def lose_step(self):
        """
        this function mark the winner discs, shows the loser the message
        that he lose and ends the game.
        """
        self.mark_win()
        self.show_message(self.TITLE_MSG, self.LOSE_MSG)
        self.end_game()

    def update_game(self, col):
        """
        this function checks if the player is the server of the client and
        than checks if it is his turn. If so, it activate the paint_oval
        function with the col given and with the player color
        :param col: a col that the player chose to place his disc at
        :return: an painted oval at the color of the player and message at
        the end of the game(win of draw)
        """
        cur_player = self.game.get_current_player()
        if self.server:
            if cur_player == self.game.PLAYER_ONE:
                self.paint_oval(col, self.SERVER_COLOR)
                self.__communicator.send_message(col)
            if self.game.get_winner() == self.game.PLAYER_ONE:
                self.win_step()
        elif not self.server:
            if cur_player == self.game.PLAYER_TWO:
                self.paint_oval(col, self.CLIENT_COLOR)
                self.__communicator.send_message(col)
            if self.game.get_winner() == self.game.PLAYER_TWO:
                self.win_step()
        if self.game.get_winner() == self.game.DRAW:
            self.show_message(self.TITLE_MSG, self.DRAW_MSG)
            self.__communicator.send_message(self.DRAW_MSG)
            self.end_game()

    def create_board(self):
        """
        this function creates the screen-  the visual board. including it's
        labels and empty ovals.
        :return: the visual initial screen
        """
        label_start = tk.Label(self.root,
                               text=self.OPENING_MSG,
                               font=('Arial', 18))
        label_start.pack()
        label_play = tk.Label(self.root,
                              text=self.GAME_INSTRUCTIONS,
                              font=('David', 15))
        label_play.pack()
        for j in range(5, 600, 90):
            for i in range(5, 700, 90):
                self.game_board.create_oval(i,
                                            j,
                                            i + self.WIDGET_LOCATION,
                                            j + self.WIDGET_LOCATION,
                                            fill=self.EMPTY_OVAL)

    def handle_message(self, text=None):
        """
        this function receive a message from the other player that says
        where he put his disc and if the game ends it shows the matching
        message
        :param text: the string of the location that the player want's to
        put his disc at
        :return: the painting oval at the other player's screen and if the
        game ends it shows the matching message(win, lose or draw)
        """
        if text:
            if self.server:
                color = self.CLIENT_COLOR
                self.paint_oval(int(text), color)
                if not self.is_human():
                    self.ai.find_legal_move(self.game, self.update_game)
            elif not self.server:
                color = self.SERVER_COLOR
                self.paint_oval(int(text), color)
                if not self.is_human():
                    self.ai.find_legal_move(self.game, self.update_game)
        if self.game.get_winner() == self.game.PLAYER_ONE:
            if not self.server:
                self.lose_step()
        if self.game.get_winner() == self.game.PLAYER_TWO:
            if self.server:
                self.lose_step()
        if self.game.get_winner() == self.game.DRAW:
            self.show_message(self.TITLE_MSG, self.DRAW_MSG)
            self.end_game()

    def run_game(self):
        """
        this function runs the game.
        it creates the buttons of the columns and the quit button.
        :return: the updating screen, with the buttons so pressing on them
        will paint the wanted disc
        """
        col_but_lst = []
        for index in range(1, 8):
            col_but = tk.Button(
                self.frame,
                text=("col", index),
                command=lambda x=index: self.update_game(x - 1))
            col_but.pack()
            col_but_lst.append(col_but)
        for i in range(len(col_but_lst)):
            col_but_lst[i].place(x=235 + self.WIDGET_LOCATION * i, y=25)
        quit_but = tk.Button(self.root,
                             text=self.EXIT_MSG,
                             command=self.end_game)
        quit_but.pack()
        quit_but.place(x=950, y=30)
        self.frame.pack()
        self.root.mainloop()
class Game:
    """
    This class represents the game 4 in a row we were asked to implement.
    """

    PLAYER_ONE = 0
    PLAYER_TWO = 1
    DRAW = 2
    NUMBER_TO_PLAYER = {PLAYER_ONE: "Server", PLAYER_TWO: "Client"}
    CONTINUE_GAME = 3
    ILLEGAL_MOVE = "Illegal move"
    PLAYER_ONE_WON_MSG = NUMBER_TO_PLAYER[PLAYER_ONE] + " has won the game"
    PLAYER_TWO_WON_MSG = NUMBER_TO_PLAYER[PLAYER_TWO] + " has won the game"
    DRAW_MSG = "There is a draw"
    DEFAULT_NUMBER_OF_ROWS = 6
    DEFAULT_NUMBER_OF_COLS = 7
    NUMBER_OF_ELEMENTS_IN_SEQUENCE = 4
    WIN_NOT_FOUND = -2
    DEFAULT_IP = socket.gethostbyname(socket.gethostname())
    AI_DEFAULT_VALUE = None

    def __init__(self,
                 is_human,
                 is_server,
                 port,
                 ip_of_server=DEFAULT_IP):
        """
        :param is_human: string. Describes if the player is human or ai.
        :param is_server: boolean value. If True, the user is the server
        and plays first, otherwise the player is the client and plays second.
        :param port: the port number the players uses for the game.
        goes between 1 to 65355.
        :param ip_of_server: the ip number of the server.
        the client insert this number.
        """
        # Creates the board game
        self.board = Game_Board(Game.DEFAULT_NUMBER_OF_COLS,
                                Game.DEFAULT_NUMBER_OF_ROWS,
                                Game.PLAYER_ONE,
                                Game.PLAYER_TWO,
                                Game.DRAW,
                                Game.WIN_NOT_FOUND,
                                Game.NUMBER_OF_ELEMENTS_IN_SEQUENCE)

        self.root = tki.Tk()
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()

        # Creates the GUI method of the game
        self.gameGui = FourInARowGui(self.root,
                                     self.make_move,
                                     Game.DEFAULT_NUMBER_OF_ROWS,
                                     Game.DEFAULT_NUMBER_OF_COLS,
                                     screen_height,
                                     screen_width)

        # Defines which player begins.
        if is_server:
            self.root.title("Server")
            self.__communicator = Communicator(self.root, port)
        else:
            self.root.title("Client")
            self.__communicator = Communicator(self.root,
                                               port,
                                               ip_of_server)
            self.gameGui.lock_player()

        self.__communicator.bind_action_to_message(self.act_upon_message)
        self.__communicator.connect()

        # sets whose turn it is
        current_player = self.get_current_player()
        self.change_whose_turn_label(self.get_other_player(current_player))

        # Creates AI user if needed
        self.ai = Game.AI_DEFAULT_VALUE
        if not is_human:
            self.ai = AI()
            if is_server:
                self.make_ai_move()
            else:
                self.gameGui.lock_player()

        self.run_game()

    def run_game(self):
        """
        The function runs the game.
        """
        self.root.mainloop()

    def make_ai_move(self):
        """
        Makes an AI move if there is an ai player.
        :return:
        """
        col = self.ai.find_legal_move(self, self.make_ai_func())
        self.gameGui.simulate_press_by_ai(col)

    def act_upon_message(self, message):
        """
        :param message: a message given by the client or the server
        the message is expected to be the number of the column that was pressed
        :return:
        """
        current_player = self.get_current_player()
        self.change_whose_turn_label(current_player)

        self.gameGui.unlock_player()
        self.gameGui.simulate_press(int(message))

        if self.ai is not Game.AI_DEFAULT_VALUE:
            self.make_ai_move()

    def send_action_to_other_player(self, column):
        """
        :param column: the column the user has pressed
        locks the current player from pressing any other column
        and sends the column that was pressed on as a message to the
        other player
        """
        current_player = self.get_current_player()
        self.change_whose_turn_label(current_player)

        self.gameGui.lock_player()
        self.__communicator.send_message(str(column))

    def change_whose_turn_label(self, current_player):
        """
        :param current_player: the code of the current player
        changes the label such that it will say that the turn
        is that of the other player
        """
        whose_turn = \
            Game.NUMBER_TO_PLAYER[self.get_other_player(current_player)] + " Turn"
        self.gameGui.change_top_label(whose_turn)

    def get_other_player(self, current_player):
        """
        :param current_player: the player who is playing now
        :return: the other player
        """
        if current_player == Game.PLAYER_ONE:
            return Game.PLAYER_TWO
        if current_player == Game.PLAYER_TWO:
            return Game.PLAYER_ONE

    def make_move(self,
                  column,
                  me_has_pressing=True,
                  illegal_move_was_made=False):
        """
        This function implement single move in the game
        :param column: the column the player choose
        :param me_has_pressing: if the player pressed something
        :param illegal_move_was_made: a boolean, if its true an exception
        will be raised
        """
        if illegal_move_was_made:
            raise Exception(Game.ILLEGAL_MOVE)

        if me_has_pressing:
            self.send_action_to_other_player(column)

        current_player = self.get_current_player()

        if not self.board.valid_player(current_player):
            raise Exception(Game.ILLEGAL_MOVE)
        else:
            self.board.move_of_player(current_player, column)

            self.check_status()

    def check_status(self):
        """
        The function deals with cases of winning or draw in the game.
        Than it shuts the game down.
        """
        win_check = self.board.check_if_there_is_win()
        if win_check:
            # The winner player and the sequence of slots that won the game
            winner = win_check[0]
            win_sequence = win_check[1]
            win_sequence = Game.reverse_tuples_in_list(win_sequence)
            # Displaying the message to the players
            self.gameGui.game_over(self.final_stage_msg(winner), win_sequence)
            self.reset_ai()

        if self.board.check_board_if_full():
            # Displaying draw message to the players
            self.gameGui.game_over(self.final_stage_msg(Game.DRAW))
            self.reset_ai()

    def get_winner(self):
        """
        this function checks if there is a winner or a draw
        if there is a winner it returns the code of the player that won
        if there is a draw it returns the code for a draw
        if none of the above it returns None
        """
        win_check = self.board.check_if_there_is_win()
        if win_check:
            winner = win_check[0]
            return winner
        if self.board.check_board_if_full():
            return Game.DRAW

    def reset_ai(self):
        """
        Changes the ai to None
        """
        self.ai = Game.AI_DEFAULT_VALUE

    def final_stage_msg(self, winner_num):
        """
        printing message to the screen for win or draw
        """
        if winner_num == Game.PLAYER_ONE:
            return Game.PLAYER_ONE_WON_MSG

        if winner_num == Game.PLAYER_TWO:
            return Game.PLAYER_TWO_WON_MSG

        if winner_num == Game.DRAW:
            return Game.DRAW_MSG

    def get_player_at(self, row, col):
        """
        returns which player is on specific location
        """
        board = self.board.get_board()
        return board[row][col]

    def get_current_player(self):
        """
        returns whose turn is it now
        """
        return self.board.board_status()

    def make_ai_func(self):
        """
        Implement the moves of the AI.
        """

        def make_ai_move(col):
            self.gameGui.simulate_press(col)

        return make_ai_move

    @staticmethod
    def reverse_tuples_in_list(list_of_tuples):
        """
        :param list_of_tuples
        returns the same list with the same tuples but repositioned.
        """
        new_list = []
        for tpl in list_of_tuples:
            new_list.append(tuple(reversed(tpl)))
        return new_list
Exemple #5
0
class Graphics:
    """class that operate the graphic interface and communication of
    four_in_a_row game"""
    FIRST_COLUMN = 0
    TEXT1 = 'Player 1 turn'
    TEXT2 = 'Player 2 turn'
    TEXT3 = 'use LEFT/RIGHT keys to choose a column'
    TEXT4 = 'use DOWN key to drop a disc'
    TEXT5 = 'ILLEGAL MOVE'
    FIRST_TURN = 0
    MESSAGE_DISPLAY_TIMEOUT = 1000
    MAIN_WINDOW_WIDTHE = 350
    SQUARE_SIZE = 50
    TOP_FRAME_HEIGHT = 100
    MIDDLE_FRAME_HEIGHT = 300
    OVAL_1 = 5
    OVAL_2 = 45
    BLUE1 = 'dodger blue'
    FILL_WHITE = "white"
    TAG_OVAL = "oval"
    CANVAS_WIDTH = 370
    FILL_RED = "red"
    W_ANCHOR = "w"
    NW_ANCHOR = "nw"
    TAG_PLAYER = "player"
    BD10 = 10
    CANVAS5 = 5
    CANVAS_0 = 0
    FONT16 = 16
    FONT12 = 12
    CANVAS40 = 40
    CANVAS70 = 70
    SW_ANCHOR = "sw"
    CANVAS185 = 185
    CANVAS45 = 45
    FILL_GOLD = "gold"
    CANVAS85 = 85
    ILLEGAL_MSG = "illegal_msg"
    FONT18 = 18
    CANVAS25 = 25
    MIN_MOVE = 0
    MAX_MOVE = 6
    TAG_ARROW = "arrow"
    CHANGE_ARROW = 54
    FILL_GREEN = 'lawn green'
    TAG_HUMAN = 'human'
    TAG_AI = "ai"
    MOVE_RIGHT = "<Right>"
    MOVE_LEFT = "<Left>"
    MOVE_DOWN = '<Down>'
    CHANGE = 1

    def __init__(self, parent, port, player, ip=None, player_turn=True):
        """initiate a graphics object
        :param parent: a tkinter root(main window)
        :param port: the port the player choose that manage the
        communication
        :param player: type of player- human or ai
        :param ip: an ip required if the player defines as the client
        :param player_turn: defines if its the player turn to play- changes
        through the game"""
        self.__player = player
        self.__player_turn = player_turn
        if ip:
            self.__player_turn = False  # in order the server to get the
            # first turn
        self.__parent = parent
        self.__communicator = Communicator(self.__parent, port, ip)  # to
        # unblock the communication
        self.__communicator.connect()
        self.__communicator.bind_action_to_message(self.__handle_messege)
        self.__game_board = Game()  # initiate a game object
        # now dividing the main window to three frames- top for title and
        # announcements, middle for the board an bottom for rules and state
        self.__top_frame = tki.Frame(self.__parent,
                                     width=self.MAIN_WINDOW_WIDTHE,
                                     height=self.TOP_FRAME_HEIGHT,
                                     bd=10,
                                     relief=tki.GROOVE)
        self.__top_frame.pack(side=tki.TOP)
        self.__middle_frame = tki.Frame(self.__parent,
                                        width=self.MAIN_WINDOW_WIDTHE,
                                        height=self.MIDDLE_FRAME_HEIGHT,
                                        bd=self.BD10,
                                        relief=tki.GROOVE)
        self.__middle_frame.pack()
        self.__bottom_frame = tki.Frame(self.__parent,
                                        width=self.MAIN_WINDOW_WIDTHE,
                                        height=self.TOP_FRAME_HEIGHT,
                                        bd=self.BD10,
                                        relief=tki.GROOVE)
        self.__bottom_frame.pack(side=tki.BOTTOM)
        self.create_widgets()
        self.create_ovals(self.__middle_frame)
        self.moves()
        self.__arrow_point = self.FIRST_COLUMN  # initiate arrow at first
        # column

    def create_ovals(self, frame):
        """initiate canvases represnt the discs container"""
        for i in range(self.__game_board.LENTGH):
            for j in range(self.__game_board.WIDTH):
                square = tki.Canvas(frame,
                                    width=self.SQUARE_SIZE,
                                    height=self.SQUARE_SIZE,
                                    bg=self.BLUE1)
                square.create_oval(self.OVAL_1,
                                   self.OVAL_1,
                                   self.OVAL_2,
                                   self.OVAL_2,
                                   fill=self.FILL_WHITE,
                                   tag=self.TAG_OVAL)
                square.grid(row=i, column=j)

    def create_widgets(self):
        """creates and pack all main canvases texts and images that make up
        the design"""
        self.__top_canvas = tki.Canvas(self.__top_frame,
                                       width=self.CANVAS_WIDTH,
                                       height=self.TOP_FRAME_HEIGHT,
                                       bg=self.FILL_WHITE)
        self.__top_canvas.pack()
        self.__bottom_canvas = tki.Canvas(self.__bottom_frame,
                                          width=self.CANVAS_WIDTH,
                                          height=self.TOP_FRAME_HEIGHT,
                                          bg=self.FILL_WHITE)
        self.__bottom_canvas.pack()
        self.__bottom_canvas.create_text(self.CANVAS5,
                                         self.CANVAS_0,
                                         anchor=self.NW_ANCHOR,
                                         text=self.TEXT1,
                                         fill=self.FILL_RED,
                                         tag=self.TAG_PLAYER,
                                         font=(False, self.FONT16))
        self.__bottom_canvas.create_text(self.CANVAS5,
                                         self.CANVAS40,
                                         anchor=self.W_ANCHOR,
                                         text=self.TEXT3,
                                         fill=self.BLUE1,
                                         font=(False, self.FONT12))
        self.__bottom_canvas.create_text(self.CANVAS5,
                                         self.CANVAS70,
                                         anchor=self.SW_ANCHOR,
                                         text=self.TEXT4,
                                         fill=self.BLUE1,
                                         font=(False, self.FONT12))
        self.__title_file = tki.PhotoImage(file='title.gif')
        self.__top_canvas.create_image(self.CANVAS185,
                                       self.CANVAS40,
                                       image=self.__title_file)
        self.__arrow = tki.PhotoImage(file="giphy.gif")
        self.__win_file = tki.PhotoImage(file='youwin.png')
        self.__lose_file = tki.PhotoImage(file='lose.png')
        self.__draw_file = tki.PhotoImage(file='draw.png')
        self.__top_canvas.focus_set()  # focusing key_board to top canvas

    def check_for_winner(self):
        """using the game_board function of checking winner and show the
        winning state on the screen"""
        if self.__game_board.get_winner() == \
                self.__game_board.get_other_player():  # after making a move
            #  the turn changes so other player means winning
            self.mark_sequence()  # marking the winning discs
            self.__top_canvas.create_image(self.CANVAS185,
                                           self.CANVAS45,
                                           image=self.__win_file)
            self.__player_turn = False  # allowing no more turns
        elif self.__game_board.get_winner() == \
                self.__game_board.get_current_player():
            self.mark_sequence()
            self.__top_canvas.create_image(self.CANVAS185,
                                           self.CANVAS45,
                                           image=self.__lose_file)
            self.__player_turn = False
        elif self.__game_board.get_winner() == self.__game_board.DRAW:
            self.__top_canvas.create_image(self.CANVAS185,
                                           self.CANVAS45,
                                           image=self.__draw_file)
            self.__player_turn = False

    def callback(self, event):
        """the function bind to the event of pressing the down key in order
        to drop a disc. it updates the move on the game board and update the
        screen in accordance"""
        if self.__player_turn:  # allows the play only in turn
            try:
                self.__game_board.make_move(self.__arrow_point)  # making
                # the move according to the place of the arrow
                x, y = self.__game_board.get_last_disc()
                message = str(self.__arrow_point)
                # sending the message after making a move in order the
                # other player's board to update
                self.__communicator.send_message(message)
                # update the place in the  board chosen to the appropriate
                # color
                if self.__game_board.get_current_player():
                    self.__middle_frame.grid_slaves(
                        row=x, column=y)[0].itemconfig(self.TAG_OVAL,
                                                       fill=self.FILL_RED)
                else:
                    self.__middle_frame.grid_slaves(
                        row=x, column=y)[0].itemconfig(self.TAG_OVAL,
                                                       fill=self.FILL_GOLD)
                # now we update the indication of player_turn
                if not self.__game_board.get_winner():
                    if self.__game_board.get_current_player():
                        self.__bottom_canvas.itemconfig(self.TAG_PLAYER,
                                                        text=self.TEXT2,
                                                        fill=self.FILL_GOLD)
                    else:
                        self.__bottom_canvas.itemconfig(self.TAG_PLAYER,
                                                        text=self.TEXT1,
                                                        fill=self.FILL_GOLD)
                self.check_for_winner()
                self.__player_turn = False  # changing the turn
            except Exception:  # display a message for an instance when a
                # wrong column was chosen
                self.__bottom_canvas.create_text(self.CANVAS185,
                                                 self.CANVAS85,
                                                 text=self.TEXT5,
                                                 fill=self.FILL_RED,
                                                 tag=self.ILLEGAL_MSG,
                                                 font=(False, self.FONT18))
                self.__bottom_canvas.after(
                    self.MESSAGE_DISPLAY_TIMEOUT,
                    lambda: self.__bottom_canvas.delete(self.ILLEGAL_MSG))

    def ai_move(self, ai):
        """makes a move and update the screen automatically using the ai
        object gives as parameter"""
        if self.__player_turn:
            # the move is set according to the find_legal_move_func'
            ai.find_legal_move(self.__game_board, self.__game_board.make_move)
            # same as callback apart from the exception which handled in
            # 'find_legal_move'
            x, y = self.__game_board.get_last_disc()
            message = str(y)
            self.__communicator.send_message(message)
            if self.__game_board.get_current_player():
                self.__middle_frame.grid_slaves(row=x, column=y)[0].itemconfig(
                    self.TAG_OVAL, fill=self.FILL_RED)
            else:
                self.__middle_frame.grid_slaves(row=x, column=y)[0].itemconfig(
                    self.TAG_OVAL, fill=self.FILL_GOLD)
            if not self.__game_board.get_winner():
                if self.__game_board.get_current_player():
                    self.__bottom_canvas.itemconfig(self.TAG_PLAYER,
                                                    text=self.TEXT2,
                                                    fill=self.FILL_GOLD)
                else:
                    self.__bottom_canvas.itemconfig(self.TAG_PLAYER,
                                                    text=self.TEXT1,
                                                    fill=self.FILL_RED)
            self.check_for_winner()
            self.__player_turn = False

    def moves(self):
        """distinguish between a human and ai player and allows the moves
        accordingly. called in the init function"""
        if self.__player == self.TAG_HUMAN:
            self.create_arrow()
        else:
            ai = AI()  # creating the ai object for this running
            self.ai_move(ai)

    def create_arrow(self):
        """in charge of all the bindings while the player is human"""
        self.__top_canvas.create_image(self.CANVAS25,
                                       self.CANVAS85,
                                       image=self.__arrow,
                                       tag=self.TAG_ARROW)
        # creating the arrow first
        self.__top_canvas.bind(self.MOVE_RIGHT, self.move_arrow_right)
        self.__top_canvas.bind(self.MOVE_LEFT, self.move_arrow_left)
        self.__top_canvas.bind(self.MOVE_DOWN, self.callback)
        self.__top_canvas.register(self.__arrow)

    def move_arrow_right(self, event):
        """moves arrow to the right while pressing RIGHT"""
        if self.MIN_MOVE <= self.__arrow_point < self.MAX_MOVE:
            self.__arrow_point += self.CHANGE  # updates arrow location
            event.widget.move(self.TAG_ARROW, self.CHANGE_ARROW, self.CANVAS_0)

    def move_arrow_left(self, event):
        """moves arrow to the left while pressing LEFT"""
        if self.MIN_MOVE < self.__arrow_point <= self.MAX_MOVE:
            self.__arrow_point -= self.CHANGE
            event.widget.move(self.TAG_ARROW, -self.CHANGE_ARROW,
                              self.CANVAS_0)

    def mark_sequence(self):
        """using the winning_sec_lst variable of the game_board and marks
        the winning strait"""
        for coor in self.__game_board.get_winning_sec_lst():
            self.__middle_frame.grid_slaves(
                row=coor[0], column=coor[1])[0].config(bg=self.FILL_GREEN)

    def __handle_messege(self, text):
        """when receiving a message updates the game_board and screen in
        accordance and makes all the checks for winner like in callback"""
        if text:
            arrow_point = int(text)
            self.__game_board.make_move(arrow_point)
            x, y = self.__game_board.get_last_disc()
            if self.__game_board.get_current_player():
                self.__middle_frame.grid_slaves(row=x, column=y)[0].itemconfig(
                    self.TAG_OVAL, fill=self.FILL_RED)
            else:
                self.__middle_frame.grid_slaves(row=x, column=y)[0].itemconfig(
                    self.TAG_OVAL, fill=self.FILL_GOLD)
            if not self.__game_board.get_winner():
                if self.__game_board.get_current_player():
                    self.__bottom_canvas.itemconfig(self.TAG_PLAYER,
                                                    text=self.TEXT2,
                                                    fill=self.FILL_GOLD)
                else:
                    self.__bottom_canvas.itemconfig(self.TAG_PLAYER,
                                                    text=self.TEXT1,
                                                    fill=self.FILL_RED)
            # changing the terms of win and lose-opposite than callback
            if self.__game_board.get_winner() == \
                                      self.__game_board.get_current_player():
                self.mark_sequence()
                self.__top_canvas.create_image(self.CANVAS185,
                                               self.CANVAS45,
                                               image=self.__win_file)
                self.__player_turn = False
                return  # in order to freeze the game for the second player
            elif self.__game_board.get_winner() == \
                                        self.__game_board.get_other_player():
                self.mark_sequence()
                self.__top_canvas.create_image(self.CANVAS185,
                                               self.CANVAS45,
                                               image=self.__lose_file)
                self.__player_turn = False
                return
            elif self.__game_board.get_winner() == self.__game_board.DRAW:
                self.__top_canvas.create_image(self.CANVAS185,
                                               self.CANVAS45,
                                               image=self.__draw_file)
                self.__player_turn = False
                return
            self.__player_turn = True
            if self.__player == self.TAG_AI:  # in case its an ai the message
                # activates the moves func again
                self.moves()
Exemple #6
0
class GUI:
    """
    A class responsible for handling the GUI aspects of a for in a row game.
    Also handles the connection of the communicator
    """

    IMAGE_PATHS = [
        "images/empty_cell.png", "images/player1_disk.png",
        "images/player2_disk.png", "images/player1_disk_win.png",
        "images/player2_disk_win.png"
    ]
    FONT = ("times", 24)
    HUMAN = 0
    AI = 1
    MSG_COLOR = "blue"
    EMPTY_CELL_IMG = 0
    PLAYER1_CELL_IMG = 1
    PLAYER2_CELL_IMG = 2
    PLAYER1_WIN_DISK = 3
    PLAYER2_WIN_DISK = 4
    WINDOW_SIZE = 500
    BOARD_HEIGHT = 6
    BOARD_WIDTH = 7
    P1 = "Player 1"
    P2 = "Player 2"
    HOLD_MSG = "Please wait for %s to finnish their turn"
    DRAW = "No more moves. Game ended with a draw"
    WIN = "%s won!"
    DEFAULT_MSG = ""
    TOP = 0
    DROP_RATE = 100

    def __init__(self, root, player, port, ip=None):
        """
        Initialize GUI and connect the communicator
        :param root: The tkinter root
        :param player: An integer representing if the player is human or ai
        :param port: The port to connect to
        :param ip: The ip to connect to
        """
        self.__root = root
        self.__game = Game()
        self.__bottoms = self.__game.get_board().get_bottoms()
        self.__communicator = Communicator(root, port, ip)
        self.__communicator.connect()
        self.__communicator.bind_action_to_message(self.__handle_message)
        self.__top_frame = tk.Frame(self.__root)
        self.__top_frame.pack()
        self.__col_frames = []
        self.__buttons = []
        self.__player = GUI.P1
        self.__opponent = GUI.P2
        if ip is not None:
            self.__player, self.__opponent = self.__opponent, self.__player
        self.__msg_board = tk.Message(root, font=GUI.FONT, fg=GUI.MSG_COLOR)
        self.__msg_board.pack()
        self.__images = []
        self.__load_images()
        self.__place_widgets()
        self.__ai = None
        if player == GUI.AI:
            self.__ai = AI()
        self.__start_game()

    def __start_game(self):
        """
        Allow player one to make a move
        :return: None
        """
        if self.__player == GUI.P1:
            if self.__ai is not None:
                self.__ai_play()
            else:
                self.__change_buttons_state(tk.NORMAL)
        else:
            self.__update_msg(GUI.HOLD_MSG % self.__opponent)

    def __place_widgets(self):
        """
        Place widgets in the gui
        :return: None
        """
        for col in range(GUI.BOARD_WIDTH):
            col_frame = tk.Frame(self.__top_frame)
            col_frame.pack(side=tk.LEFT)
            self.__col_frames.append(col_frame)
            for row in range(GUI.BOARD_HEIGHT):
                cell = tk.Label(col_frame,
                                image=self.__images[GUI.EMPTY_CELL_IMG])
                cell.grid(row=row)
            col_button = tk.Button(
                col_frame,
                text=str(col + 1),
                command=lambda column=col: self.__play_col(column),
                state=tk.DISABLED)
            col_button.grid(row=GUI.BOARD_HEIGHT)
            self.__buttons.append(col_button)

    def __load_images(self):
        """
        Load all images necessary for the gui
        :return: None
        """
        images = []
        for path in GUI.IMAGE_PATHS:
            self.__images.append(tk.PhotoImage(file=path))
        return images

    def __play_col(self, col, report=True):
        """
        Make a move in a column
        :param col: The column's index
        :return: None
        """
        self.__update_msg(GUI.DEFAULT_MSG)
        self.__change_buttons_state(tk.DISABLED)
        player = self.__game.get_current_player()
        frame = self.__col_frames[col]
        if self.__game.get_current_player() == Game.PLAYER_ONE:
            new_img = self.__images[GUI.PLAYER1_CELL_IMG]
        else:
            new_img = self.__images[GUI.PLAYER2_CELL_IMG]
        self.__game.make_move(col)
        bottom = self.__bottoms[col] + 1
        if bottom == GUI.TOP:
            self.__buttons[col] = None
        for cell_index in range(bottom):
            cell = frame.grid_slaves(row=cell_index)[0]
            self.__disk_through_cell(cell, player)
        last_cell = frame.grid_slaves(row=bottom)[0]
        last_cell.configure(image=new_img)
        winner = self.__game.get_winner()
        if report:
            self.__communicator.send_message(col)
            if winner is None:
                self.__update_msg(GUI.HOLD_MSG % self.__opponent)
        if winner is not None:
            self.__handle_win(winner)

    def __update_msg(self, msg):
        """
        Update the message widget with a new message
        :param msg: A string
        :return: None
        """
        self.__msg_board.configure(text=msg)

    def __handle_win(self, winner):
        """
        Change the board to fit the end state of the game
        :param winner: An integer representing the winner of the game
        :return: None
        """
        self.__change_buttons_state(tk.DISABLED)
        self.__buttons = []
        if winner == Game.DRAW:
            self.__update_msg(GUI.DRAW)
            return
        elif winner == Game.PLAYER_ONE:
            msg = GUI.WIN % GUI.P1
            img = self.__images[GUI.PLAYER1_WIN_DISK]
        else:
            msg = GUI.WIN % GUI.P2
            img = self.__images[GUI.PLAYER2_WIN_DISK]
        for cell in self.__game.get_wining_streak():
            row, col = cell
            frame = self.__col_frames[col]
            cell_widget = frame.grid_slaves(row=row)[0]
            cell_widget.configure(image=img)
        self.__update_msg(msg)

    def __handle_message(self, move):
        """
        Specifies the event handler for the message getting event in the
        communicator. Makes a move when invoked
        :param move: A string representing a single move in the game
        :return: None
        """
        move = int(move)
        self.__play_col(move, report=False)
        if self.__ai is not None:
            self.__ai_play()
        else:
            self.__change_buttons_state(tk.NORMAL)

    def __ai_play(self):
        """
        Make a play with the ai
        :return: None
        """
        if self.__game.get_winner() is None:
            self.__ai.find_legal_move(self.__game, self.__play_col)

    def __change_buttons_state(self, state):
        """
        Change all relevant buttons' state
        :param state: Either normal or disabled
        :return: None
        """
        for button in self.__buttons:
            try:
                button.configure(state=state)
            except AttributeError:
                continue

    def __disk_through_cell(self, cell, player=None):
        """
        Animate a disk going through an empty cell
        :param cell: The cell
        :param player: The player whose disk is dropping
        :return: None
        """
        if player is not None:
            if player == Game.PLAYER_ONE:
                cell.configure(image=self.__images[GUI.PLAYER1_CELL_IMG])
            else:
                cell.configure(image=self.__images[GUI.PLAYER2_CELL_IMG])
            cell.update()
            cell.after(GUI.DROP_RATE, self.__disk_through_cell(cell))
        else:
            cell.configure(image=self.__images[GUI.EMPTY_CELL_IMG])
Exemple #7
0
class Gui():
    def __init__(self, root, port, player, ip=None, ai=None):
        self.root = root
        self.player = player
        self.game = Game()

        self.ai = ai

        self.__communicator = Communicator(root, port, ip)
        self.__communicator.connect()
        self.__communicator.bind_action_to_message(self.__handle_message)

        self.pattern = tk.PhotoImage(file=PATTERN_IMAGE)
        self.red = tk.PhotoImage(file=RED_TOKEN_IMAGE)
        self.blue = tk.PhotoImage(file=BLUE_TOKEN_IMAGE)
        self.player_one = tk.PhotoImage(file=PLAYER_ONE_IMAGE)
        self.player_two = tk.PhotoImage(file=PLAYER_TWO_IMAGE)
        self.blue_win = tk.PhotoImage(file=BLUE_WIN_IMAGE)
        self.red_win = tk.PhotoImage(file=RED_WIN_IMAGE)
        self.no_win = tk.PhotoImage(file=NO_WINNER_IMAGE)
        self.illegal = tk.PhotoImage(file=ILLEGAL_ACTION_IMAGE)
        self.wait = tk.PhotoImage(file=WAIT_IMAGE)
        self.win_symbol = tk.PhotoImage(file=WIN_SYMBOL_IMAGE)

        self.canvas = tk.Canvas(self.root,
                                width=CANVAS_WIDTH,
                                height=CANVAS_HEIGHT)
        self.canvas.pack()
        self.canvas.create_image(0, 0, anchor=tk.NW, image=self.pattern)

        self.canvas.bind('<Button-1>', self.callback)
        if ip is None and self.ai:
            column = self.ai.find_legal_move(self.game, self.update)
            self.__communicator.send_message(column)

    def draw_coin(self, row, column, player):
        """
        This function draw the tokens in the board, and assigns one to each
        player
        """

        if player == 0:
            coin = self.blue
            self.player_b = self.canvas.create_image(57,
                                                     140,
                                                     image=self.player_two)
            self.player_a = self.canvas.create_image(744,
                                                     140,
                                                     image=self.player_one)
        else:
            coin = self.red
            self.canvas.delete(self.player_a)
            self.canvas.delete(self.player_b)

        self.canvas.create_image(FIRST_ROW + (column * COLUMN_STEP),
                                 FIRST_COLUMN + (row * ROW_STEP),
                                 image=coin)

    def check_winner(self):
        winner = self.game.get_winner()
        if winner == self.game.PLAYER_ONE:
            blue_win = self.canvas.create_image(379, 300, image=self.blue_win)
            self.canvas.after(1500, self.delete_msg, blue_win)
        elif winner == self.game.PLAYER_TWO:
            red_win = self.canvas.create_image(379, 300, image=self.red_win)
            self.canvas.after(1500, self.delete_msg, red_win)
        elif winner == self.game.DRAW:
            self.canvas.create_image(400, 300, image=self.no_win)
        if winner is not None and winner != self.game.DRAW:
            self.canvas.after(1500, self.design_win)

    def design_win(self):
        direction, coord = self.game.found_winner()
        if direction == 'h':
            self.horizontal_win(coord)
        if direction == 'v':
            self.vertical_win(coord)
        if direction == 'd':
            self.diagonal_win(coord)

    def horizontal_win(self, coord):
        for i in range(coord[1], coord[1] - 4, -1):
            self.canvas.create_image(FIRST_ROW + i * COLUMN_STEP,
                                     FIRST_COLUMN + coord[0] * ROW_STEP,
                                     image=self.win_symbol)

    def vertical_win(self, coord):
        for i in range(coord[1], coord[1] - 4, -1):
            self.canvas.create_image(FIRST_ROW + coord[0] * COLUMN_STEP,
                                     FIRST_COLUMN + i * ROW_STEP,
                                     image=self.win_symbol)

    def diagonal_win(self, coord):
        for coord_tup in coord:
            self.canvas.create_image(FIRST_ROW + coord_tup[1] * COLUMN_STEP,
                                     FIRST_COLUMN + coord_tup[0] * ROW_STEP,
                                     image=self.win_symbol)

    def put_disk(self, column_coord):
        if column_coord > 134:
            column = (column_coord - 134) // COLUMN_STEP
            self.update(column)
            self.__communicator.send_message(column)
        else:
            raise Exception(self.game.ILLEGAL_MOVE)

    def delete_msg(self, illegal_image):
        self.canvas.delete(illegal_image)

    def callback(self, event):
        if self.game.get_winner() is None:
            if self.player == self.game.get_current_player():
                try:
                    self.put_disk(event.x)
                except Exception:
                    illegal_image = self.canvas.create_image(
                        400, 300, image=self.illegal)
                    self.canvas.after(MSG_TIME, self.delete_msg, illegal_image)
            else:
                wait_image = self.canvas.create_image(400,
                                                      300,
                                                      image=self.wait)
                self.canvas.after(MSG_TIME, self.delete_msg, wait_image)

    def update(self, column):
        row, cur_player = self.game.make_move(column)
        self.draw_coin(row, column, cur_player)
        self.check_winner()

    def __handle_message(self, msg):

        self.update(int(msg))

        if self.ai and self.game.get_winner() is None:
            column = self.ai.find_legal_move(self.game, self.update)
            self.__communicator.send_message(column)
Exemple #8
0
class GUI:
    """
    The GUI of the battle ship game and run the game.
    """
    def __init__(self, root, game, port, ip=None):
        """
        :param root: tkinter root.
        :param game: the game object.
        :param port: for connection.
        :param ip: for connection.
        """
        self._root = root
        self.__game = game
        self.__state = CONNECTING
        self.__enemy_tiles = []
        self.__self_tiles = []
        self.__ship_dir = Board.H_DIR
        self.__communicator = Communicator(self._root, port, ip)
        self.__communicator.connect()
        self.__random_player = (player_type == LEGAL_PLAYER[1])
        self.__random_possible_targets = [(x, y) for x in range(NUM_OF_COL)
                                          for y in range(NUM_OF_ROW)]
        self.__communicator.bind_action_to_message(self.__handle_message)
        self.__place_widgets()
        self.__check_connection()

    def __place_widgets(self):
        """
        place the widgets on screen.
        """
        # background
        self.__background_img = tk.PhotoImage(file=BACKGROUND_IMG)
        self.__tile_img = tk.PhotoImage(file=TILE_IMG)
        self._canvas = tk.Canvas(self._root, width=WINDOW_W, height=WINDOW_H)
        self._canvas.create_image((0, 0),
                                  anchor=tk.NW,
                                  image=self.__background_img)
        self._canvas.pack(side=tk.TOP)
        # massage display
        self.__display_msg = self._canvas.create_text(MSG_DISPLAY_CORD,
                                                      font=("Jockey One", 20),
                                                      fill="white",
                                                      text=CONNECTING_MSG)
        # enemy board
        for x in range(NUM_OF_COL):
            for y in range(NUM_OF_ROW):
                self.__enemy_tiles.append(EnemyTile(x, y, self._root, self))
        # player board
        for x in range(NUM_OF_COL):
            for y in range(NUM_OF_ROW):
                self.__self_tiles.append(SelfTile(x, y, self._root, self))

    def __fix_display_msg(self):
        """
        replace the shown massage to the correct one.
        """
        if self.__state == CONNECTING:
            self._canvas.itemconfigure(self.__display_msg, text=CONNECTING_MSG)
        elif self.__state == SETTING_UP:
            self._canvas.itemconfigure(self.__display_msg, text=SETTING_UP_MSG)
        elif self.__state == WAITING_FOR_OPPONENT:
            self._canvas.itemconfigure(self.__display_msg,
                                       text=WAITING_FOR_OPPONENT_MSG)
        elif self.__state == PLAYER_TURN:
            self._canvas.itemconfigure(self.__display_msg,
                                       text=PLAYER_TURN_MSG)
        elif self.__state == OPPONENT_TURN:
            self._canvas.itemconfigure(self.__display_msg,
                                       text=OPPONENT_TURN_MSG)
        elif self.__state == GAME_OVER:
            if self.__game.get_winner() == player_num:
                self._canvas.itemconfigure(self.__display_msg, text=WIN_MSG)
            else:
                self._canvas.itemconfigure(self.__display_msg, text=LOSE_MSG)

    def __check_connection(self):
        """
        check if connection was established and start the game. if not,
        check again after a period of time.
        """
        if self.__communicator.is_connected():
            self.__state = SETTING_UP
            self.__enemy_placed_ships = 0
            self.__ship_index = 0
            self._canvas.itemconfigure(self.__display_msg,
                                       text=STARTING_GAME_MSG)
            self._root.after(LONGER_WAIT_PERIOD, self.__fix_display_msg)
            self._root.bind("<ButtonPress-3>", self.__switch_v_h)
            self._root.bind("<space>", self.__switch_v_h)
            if self.__random_player:
                self._root.after(WAIT_PERIOD, self.__random_place_ship)
        else:
            self._root.after(WAIT_PERIOD, self.__check_connection)

    def __random_place_ship(self):
        """
        placing a ship for a random player.
        """
        self.__ship_dir = choice([Board.V_DIR, Board.H_DIR])
        self.player_place_a_ship(randint(0, NUM_OF_ROW - 1),
                                 randint(0, NUM_OF_COL - 1))

    def player_place_a_ship(self, row, col):
        """
        placing a ship on the player board.
        :param row: the chosen row.
        :param col: the chosen column.
        """
        if self.__game.set_a_ship(player_num, col, row,
                                  SHIPS_SIZES[self.__ship_index],
                                  self.__ship_dir):
            self.__communicator.send_message(
                "%d,%d,%d," % (col, row, SHIPS_SIZES[self.__ship_index]) +
                self.__ship_dir)
            self.__create_a_ship(SELF, col, row,
                                 SHIPS_SIZES[self.__ship_index],
                                 self.__ship_dir)
            self.__ship_index += 1
            if self.__ship_index == len(SHIPS_SIZES):
                self.__state = WAITING_FOR_OPPONENT
                self.__fix_display_msg()
                self.__start_game()

            elif self.__random_player:
                self._root.after(LONGER_WAIT_PERIOD, self.__random_place_ship)
        else:
            self._canvas.itemconfigure(self.__display_msg,
                                       text=WRONG_SHIP_SETTING_MSG)
            self._root.after(LONGER_WAIT_PERIOD, self.__fix_display_msg)
            if self.__random_player:
                self._root.after(LONGER_WAIT_PERIOD, self.__random_place_ship)

    def __create_a_ship(self, player, col, row, length, direction):
        """
        creating a new ship on board.
        :param player: SELF or OPPONENT
        :param col: integer.
        :param row: integer.
        :param length: integer.
        :param direction: Board.H_DIR or Board.V_DIR
        """
        for i in range(length):
            current_tile = self.get_tile(row, col, player)
            if i == 0:
                if direction == Board.H_DIR:
                    current_tile.add_ship(SHIP_FRONT_H)
                elif direction == Board.V_DIR:
                    current_tile.add_ship(SHIP_FRONT_V)
            elif i == length - 1:
                if direction == Board.H_DIR:
                    current_tile.add_ship(SHIP_BOTTOM_H)
                elif direction == Board.V_DIR:
                    current_tile.add_ship(SHIP_BOTTOM_V)
            else:
                if direction == Board.H_DIR:
                    current_tile.add_ship(SHIP_MIDDLE_H)
                elif direction == Board.V_DIR:
                    current_tile.add_ship(SHIP_MIDDLE_V)
            if player == SELF:
                current_tile.show_ship()
            if direction == Board.H_DIR:
                col += 1
            if direction == Board.V_DIR:
                row += 1

    def __switch_v_h(self, event):
        """
        switch between placing a ship horizontally or vertically.
        """
        if self.__ship_dir == Board.H_DIR:
            self.__ship_dir = Board.V_DIR
        elif self.__ship_dir == Board.V_DIR:
            self.__ship_dir = Board.H_DIR

    def __start_game(self):
        """
        start the game after the ships where placed.
        """
        if self.__enemy_placed_ships == len(SHIPS_SIZES) and\
                self.__ship_index == len(SHIPS_SIZES):
            self.__game.start_game()
            if player_num == Game.PLAYER1:
                self.__state = PLAYER_TURN
                if self.__random_player:
                    self._root.after(LONGER_WAIT_PERIOD,
                                     self.__play_a_random_turn)
            elif player_num == Game.PLAYER2:
                self.__state = OPPONENT_TURN
            self.__fix_display_msg()

    def __play_a_random_turn(self):
        """
         plays a random turn for a random player.
        """
        location = choice(self.__random_possible_targets)
        self.__random_possible_targets.remove(location)
        self.play_a_turn(location[0], location[1])

    def play_a_turn(self, col, row):
        """
        plays a turn based on a chosen target.
        :param row: the chosen row.
        :param col: the chosen column.
        """
        if self.__state == PLAYER_TURN:
            coords_got_hit = self.__game.play_a_turn(col, row)
            if len(coords_got_hit) == 0:
                self.get_tile(row, col, OPPONENT).got_a_hit()
                self._canvas.itemconfigure(self.__display_msg, text=MISS_MSG)
                self.__state = OPPONENT_TURN
                self._root.after(LONGER_WAIT_PERIOD, self.__fix_display_msg)
            else:
                self.get_tile(row, col, OPPONENT).got_a_hit()
                if len(coords_got_hit) == 1:
                    self._canvas.itemconfigure(self.__display_msg,
                                               text=SUCCESS_MSG)
                else:
                    for c in coords_got_hit:
                        self.get_tile(c[1], c[0], OPPONENT).show_ship()
                    self._canvas.itemconfigure(self.__display_msg,
                                               text=DROWNING_OPPONENT_SHIP_MSG)
                if self.__random_player:
                    self._root.after(LONGER_WAIT_PERIOD,
                                     self.__play_a_random_turn)
            self.__communicator.send_message("%d,%d" % (col, row))
            if self.__game.game_over():
                self.__state = GAME_OVER
                self.__game_over()

    def state(self):
        """
        :return: integer representing the current state of the game.
        """
        return self.__state

    def get_tile(self, row, col, player):
        """
        gets the tile object based on coordinates and player.
        :param row: the chosen row.
        :param col: the chosen column.
        :param player: self or opponent.
        :return: tile object.
        """
        if 0 <= col < NUM_OF_COL and 0 <= row < NUM_OF_ROW:
            if player:
                return self.__enemy_tiles[row + col * NUM_OF_ROW]
            else:
                return self.__self_tiles[row + col * NUM_OF_ROW]

    def __handle_message(self, text=None):
        """
        handle incoming massages from the other side of the network.
        :param text: the incoming massage.
        """
        if text:
            if (self.__state == SETTING_UP or
                    self.__state == WAITING_FOR_OPPONENT) and\
                    self.__enemy_placed_ships < len(SHIPS_SIZES):
                opponent_move = text.split(",")
                if player_num == game.PLAYER1:
                    opponent = game.PLAYER2
                else:
                    opponent = game.PLAYER1
                self.__game.set_a_ship(opponent, int(opponent_move[0]),
                                       int(opponent_move[1]),
                                       int(opponent_move[2]), opponent_move[3])
                self.__create_a_ship(OPPONENT, int(opponent_move[0]),
                                     int(opponent_move[1]),
                                     int(opponent_move[2]), opponent_move[3])
                self.__enemy_placed_ships += 1
                if self.__enemy_placed_ships == len(SHIPS_SIZES):
                    self.__start_game()
            elif self.__state == OPPONENT_TURN:
                opponent_move = text.split(",")
                coords_got_hit = self.__game.play_a_turn(
                    int(opponent_move[0]), int(opponent_move[1]))
                self.get_tile(int(opponent_move[1]), int(opponent_move[0]),
                              SELF).got_a_hit()
                if len(coords_got_hit) == 1:
                    self._canvas.itemconfigure(self.__display_msg,
                                               text=HIT_MSG)
                elif len(coords_got_hit) > 1:
                    self._canvas.itemconfigure(self.__display_msg,
                                               text=LOSE_A_SHIP_MSG)
                else:
                    self.__state = PLAYER_TURN
                    self._canvas.itemconfigure(self.__display_msg,
                                               text=PLAYER_TURN_MSG)
                    if self.__random_player:
                        self.__play_a_random_turn()
                if self.__game.game_over():
                    self.__state = GAME_OVER
                    self.__game_over()

    def __game_over(self):
        """
        handle end of game.
        """
        self.__fix_display_msg()
        if self.__game.get_winner() != player_num:
            if player_num == game.PLAYER1:
                opponent = game.PLAYER2
            else:
                opponent = game.PLAYER1
            list_of_cords = self.__game.get_all_ships_cord(opponent)
            for i in list_of_cords:
                self.get_tile(i[1], i[0], True).show_ship()
Exemple #9
0
class AI:
    def __init__(self, player, game, port, ip=None):
        self.game = game
        self.canvas = game.canvas
        self.player = player
        self.__communicator = Communicator(self.canvas, port, ip)
        self.__communicator.connect()
        self.__communicator.bind_action_to_message(self.read_message)
        self.difficulty = DIFFICULTY
        if self.player == PLAYER_1:
            self.color = 'b'
            if self.game.turn_counter % 2 == 0:
                self.game.make_move(3)
                self.__communicator.send_message(str(3))
                self.game.player2_message = 0
                self.game.mylist_player2 = []
                self.game.waiting_player_2()
        else:
            self.color = 'r'
        self.counter = 0
        self.b_column_counter = 0
        self.b_counter = 0
        self.canvas.bind("<Button-1>", self.illegal_move)
        self.b_illegal_counter = 0
        self.canvas.mainloop()

    def read_message(self, text=None):
        if text:
            if len(text) == 1:
                column = int(text)
                self.game.make_move(column)
                if self.color == 'b':
                    self.game.player2_message = 1
                if self.color == 'r':
                    self.game.player1_message = 1
                self.find_legal_move(self.game, func=None)
            else:
                text_list = []
                for i in range(len(text)):
                    if text[i].is_numeric():
                        text_list.append(text[i])
                if len(text_list) > 0:
                    column = int(text_list[0])
                    self.game.make_move(column)
                    if self.color == 'b':
                        self.game.player2_message = 1
                    if self.color == 'r':
                        self.game.player1_message = 1
                    self.find_legal_move(self.game, func=None)

    def illegal_move(self, event):
        if self.b_illegal_counter < 1:
            b = Button(text="Illegal move", height=4, width=20)
            self.b_illegal_counter += 1

            def forget():
                b.place_forget()
                self.b_illegal_counter = 0

            b.place(relx=0.4, rely=0.435)
            b.config(command=forget)

    def find_legal_move(self, g, func, timeout=None):
        if g.win_status == 0:
            if timeout == None:
                try:
                    column = self.get_ai_move(g.new_board,
                                              DIFFICULTY,
                                              func=None)
                    g.make_move(column)
                    self.__communicator.send_message(str(column))
                    if self.color == 'b':
                        g.player2_message = 0
                        g.mylist_player2 = []
                        g.waiting_player_2()
                    else:
                        g.player1_message = 0
                        g.mylist_player1 = []
                        g.waiting_player_1()
                except IndexError:
                    self.no_move()
            else:
                column = self.get_ai_move(g.new_board, DIFFICULTY, func)
                g.make_move(column)
                self.__communicator.send_message(str(column))

    def get_ai_move(self, board, depth, func):
        """ Returns the best move (as a column number) and the associated alpha
            Calls search()
        """
        # determine opponent's color
        if self.color == 'b':
            enemy_tile = 'r'
        else:
            enemy_tile = 'b'
        # enumerate all legal moves
        legal_moves = {}  # will map legal move states to their alpha values
        for col in range(BOARDWIDTH):
            # if column i is a legal move...
            if self.move_check(board, col):
                # make the move in column 'col' for curr_player
                temp = self.make_a_move(board, self.color, col)
                legal_moves[col] = -self.search(depth - 1, temp, enemy_tile)
                max_move = max(legal_moves.values())
                if func != None:
                    func(
                        list(legal_moves.keys())[list(
                            legal_moves.values()).index(max_move)])

        best_alpha = -99999999
        best_move = []
        moves = legal_moves.items()
        for move, alpha in moves:
            if alpha > best_alpha:
                best_alpha = alpha
                best_move = []
                best_move.append(move)
            if alpha == best_alpha:
                best_move.append(move)
        if len(best_move) > 1:
            return random.choice(best_move)
        if len(best_move) == 1:
            return best_move[0]
        else:
            raise IndexError

    def search(self, depth, board, tile):
        """ Searches the tree at depth 'depth'

            Returns the alpha value
        """

        # enumerate all legal moves from this state
        legal_moves = []
        for column in range(BOARDWIDTH):
            # if column is a legal move...
            if self.move_check(board, column):
                # make the move in column for curr_player
                temp = self.make_a_move(board, tile, column)
                legal_moves.append(temp)

        # if this node (state) is a terminal node or depth == 0...
        if depth == 0 or len(legal_moves) == 0 or self.board_full_check(board):
            # return the heuristic value of node
            return self.value(board, tile)

        # determine opponent's color
        if tile == 'b':
            enemy_tile = 'r'
        else:
            enemy_tile = 'b'

        alpha = -99999999
        for child in legal_moves:
            try:
                if child != None:
                    alpha = max(alpha,
                                -self.search(depth - 1, child, enemy_tile))
                else:
                    raise TypeError
            except TypeError:
                self.no_move()
        return alpha

    def value(self, board, tile):
        if tile == 'b':
            enemy_tile = 'r'
        else:
            enemy_tile = 'b'
        my_fours = self.check_for_streak(board, tile, 4)
        my_threes = self.check_for_streak(board, tile, 3)
        my_twos = self.check_for_streak(board, tile, 2)
        enemy_fours = self.check_for_streak(board, enemy_tile, 4)
        enemy_threes = self.check_for_streak(board, enemy_tile, 3)
        enemy_twos = self.check_for_streak(board, enemy_tile, 2)
        if enemy_fours > 0:
            return -100000 - DIFFICULTY
        else:
            return (my_fours*100000 + my_threes*100 + my_twos*10) - \
               (enemy_threes*100 + enemy_twos*10)\
               + DIFFICULTY

    def check_for_streak(self, board, color, streak):
        count = 0
        # for each piece in the board...
        for i in range(BOARDWIDTH):
            for j in range(BOARDHEIGHT - 1, -1, -1):
                # ...that is of the color we're looking for...
                if board[i][j] == color:
                    # check if a vertical streak starts at (i, j)
                    count += self.vertical_streak(j, i, board, streak)

                    # check if a horizontal four-in-a-row starts at (i, j)
                    count += self.horizontal_streak(j, i, board, streak)

                    # check if a diagonal (either way) four-in-a-row starts at (i, j)
                    count += self.diagonal_check(j, i, board, streak)

        # return the sum of streaks of length 'streak'
        return count

    def vertical_streak(self, row, col, board, streak):
        consecutive_count = 0
        for i in range(streak):
            if row - i < 0:
                break
            if board[col][row] == board[col][row - i]:
                consecutive_count += 1
        if consecutive_count >= streak:
            return 1
        else:
            return 0

    def horizontal_streak(self, row, col, board, streak):
        consecutive_count = 0
        for i in range(streak):
            if col + i >= BOARDWIDTH:
                break
            if board[col][row] == board[col + i][row]:
                consecutive_count += 1
        if consecutive_count >= streak:
            return 1
        else:
            return 0

    def diagonal_check(self, row, col, board, streak):

        total = 0
        # check for diagonals with positive slope
        consecutive_count = 0
        for i in range(streak):  # check the first 4 columns
            if col + i > BOARDWIDTH - 1 or row - i < 0:
                break
            if board[col][row] == board[col + i][row - i]:
                consecutive_count += 1
        if consecutive_count >= streak:
            total += 1
        # check for diagonals with negative slope
        consecutive_count = 0
        for i in range(streak):
            if col + i > BOARDWIDTH - 1 or row + i > BOARDHEIGHT - 1:
                break
            if board[col][row] == board[col + i][row + i]:
                consecutive_count += 1
        if consecutive_count >= streak:
            total += 1

        return total

    def make_a_move(self, board, player, column):
        practice_board = copy.deepcopy(board)
        lowest = self.get_lowest_empty_space(practice_board, column)
        if lowest != -1:
            practice_board[column][lowest] = player
        return practice_board

    def get_lowest_empty_space(self, board, column):
        # Return the row number of the lowest empty row in the given column.
        for y in range(BOARDHEIGHT - 1, -1, -1):
            if board[column][y] == 'e':
                return y
        return -1

    def board_full_check(self, board):
        # Returns True if there are no empty spaces anywhere on the board.
        for x in range(BOARDWIDTH):
            if board[x][0] == 'e':
                return False
        return True

    def move_check(self, board, column):
        # Returns True if there is an empty space in the given column.
        # Otherwise returns False.
        if column < 0 or column >= (BOARDWIDTH) or board[column][0] != 'e':
            return False
        return True

    def no_move(self):
        """
        a graphic representations of an illegal move
        :return:
        """
        if self.b_counter < 1:
            self.b = Button(text="No possible AI moves", height=4, width=20)
            self.b_counter += 1

            def forget():
                self.b.place_forget()
                self.b_column_counter = 0

            self.b.place(relx=0.4, rely=0.435)
            self.b.config(command=forget)
class FourInARow:
    """Class representing a game of connect-4 with graphics"""
    def __init__(self, parent, player, port, ip=None):
        """Instructor of FourInARow object"""
        self._end_game = False
        self.__init_graphics__()
        self._game = Game()
        self._root = parent
        self._status = None
        self._player = player
        self.__init__ai_or_human()
        self.__init__ip_distinguisher(ip)
        self.__communicator = Communicator(self._root, port, ip)
        self.__communicator.connect()
        self.__communicator.bind_action_to_message(self.__handle_message)

    def __init_graphics__(self):
        """initiates the graphic of the game"""
        self._player1_disc = PhotoImage(file=PLAYER_ONE_DISK)
        self._player2_disc = PhotoImage(file=PLAYER_TWO_DISK)
        self._player1_turn = PhotoImage(file=PLAYER_ONE_TURN)
        self._player2_turn = PhotoImage(file=PLAYER_TWO_TURN)
        self._win_disc = PhotoImage(file=WIN_DISC)
        self._player1_won = PhotoImage(file=PLAYER_ONE_WIN_SCREEN)
        self._player2_won = PhotoImage(file=PLAYER_TWO_WIN_SCREEN)
        self._draw_screen = PhotoImage(file=DRAW_SCREEN)

    def __init__ai_or_human(self):
        """initiates the type of player"""
        if self._player == HUMAN_PLAYER:
            self.__init__new_canvas(BOARD)
            self._canvas.bind("<Button-1>", self.game_screen_callback)
        if self._player == AI_PLAYER:
            self.__init__new_canvas(BOARD)
            self._ai = AI()
            self._root.after(1, self.make_ai_move)

    def __init__ip_distinguisher(self, ip):
        """initiates the player num whether ip is None or not"""
        if ip is not None:
            self._player_num = self._game.PLAYER_TWO
        else:
            self._player_num = self._game.PLAYER_ONE

    def __init__new_canvas(self, img):
        """this method receives an image initiates a new canvas with it."""
        self._background = PhotoImage(file=img)
        self._canvas = Canvas(self._root,
                              height=BACKGROUND_HEIGHT,
                              width=BACKGROUND_WIDTH)
        self._canvas.create_image(3, 3, image=self._background, anchor=NW)
        self._canvas.pack()

    def make_ai_move(self):
        """makes a move for an ai player"""
        if not self._end_game:
            if self._game.get_current_player() == self._player_num:
                col = self._ai.find_legal_move(self._game, self.general_move)
                self.__communicator.send_message(str(col))
            self._root.after(1, self.make_ai_move)

    def __handle_message(self, text=None):
        """this method receives text that represent a column-index and
        operates general_move with this column."""
        if text:
            column = int(text)
            self.general_move(column)

    def game_screen_callback(self, event):
        """the callback method for the game-screen.
        The method receives an event and operates other func whether the
        event was under certain conditions or not"""

        # numbers in this function represent coordinates on the screen only!

        # if self._game.get_current_player() != self._player_num:
        #     return
        x = event.x
        y = event.y
        for col in range(game.COLUMNS):
            if x in range(39 + col * 63, 86 + col * 63) and 26 < y < 447:
                if self.general_move(col):
                    self.__communicator.send_message(str(col))

    def general_move(self, column):
        """this is the general method for making moves in the game.
        It receives a column-index and inserts the current player's disc in
        this column (in the game's board and in the graphic screen as well).
        If something went wrong during the process an exception is raised."""
        self._game.make_move(column)
        row_counter = 0
        for i in range(len(self._game.get_board()) - 1, -1, -1):
            if self._game.get_board()[i][column] == game.board.FREE_SPACE:
                break
            row_counter += 1
        self.add_disc(column, game.ROWS - row_counter)
        return True

    def add_disc(self, col, row):
        """adds a current player's graphic disc to the screen."""

        # numbers in this function represent coordinates on the screen only!

        if self._game.get_current_player() == Game.PLAYER_ONE:
            self._canvas.create_image(64 + 64 * col,
                                      70 + 56.5 * row,
                                      image=self._player1_disc)
            self._canvas.create_image(559, 211, image=self._player1_turn)
        else:
            self._canvas.create_image(64 + 64 * col,
                                      70 + 56.5 * row,
                                      image=self._player2_disc)
            self._canvas.create_image(559, 211, image=self._player2_turn)
        self.game_status()

    def game_status(self):
        """checks for the game status. Whether one of the players won or its a
        draw, and operates other methods according to the status."""
        self._status = self._game.get_winner()
        if self._status[0] in [self._game.PLAYER_ONE, self._game.PLAYER_TWO]:
            self.show_winner(self._status[0], self._status[1])
            self._canvas.bind("<Button-1>", self.exit_game)
            self._end_game = True
        if self._status[0] == self._game.DRAW:
            self._canvas.create_image(3, 3, image=self._draw_screen, anchor=NW)
            self._canvas.bind("<Button-1>", self.exit_game)
            self._end_game = True

    def show_winner(self, winner, win_discs_list):
        """if a winner was found in the game status method, this method
        show's the winner's discs that made a sequence and the winner player
        himself."""

        # numbers in this function represent coordinates on the screen only!

        for disc in win_discs_list:
            row, col = disc
            self._canvas.create_image(64 + 64 * col,
                                      70 + 56.5 * row,
                                      image=self._win_disc)
        if winner == self._game.PLAYER_ONE:
            self._canvas.create_image(3, 3, image=self._player1_won, anchor=NW)
        else:
            self._canvas.create_image(3, 3, image=self._player2_won, anchor=NW)

    def exit_game(self, event):
        """this method ends the game (including graphics)."""
        if event:
            self._root.quit()
            self._root.destroy()
class Gui:
    """
    This class is in charge of the graphics of the game, and operating it as
     well. It changes the graphic board accordingly to the changes in the
     Game object (a board dictionary).
    """
    TIE_SCORE = "It's a tie!"
    OPEN_MESSAGE = 'WELCOME!'
    WINNER_MESAGGE = 'YOU WON!'
    LOSER_MESSAGE = 'YOU LOST!'
    COL_0_COORD = 77
    COL_FACTOR = 90
    ROW_0_COORD = 557
    ROW_FACTOR = 67
    INDICATOR = 'column'
    TAG = 'player'
    PLAY = 'Your turn'
    DONT_PLAY = "Oppenent's turn"
    ON = 1
    OFF = 0
    INDICATOR_ROW = -1
    CANVAS_WID = 700
    CANVAS_HIGHT = 600
    ILLEGAL_TAG = 'illegal'
    OUT_OF_BOARD = 50
    TIME_OF_ILLEGAL_MOVE = 250
    WELCOME_FONT_SIZE = 50
    MAIN_TITLE_FONT_SIZE = 50
    SECOND_TITLE_FONT_SIZE = 30
    CENTER_WIDTH = 350
    PINEAPPLE_IMG = 'pineapple.png'
    COCONUT_IMG = 'coconut1.png'
    BACKGROUND_IMG = 'background.png'
    WINNER_IMG = 'like.png'
    ILLEGAL_IMG = 'illegal_move.png'
    COL_START = 31
    FACTOR = 91

    def __init__(self, root, ip, port, server, is_human):
        """
        the game starts with a board (from class Game), creates canvas with
        all the images needed, and starts a communicator object
        :param root: the Tk 'parent' object
        :param ip: servers ip
        :param port: the game port
        :param server: True or False
        """
        self.__board = Game()
        self.__root = root
        self.__is_human = is_human
        self.__coconut_img, self.__pineapple_img, self.__background_img, \
        self.__winner_img, self.__illegal_img = self.create_images()
        self.__col_lst = []  # used for the indicator
        self.__indicator = self.OFF  # a 'switch' for the indicator
        self.__illegal_sign = self.OFF  # counter for illegal move sign
        self.__canvas, self.__title, self.__second_title = self.create_canvas(
            self.__background_img)
        self.__communicator = Communicator(root, port, ip)
        self.__communicator.connect()
        self.__communicator.bind_action_to_message(self.__handle_message)
        self.__is_playing = server
        if self.__is_human == COMPUTER:
            self.__ai = AI()
        self.initialization()

    def initialization(self):
        """
        initials the players. for human player - activates his mouse,
        for ai player - starting to play
        """
        if player_type == COMPUTER and is_server:
            self.play_with_comp()
        if player_type == HUMAN:
            self.__canvas.bind("<Button-1>", self.callback)
            self.__canvas.bind("<Motion>", self.motion)

    def create_images(self):
        """
        creates the images needed for the graphic display
        :return: the images
        """
        coconut_img = tk.PhotoImage(file=self.COCONUT_IMG)
        pineapple_img = tk.PhotoImage(file=self.PINEAPPLE_IMG)
        background_img = tk.PhotoImage(file=self.BACKGROUND_IMG)
        winner_img = tk.PhotoImage(file=self.WINNER_IMG)
        illegal_img = tk.PhotoImage(file=self.ILLEGAL_IMG)
        return coconut_img, pineapple_img, background_img, winner_img, \
               illegal_img

    def create_canvas(self, background_img):
        """
        creates the canvas that will be the graphic game board
        :param background_img: the background image for the canvas
        :return: the canvas, the board title and a secondary title to be
        used later
        """
        canvas = tk.Canvas(self.__root,
                           width=self.CANVAS_WID,
                           height=self.CANVAS_HIGHT,
                           background='white')
        canvas.create_image(self.CENTER_WIDTH,
                            300,
                            image=background_img,
                            anchor=tk.CENTER)

        if self.__is_human == HUMAN:
            title = canvas.create_text(self.CENTER_WIDTH,
                                       50,
                                       text=self.OPEN_MESSAGE,
                                       font=('', self.WELCOME_FONT_SIZE),
                                       fill='white')
            if is_server:  # server starts the game.his title would be
                # accordingly
                second_title = canvas.create_text(
                    self.CENTER_WIDTH,
                    100,
                    text=self.PLAY,
                    font=('', self.SECOND_TITLE_FONT_SIZE),
                    fill='white')
            else:
                second_title = canvas.create_text(
                    self.CENTER_WIDTH,
                    150,
                    text=self.DONT_PLAY,
                    font=('', self.SECOND_TITLE_FONT_SIZE),
                    fill='white')
            canvas.pack()
            return canvas, title, second_title
        else:
            empty_title = canvas.create_text(350,
                                             100,
                                             text='',
                                             font=('', 30),
                                             fill='white')
            canvas.pack()
        return canvas, empty_title, empty_title

    def motion(self, event):
        """
        shows the current player what is the column his mouse is pointing on
        :param event: the location of the mouse (x and y coordinates)
        """
        if self.__is_playing:
            self.__col_lst.append(self.get_column(event.x))
            column = self.get_column(event.x)
            if column is None:
                return

            # Checking who is playing:
            player = self.__board.get_current_player()

            if self.__indicator == self.OFF:  # no indicator on screen
                # the screen
                self.put_image_helper(self.INDICATOR_ROW, column, player,
                                      self.INDICATOR)

            self.__indicator = self.ON
            if self.__col_lst[0] == self.__col_lst[-1]:  # first and last
                # item are the same- mouse is still on the same column
                return

            self.delete_img(self.INDICATOR)
            self.__col_lst = []
            self.__indicator = self.OFF

    def delete_img(self, name):
        """
        deleting an image from screen with a given name (tag).
        """
        tag = self.__canvas.find_withtag(name)
        if tag:  # if the image exist on the board at the moment
            self.__canvas.delete(tag)

    def __handle_message(self, column):
        """
        when receiving a message it means that one player had played and
        now it's the second one turn. this function is placing the disc that
        the first player put in his turn on the second's player board. if the
        second player is an ai player- it makes a move.
        :param column: the column the user clicked.
        """
        self.put_image(int(column))

        if self.__is_human == COMPUTER and self.__board.get_winner() is None:
            self.play_with_comp()  # calling to computer to act

    def get_column(self, event_x):
        """
        finds the column that fits the dictionary board coordinates form,
        according to the pixel the user clicked on
        :param event_x: the x-coordinate of the user click
        :return: the column in boards coordinate (int in range(7))
        """

        if event_x in range(self.COL_START,
                            self.COL_START + self.COL_FACTOR):  # range 31,122
            return 0
        elif event_x in range(self.COL_START + self.FACTOR,
                              self.COL_START + 2 * self.FACTOR):
            # range 122,213
            return 1
        elif event_x in range(self.COL_START + 2 * self.FACTOR,
                              self.COL_START + 3 * self.FACTOR):
            # range 213,304
            return 2
        elif event_x in range(self.COL_START + 3 * self.FACTOR,
                              self.COL_START + 4 * self.FACTOR):
            # range 304,395
            return 3
        elif event_x in range(self.COL_START + 4 * self.FACTOR,
                              self.COL_START + 5 * self.FACTOR):
            # range 395,486
            return 4
        elif event_x in range(self.COL_START + 5 * self.FACTOR,
                              self.COL_START + 6 * self.FACTOR):
            # range 486,577
            return 5
        elif event_x in range(self.COL_START + 6 * self.FACTOR,
                              self.COL_START + 7 * self.FACTOR):
            # range 577,668
            return 6
        else:
            return

    def put_image_helper(self, row, column, player, tag):
        """
        puts the user disc in its place according to a given 'row' and
        'column'. gets the current player, to determine which disc should be
        placed there. 'tag' is used for image that we want to remove later on.
        :param row: a given row number
        :param column: a given row number
        :param player: current player
        :param tag: the tag of the image (a string)
        :return:
        """
        if player == Game.PLAYER_ONE:
            self.__canvas.create_image(
                self.COL_0_COORD + column * self.COL_FACTOR,
                self.ROW_0_COORD - (5 - row) * self.ROW_FACTOR,
                image=self.__coconut_img,
                tag=tag)

        elif player == Game.PLAYER_TWO:
            self.__canvas.create_image(
                self.COL_0_COORD + column * self.COL_FACTOR,
                self.ROW_0_COORD - (5 - row) * self.ROW_FACTOR,
                image=self.__pineapple_img,
                tag=tag)

    def put_image(self, column):
        """
        gets the column (integer number in range(7)), and checking if its
        possible to put disc in this column. if it is- it will update
        the Game() object and update the graphic board by calling
        'put_image_helper'. after putting the disc, the is_plying will
        change to True, it means that the other player can play now.
        :param column: the column the user chose.
        :return: True if the disc was added.
        """
        if self.__board.legal_assignment(column):  # if the user move was legal
            player = self.__board.get_current_player()
            row = self.__board.make_move(column)
            self.put_image_helper(row, column, player, self.TAG)
            if self.__is_human == HUMAN:
                self.__canvas.itemconfig(self.__title,
                                         text=self.PLAY,
                                         font=('', self.MAIN_TITLE_FONT_SIZE))
                self.__canvas.delete(self.__second_title)

            self.__is_playing = True
            self.get_game_status()  # checks if the game is over or continues
            return True
        else:
            self.__canvas.create_image(self.CENTER_WIDTH,
                                       300,
                                       image=self.__illegal_img,
                                       tag=self.ILLEGAL_TAG)
            self.__canvas.after(self.TIME_OF_ILLEGAL_MOVE,
                                lambda: self.__canvas.delete(self.ILLEGAL_TAG))

    def paint_winner(self):
        """
        checks if there is a winner. if there is- it marks the 4 winning discs
        """
        win_coords = self.__board.get_win_seq()
        for coord in win_coords:
            row = coord[0]
            col = coord[1]
            self.__canvas.create_image(
                self.COL_0_COORD + col * self.COL_FACTOR,
                self.ROW_0_COORD - (5 - row) * self.ROW_FACTOR,
                image=self.__winner_img)

    def callback(self, event):
        """
        when user clicks on the board, the board should update. this
        function will react only if self.__is_playing=True, otherwise,
        clicking on the board will do nothing. its sends a message with the
        column clicked to the other player.
        :param event: x and y coordinates of the user click
        """
        if self.__is_playing:
            # user clicks outside the top border of the board - do nothing
            if event.y < self.OUT_OF_BOARD:
                return
            column = self.get_column(event.x)
            if column is None:
                return

            if self.put_image(column):
                self.delete_img(self.INDICATOR)
                self.__indicator = self.OFF  # no indicator on screen
                self.__canvas.delete(self.__second_title)
                self.__canvas.itemconfig(self.__title,
                                         text=self.DONT_PLAY,
                                         font=('', self.MAIN_TITLE_FONT_SIZE))
                # after a player had made a move, disable his mouse
                self.__is_playing = False
                # send message to the other player, so he can play:
                self.__communicator.send_message(column)

            self.get_game_status()  # checks if the game is over or continues

    def get_game_status(self):
        """
        checks if there is a winner, or draw in the game
        """
        if self.__board.get_winner() is not None:  # there is a winner
            self.__canvas.unbind("<Button-1>")  # human can't use their mouse
            self.__canvas.unbind("<Motion>")

            winner = self.__board.get_winner()

            if winner is self.__board.DRAW:
                self.__canvas.itemconfig(self.__title,
                                         text=self.TIE_SCORE,
                                         font=('', self.MAIN_TITLE_FONT_SIZE))

            else:
                if (is_server and not winner) or (not is_server and winner):
                    # display to each player if he won or lost the game
                    self.__canvas.itemconfig(self.__title,
                                             text=self.WINNER_MESAGGE,
                                             font=('',
                                                   self.MAIN_TITLE_FONT_SIZE))
                else:
                    self.__canvas.itemconfig(self.__title,
                                             text=self.LOSER_MESSAGE,
                                             font=('',
                                                   self.MAIN_TITLE_FONT_SIZE))
                self.paint_winner()  # displaying the winning discs

    def play_with_comp(self):
        """
        when this function is called, computer is playing. the function 
        determine in what column the computer should put the disc (if its 
        possible), then makes the move, and sends a message to the other
        player with the column the disc was placed in.
        :return: 
        """
        col = self.__ai.find_legal_move(self.__board, self.__board.make_move)
        self.put_comp_image(col)
        # send message to the other player, so he can play:
        self.__communicator.send_message(col)
        self.get_game_status()

    def put_comp_image(self, col):
        """
        checking which disc player should be added to the board, than check
        what is the row that the disc will be placed in, than call
        'put_image_helper' and update the graphic board. (Game() object has
        already updated).
        :param col: an integer (the column that the computer chose).
        :return: 
        """
        # disc is already updated on the board, so we need the opposite
        # player (not current one)
        num_of_discs = self.__board.count_discs()
        if num_of_discs % 2 == 0:
            player = self.__board.PLAYER_TWO
        else:
            player = self.__board.PLAYER_ONE
        row = self.__board.get_comp_row(col)
        self.put_image_helper(row, col, player, self.TAG)
        self.get_game_status()
Exemple #12
0
class GUI:
    """We defined this class using the tkinter class, here we defined the
    design of the board: colors, visibility, size, mode of operation..."""
    PICTURE_WIDTH = 800
    PICTURE_HEIGHT = 600
    SHIFT_RIGHT = 134
    SHIFT_LEFT = 134
    SHIFT_BOTTOM = 78
    SHIFT_UP = 75
    COIN_WIDTH = 76
    COIN_LENGTH = 90
    COIN_SHIFT = 173
    PLAYER_ONE_TURN_X = 744
    PLAYER_TWO_TURN_X = 58
    PLAYER_TURN_Y = 139
    LINES_LEN = 5
    COLUMN_NUM = 7
    COIN_WIN_NUMBER = 4
    INVALID_COLUMN = 10
    TIME_SLEEP = 0.001
    MOVE_INDEX = 10
    BOARD_IMAGE = 'img/board.png'
    BLUE_COIN_IMAGE = 'img/BLUE.png'
    RED_COIN_IMAGE = 'img/RED.png'
    BLUE_WIN_COIN_IMAGE = 'img/BLUE_WIN.png'
    RED_WIN_COIN_IMAGE = 'img/RED_WIN.png'
    TIE_IMAGE = 'img/Tie.png'
    PLAYER_ONE_NO_TURN_IMAGE = 'img/player_one_no.png'
    PLAYER_TWO_TURN_IMAGE = 'img/player_two_yes.png'

    BOARD_FROM_BOTTOM = PICTURE_HEIGHT - SHIFT_BOTTOM
    BUTTON_LENGTH = int(
        (PICTURE_WIDTH - SHIFT_LEFT - SHIFT_RIGHT) / COLUMN_NUM)

    def __init__(self, parent, port, my_turn, ip=None, ai=None):
        """
        Initiation function of the GUI Class
        :param parent: original board
        :param port: port for communication between computers
        :param my_turn: True if it's the turn of the relevant player
        :param ip: ip address for communication between computers
        """
        self.__parent = parent
        self.__ai = ai
        self.__my_turn = my_turn
        self.__parent.resizable(width=False, height=False)
        self.__game = Game()
        # limits of the board
        self.__canvas = tki.Canvas(self.__parent,
                                   width=self.PICTURE_WIDTH,
                                   height=self.PICTURE_HEIGHT)

        self.__background = tki.PhotoImage(file=self.BOARD_IMAGE)
        self.__canvas.create_image(0,
                                   0,
                                   anchor=tki.NW,
                                   image=self.__background)
        # Anchor NW will position the text so that the reference point
        # coincides with the northwest
        self.__canvas.grid()
        self.__canvas.bind('<Button-1>', self.__callback)

        self.__communicator = Communicator(parent, port, ip)
        self.__communicator.connect()
        # import message
        self.__communicator.bind_action_to_message(self.__handle_message)

        self.__blue_coin = tki.PhotoImage(file=self.BLUE_COIN_IMAGE)
        self.__red_coin = tki.PhotoImage(file=self.RED_COIN_IMAGE)
        self.__blue_coin_win = tki.PhotoImage(file=self.BLUE_WIN_COIN_IMAGE)
        self.__red_coin_win = tki.PhotoImage(file=self.RED_WIN_COIN_IMAGE)
        self.__tie_image = tki.PhotoImage(file=self.TIE_IMAGE)
        # We change a part of the background to indicate the player turn
        self.__player_one_no = tki.PhotoImage(
            file=self.PLAYER_ONE_NO_TURN_IMAGE)
        self.__one_no = None
        self.__player_two_yes = tki.PhotoImage(file=self.PLAYER_TWO_TURN_IMAGE)
        self.__two_yes = None

        if ip is None and self.__ai:
            column = self.__ai.find_legal_move(self.__game, self.__add_coin)
            self.__communicator.send_message(column)

    def __handle_message(self, column=None):
        """This function allows to add a coin in the screen of the non turn
        player according to the choice of the turn player and it allows the
        next player to play after that"""
        column = int(column)
        if column < self.__game.WIDTH:
            self.__add_coin(column)
            self.__my_turn = not self.__my_turn
        if self.__ai:
            column = self.__ai.find_legal_move(self.__game, self.__add_coin)
            self.__communicator.send_message(column)
            self.__my_turn = not self.__my_turn

    def __callback(self, event):
        """This function allows the player to add a coin according to a *valid*
        column that he has chosen, the "event" value is given from the user
        click according to the function defined in the __init__ it also sends
        to the second player his column choice that will be process in the
        previous handle_message function, and the turn of the actual player
        will be end"""

        position_x = event.x - self.SHIFT_LEFT
        column = position_x // self.BUTTON_LENGTH
        if position_x < 0:
            column = self.INVALID_COLUMN

        if self.__my_turn and column < self.__game.WIDTH:
            self.__add_coin(column)
            self.__communicator.send_message(str(column))
            self.__my_turn = not self.__my_turn

    def __add_coin(self, column):
        """This function call the make_move function that update the game
        status and change the screen displaying according to coin adding"""

        image_coin = None
        if column < self.__game.WIDTH:  # check that is a valid column
            try:
                tuple_move = self.__game.make_move(column)
                # make_move function returns the coordinates of the new coin
                # and the player turn

                if tuple_move[1] == self.__game.PLAYER_ONE:
                    image_coin = self.__blue_coin
                    # display who have to play
                    self.__one_no = self.__canvas.\
                        create_image(self.PLAYER_ONE_TURN_X, self.PLAYER_TURN_Y,
                                     image=self.__player_one_no)
                    self.__two_yes = self.__canvas.\
                        create_image(self.PLAYER_TWO_TURN_X, self.PLAYER_TURN_Y,
                                     image=self.__player_two_yes)

                elif tuple_move[1] == self.__game.PLAYER_TWO:
                    image_coin = self.__red_coin
                    self.__canvas.delete(self.__one_no)
                    self.__canvas.delete(self.__two_yes)

                coin = self.__canvas.\
                    create_image(self.COIN_SHIFT + column * self.COIN_WIDTH,
                                 self.SHIFT_UP, image=image_coin)
                # make move (animation)
                for i in range(
                        int(tuple_move[0][1] * self.COIN_LENGTH /
                            self.MOVE_INDEX)):
                    self.__canvas.move(coin, 0, self.MOVE_INDEX)
                    time.sleep(self.TIME_SLEEP)
                    self.__canvas.update()

                self.__print_winner()  # Display winner if there is one
            except:
                pass

    def __print_winner(self):
        """When the 4 coins from the same player are aligned, this function
        allows to show on the screen which player won and what"""

        winner = self.__game.get_winner_status()
        if winner:  # if there is the end of the game
            if winner[0] == self.__game.DRAW:
                self.__canvas.create_image(0,
                                           0,
                                           anchor=tki.NW,
                                           image=self.__tie_image)
                return
            # if someone won
            win_coo = winner[1]
            if winner[0] == str(self.__game.PLAYER_ONE):
                coin_win = self.__blue_coin_win
            else:
                coin_win = self.__red_coin_win

            if winner[2] == self.__game.HORIZONTAL_WINNING:
                for i in range(self.COIN_WIN_NUMBER):
                    self.__canvas.\
                        create_image(self.COIN_SHIFT+(win_coo[0]+i) * self.COIN_WIDTH,
                                     self.BOARD_FROM_BOTTOM - (self.LINES_LEN - win_coo[1])
                                     * self.COIN_LENGTH, image=coin_win)

            elif winner[2] == self.__game.VERTICAL_WINNING:
                for i in range(self.COIN_WIN_NUMBER):
                    self.__canvas.\
                        create_image(self.COIN_SHIFT+win_coo[0] * self.COIN_WIDTH,
                                     self.BOARD_FROM_BOTTOM - (self.LINES_LEN-win_coo[1] - i)
                                     * self.COIN_LENGTH, image=coin_win)

            elif winner[2] == self.__game.DIAGONAL_RIGHT:
                for i in range(self.COIN_WIN_NUMBER):
                    self.__canvas.\
                        create_image(self.COIN_SHIFT+(win_coo[0]+i) * self.COIN_WIDTH,
                                     self.BOARD_FROM_BOTTOM - (self.LINES_LEN-win_coo[1]+i)
                                     * self.COIN_LENGTH, image=coin_win)

            elif winner[2] == self.__game.DIAGONAL_LEFT:
                for i in range(self.COIN_WIN_NUMBER):
                    self.__canvas.\
                        create_image(self.COIN_SHIFT+(win_coo[0]+i) * self.COIN_WIDTH,
                                     self.BOARD_FROM_BOTTOM - (self.LINES_LEN-win_coo[1]-i)
                                     * self.COIN_LENGTH, image=coin_win)
Exemple #13
0
class GUI:
    """
    Designed to handle the GUI aspects (creating a window, buttons and
    pop-ups. Also initializes the communicator object, the game object and
    the AI object.
    """

    SCALAR = 99  # Determines the screen size and the sizes of objects
    # within it. Any int or float will work.
    # **The dimensions of the screen must be kept at a 6 to 7 ratio for the
    # game to work correctly.**
    WIDTH = 7 * SCALAR  # Width of the screen in pixels
    HEIGHT = 6 * SCALAR  # Height of the screen in pixels

    def __init__(self, game, master, port, player=None, ip=None):
        """
        Initializes the GUI and connects the communicator and the AI.
        :param game: The game class which includes the logic of the game.
        :param master: The tkinter root.
        :param port: The port to connect to.
        :param player: The player to start as. If server then player one,
        if client then player two.
        :param ip: The ip address of the server. to be left None if the
        instance is server.
        """
        self._master = master  # The tkinter root.

        self._canvas = t.Canvas(self._master,
                                width=self.WIDTH,
                                height=self.HEIGHT,
                                highlightthickness=0)  # The tkinter canvas.
        # The AI class which is to be activated only when the game is not
        # human vs. human.
        self._AI = AI()
        self._game = game
        # The communicator class which is in charge of communication between
        # server and client.
        self.__communicator = Communicator(root, port, ip)
        # Connects between instances of the game.
        self.__communicator.connect()
        # Binds the message handler to incoming messages.
        self.__communicator.bind_action_to_message(self.__handle_message)
        # __columns: A dictionary of keys: Column numbers.
        #                            values: The columns' item IDs.
        # __discs: A dictionary of keys: Coordinates on the board.
        #                          values: The Discs' item IDs.
        self.__columns, self.__discs = self.shapes()
        # if the instance is AI, is_human changes to False.
        self.__is_human = True
        self.__player = player

    def set_is_human(self, is_human):
        """
        Sets the __is_human attribute to true if the player is human and
        false if the player is 'AI'.
        :param is_human: True if human or False if AI
        """
        self.__is_human = is_human

    def event_handler(self, column, msg=False):
        """
        The function to be activated when a move is made by one of the players.
        Calls all the necessary functions to be executed when a
        turn has been made.
        :param column: The column which was clicked by a player.
        :param msg: Determines whether the move was made by the current
        player or by the other player.
        :return: None.
        """
        if msg:
            self._game.make_move(column)
            self.add_disc()
            self.win()
            self.tie()

        elif self.__player == self._game.get_current_player() and \
                self._game.get_winner() == None:
            self._game.make_move(column)
            self.add_disc()
            self.__communicator.send_message(str(column))
            self.win()
            self.tie()

    def add_disc(self):
        """
        Adds a disc to the screen according to the coordinates supplied by
        the "make move" function. Add a green or red disc depending on who
        the current player is.
        :return: None.
        """
        game = self._game
        y, x = game.get_last_move()
        item_id = self.__discs[x, y]
        if game.get_current_player() == game.PLAYER_ONE:
            self._canvas.itemconfig(item_id, fill="#af0707", outline='#751810')
        else:
            self._canvas.itemconfig(item_id, fill="#096300", outline='#203d11')

    def shapes(self):
        """
        The function that draws the initial board. Generates all of the
        necessary shapes by using the helper functions.
        :return: A dictionary of columns and a dictionary of discs.
        """
        self._canvas.pack()
        column_dict = {k: None for k in range(int(self.WIDTH / self.SCALAR))}
        disc_dict = self._game.create_board()
        for i in range(int(self.WIDTH / self.SCALAR)):  # Creates columns.
            color = self.color_generator \
                (index=i, RGB=(0, 0, 0, 0, 3, 0), oval=False)
            rectangle = self.draw_column(i, color)
            column_dict[i] = rectangle  # adds to column dictionary.
            for j in range(int(self.HEIGHT / self.SCALAR)):  # Creates discs.
                color = self.color_generator(index=(i, j),
                                             RGB=(1, 0, 0, 0, 2, 1),
                                             oval=True)
                oval = self.draw_oval(i, j, color)
                disc_dict[i, j] = oval  # adds to disc dictionary.
        return column_dict, disc_dict

    def key_bind(self, object, index):
        """
        Binds Left mouse
        button to event handler, as well as 'enter' and 'leave' to the current
        player signal (changing the column color).
        :param object: Type of object to bind.
        :index: Column index of the object.
        """
        self._canvas.tag_bind(object, '<Button-1>',
                              lambda event: self.event_handler(index, False))
        self._canvas.tag_bind(object, '<Enter>',
                              lambda event: self.column_config(index, True))
        self._canvas.tag_bind(object, '<Leave>',
                              lambda event: self.column_config(index, False))

    def draw_oval(self, i, j, color):
        """
        Creates The oval objects to be displayed on screen.
        :param i: Current column index.
        :param j: Current row index.
        :param color: The shade to fill the oval with.
        :return: Creates an oval object
        """
        scaled_i, scaled_j = i * self.SCALAR, j * self.SCALAR
        tlo = self.SCALAR * 0.2  # top left offset
        bro = self.SCALAR * 0.8  # bottom right offset
        oval = self._canvas.create_oval(scaled_i + tlo,
                                        scaled_j + tlo,
                                        scaled_i + bro,
                                        scaled_j + bro,
                                        fill=color,
                                        outline='',
                                        width=2)
        self.key_bind(oval, i)
        return oval

    def draw_column(self, i, color):
        """
        Used for the initial drawing of columns to the screen. Binds Left mouse
        button to event handler, as well as 'enter' and 'leave' to the current
        player signal (changing the column color).
        :param canvas: Canvas to draw on.
        :param i: Current column index.
        :param color: Fill color of the column.
        :return: Creates a column (rectangle object) on the screen.
        """
        scaled_i = self.SCALAR * i
        tlo = 0  # top left offset
        rectangle = self._canvas.create_rectangle(scaled_i,
                                                  tlo,
                                                  scaled_i + self.SCALAR,
                                                  self.HEIGHT,
                                                  fill=color,
                                                  outline='')
        self.key_bind(rectangle, i)
        return rectangle

    def column_config(self, i, enter):
        """
        Changes the color of the column on mouse over depending on the
        current player. If it is not the instance's turn, the column will be
        grayed out to avoid confusion and still let the player know that the
        game is not stuck.
        :param i: Column index.
        :param enter: True if 'enter' event, False if 'leave' event.
        """
        current_player = self._game.get_current_player()
        item_id = self.__columns[i]
        if enter and self.__player == current_player:
            if self.__player == self._game.PLAYER_ONE:
                self._canvas.itemconfig(item_id, fill='#720d0d')
            else:
                self._canvas.itemconfig(item_id, fill='#16720d')
        elif enter and self.__player != current_player:
            self._canvas.itemconfig(item_id, fill='#555555')
        elif not enter:
            color = self.color_generator(index=i,
                                         RGB=(0, 0, 0, 0, 3, 0),
                                         oval=False)
            self._canvas.itemconfig(item_id, fill=color)

    def color_generator(self, index, RGB, oval):
        """
        A function that is used to generate the various shades that are
        utilized in the initial creation of the board. The formula for the
        coloring of the ovals was found solely through rigorous trial and
        error. This gives the illusion of a gradient background.
        :param index: When sent from oval, this is a tuple of the row and
        column. This enables the illusion of a gradient background. When
        sent from a rectangle, it is the column index.
        :param RGB: The amount of Red, Green and Blue. Every two items in
        the list represent the amount of each color in hex - 00 being the
        minimum and FF being the maximum.
        :param oval: True if the object being colored is an oval, False if
        it is a rectangle.
        :return: A hexadecimal color code.
        """
        color_hex_string = '#'
        if oval == True:
            shade = int((index[0] + index[1]))
        else:
            shade = int(index)
        for ind in RGB:
            color_hex_string += hex(shade + ind)[2:]
        return color_hex_string

    def __handle_message(self, text=None):
        """
        Specifies the event handler for the message getting event in the
        communicator. Upon reception of a message, sends the column number to
        the event handler.
        :param text: the number of the column to place a disc in.
        """
        if text:
            column = int(text)
            self.event_handler(column, True)

        if not self.__is_human:
            self._AI.find_legal_move(self.event_handler)

    def win(self):
        """
        Sets the winning four animation if there is a win and calls the
        message box function, otherwise changes the current player.
        """
        game = self._game
        if game.is_win():
            tuple_list, curr_player = game.is_win()
            if curr_player == game.PLAYER_ONE:
                outline = '#ff851c'
                game.set_winner(game.PLAYER_ONE)
            else:
                outline = '#00ff00'
                game.set_winner(game.PLAYER_TWO)
            for tuple in tuple_list:
                x, y = tuple
                item_id = self.__discs[y, x]
                self._canvas.itemconfig(item_id,
                                        outline=outline,
                                        dash=5,
                                        width=3)
            self.win_message()
        if game.get_current_player() == game.PLAYER_ONE:
            game.set_current_player(game.PLAYER_TWO)
        else:
            game.set_current_player(game.PLAYER_ONE)

    def tie(self):
        """
        The Tie message box.
        """
        if self._game.is_tie():
            if messagebox.showinfo('Game Over', 'It\'s a Tie!'):
                self._master.destroy()

    def win_message(self):
        """
        The Win Message boxes.
        """
        if self._game.get_current_player() == self._game.PLAYER_ONE:
            if t.messagebox.showinfo('Game Over!', 'Red Wins'):
                self._master.destroy()
        if self._game.get_current_player() == self._game.PLAYER_TWO:
            if t.messagebox.showinfo('Game Over!', 'Green Wins'):
                self._master.destroy()
Exemple #14
0
class Gui():
    """
	class of the Gui wrapping the game
	"""

    SUM_OF_ALL_BOARD_CELLS = 7 * 6
    RANGE_BALL_CLICK = 60
    END_BARRIER_AXIS_X = 435
    START_BARRIER_BALLS_AXIS_X = 15
    END_OF_AXIS_Y_BALLS = 440
    START_SPACE_AXIS_Y = 90
    START_SPACE_AXIS_X = 20
    SPLITTER_OF_BALLS_AXIS_Y = 60
    SPLITTER_OF_BALLS_AXIS_X = 60
    NUM_OR_ROWS = 6
    NUM_OF_COLUNM = 7
    CANVAS_SIZE = 450
    BALL_SIZE = 50
    STEP_SIZE = 2
    WINNER_FONT = ("Helvetica", 20)
    WINNER_MSG = "The winner is the"
    BACKGROUND_COL = "#6495ED"
    DEFAULT_DISC_COL = 'white'
    PLAYER1_COL = "red"
    WIN_COL = 'yellow'
    PLAYER2_COL = "#008000"
    NO_WIN_MSG = "no one wins"
    WIN_MSG = "You win"
    LOOSE_MSG = 'You loose'
    BEST_AI_DEFAULT = 0
    YOUR_TURN_LABEL = 'For playing your turn\nclick on the wanted column'
    LABEL_COLOR = 'white'
    LABEL_X_PLACE = 65
    LABEL_Y_PLACE = 15

    def __init__(self, parent, port, is_ai, game, ip=None):
        """
		constructor of Gui
		:param parent: root of the platform
		:param port: to conect
		:param is_ai: True if this player is the computer
		:param ip: of the server.
		"""
        #
        self.game = game

        self.best_ai_move = self.BEST_AI_DEFAULT

        self.ip = ip

        # server begins the game
        if not ip:
            self.__my_turn = True

        else:
            self.__my_turn = False

        # initial interface
        self._parent = parent
        self._canvas = tk.Canvas(parent,
                                 width=self.CANVAS_SIZE,
                                 height=self.CANVAS_SIZE,
                                 bg=self.BACKGROUND_COL)
        self._disc_places = []
        self._prepeare_canvas()

        if is_ai:
            self.is_ai = True
            self.ai = AI()
        else:
            self.is_ai = False
            self._canvas.bind("<Button-1>", self.click_bind)
        self._canvas.pack()

        # communication:
        self.__communicator = Communicator(parent, port, ip)
        self.__communicator.connect()
        self.__communicator.bind_action_to_message(self.__handle_message)

        # if self is server and ai then responds immediately
        if is_ai and not ip:
            self.modify_my_turn(True)
            self.respond()

        if not is_ai:

            self.__put_labal()

    def __put_labal(self):

        self.__label = tk.Label(self._parent,
                                text=self.YOUR_TURN_LABEL,
                                fg=self.LABEL_COLOR,
                                font=("Garamond", 20, "bold"),
                                bg=self.BACKGROUND_COL)
        self.__label.pack()
        self.__label.place(x=self.LABEL_X_PLACE, y=self.LABEL_Y_PLACE)

    def __handle_message(self, col=None):
        """
		sends the massage of the player from the other side to the board
		:param col: number of column
		:return: None
		"""

        self.game.make_move(int(col))
        self._update_interface()
        if self.game.get_winner()[0] != self.game.NO_WINNER:
            self.declare_winner()
            return
        self.modify_my_turn(True)

        if self.is_ai:
            self.respond()

    def click_bind(self, event):
        """
		for every click of the left button of the mouse the func
		interperts it to the column that the player wanted
		:param event: the coordinates of the click on the canvas
		:return: transfer the column to the func  board.make_move()
		"""
        if self.is_my_turn():
            self.game.set_turn(True)
            if self.START_SPACE_AXIS_Y <= event.y <= self.END_OF_AXIS_Y_BALLS and \
              self.START_BARRIER_BALLS_AXIS_X <= event.x <= self.END_BARRIER_AXIS_X:
                col = (event.x - self.START_BARRIER_BALLS_AXIS_X
                       ) // self.RANGE_BALL_CLICK
                self.respond(col)

    def respond(self, col=None):
        """make the next current player move"""
        if not self.is_ai:
            try:
                column = self.game.make_move(col)
                self.send_to_other_player(col)
                self._update_interface()
                winner = self.game.get_winner()[0]
                if winner != self.game.NO_WINNER and not self.is_ai:
                    self._canvas.unbind("<Button-1>")
                    self.declare_winner()
                self.modify_my_turn(False)
            except:
                return None
        else:
            col = self.ai.find_legal_move(self.game, self.ai_func)
            self._update_interface()
            if self.game.get_winner()[0] != self.game.NO_WINNER:
                self.declare_winner()
            self.send_to_other_player(col)
            self.modify_my_turn(False)

    def ai_func(self, column):
        """The next function is not necessary to the course team
		It's the function which records every potential ai moves
		 it the ai will finish all moves it will put -1 in the
		function and the ouput will be the last ai move"""

        if column not in range(len(self.game.board[0])):
            return self.game.make_move(self.best_ai_move)
        else:
            self.best_ai_move = column

    def _update_interface(self):
        """updates Gui interface so it will fit the acutal board"""
        for col in range(len(self.game.board[0])):
            for row in range(len(self.game.board)):
                if self.game.board[row][col] == self.game.PLAYER_ONE:
                    self.paint_the_disc(col, row, self.game.PLAYER_ONE)
                elif self.game.board[row][col] == self.game.PLAYER_TWO:
                    self.paint_the_disc(col, row, self.game.PLAYER_TWO)

    def _prepeare_canvas(self):
        """
		paints on the canvas ovals(discs)
		:return: None
		"""
        balls = []
        for col_x in range(self.NUM_OF_COLUNM):
            for row_y in range(self.NUM_OR_ROWS):
                x = self.START_SPACE_AXIS_X + self.SPLITTER_OF_BALLS_AXIS_X * col_x
                y = self.START_SPACE_AXIS_Y + self.SPLITTER_OF_BALLS_AXIS_Y * row_y
                self._canvas.create_oval(x,
                                         y,
                                         x + self.BALL_SIZE,
                                         y + self.BALL_SIZE,
                                         fill=self.DEFAULT_DISC_COL)
                balls.append((x, y))
            self._disc_places.append(balls)
            balls = []

    def paint_the_disc(self, col_x, row_y, player, color=None):
        """
		the func gets the place of the disc that suppose to be paint,
		by the color of the player
		:param col_x:
		:param row_y:
		:return:
		"""
        x = self.START_SPACE_AXIS_X + self.SPLITTER_OF_BALLS_AXIS_X * col_x
        y = self.START_SPACE_AXIS_Y + self.SPLITTER_OF_BALLS_AXIS_Y * row_y
        if not color:
            if player == self.game.PLAYER_ONE:
                color = self.PLAYER1_COL
            else:
                color = self.PLAYER2_COL
        self._canvas.create_oval(x,
                                 y,
                                 x + self.BALL_SIZE,
                                 y + self.BALL_SIZE,
                                 fill=color)

    def declare_winner(self):
        """when a player wins it adds title
		Who is the winner"""

        winner_player = self.game.get_winner()
        if winner_player[0] == self.game.PLAYER_ONE:
            winner = self.WIN_MSG
        elif winner_player[0] == self.game.PLAYER_TWO:
            winner = self.LOOSE_MSG
        else:
            winner = self.NO_WIN_MSG
        if winner_player[0] != self.game.DRAW:

            for ball in winner_player[1]:
                self.paint_the_disc(ball[1],
                                    ball[0],
                                    winner_player[0],
                                    color=self.WIN_COL)
        label = tk.Label(self._parent, text=winner, font=self.WINNER_FONT)
        label.pack(side=tk.TOP)

    def send_to_other_player(self, column):
        """
		sends to the other player the column that I clicked
		:param column:
		:return:
		"""
        if self.__my_turn:
            self.__communicator.send_message(column)

    def modify_my_turn(self, is_my_turn):
        """
		boolenic changer if it's my turn or not.
		:param is_my_turn: True or False
		:return:
		"""

        self.__my_turn = is_my_turn
        self.game.set_turn(is_my_turn)

    def is_my_turn(self):
        """
		:return: Ture or False if it's my turn
		"""
        return self.__my_turn
Exemple #15
0
class GUI:
    """
    Designed to handle the GUI aspects (creating a window, buttons and
    pop-ups. Also initializes the communicator object.
    """

    MESSAGE_DISPLAY_TIMEOUT = 250

    def __init__(self, root, parent, port, ip=None):
        self.game_obj = Game()
        self._ai_mode = False
        print('parent:', parent)
        print('port:', port)
        """
        Initializes the GUI and connects the communicator.
        :param parent: the tkinter root.
        :param ip: the ip to connect to.
        :param port: the port to connect to.
        :param server: true if the communicator is a server, otherwise false.
        """

        self._root = root
        self._parent = parent

        self.__communicator = Communicator(root, port, ip)
        self.__communicator.connect()
        self.__communicator.bind_action_to_message(self.__handle_message)
        self.__place_widgets()
        #self.frame = t.Frame(self._parent, width=800, height=800)

        self._canvas = t.Canvas(root, width=700, height=600, bg='blue')
        self._grid = t.Grid()
        #self.frame.pack()
        self._canvas.pack()
        self._create_circle()

        if parent == 'ai':
            self.ai_obj = Ai()
            self._ai_mode = True
            if (server == True and self.game_obj.get_current_player()
                    == 0) or (server == False
                              and self.game_obj.get_current_player() == 1):
                self.ai_move()
        else:
            self._canvas.bind("<Button-1>", self.callback)

    def __place_widgets(self):
        pass

    def popup_showinfo(self, obj):
        showinfo("Window", "The winner is: " + str(obj))

    def _create_circle(self):
        board = self.game_obj.get_board()

        for j in range(6):
            for i in range(7):
                x_place = (100 * i) + 50
                y_place = (100 * j) + 50
                r = 47.5
                color = "white"
                if board[i][j] == "0": color = "red"
                if board[i][j] == "1": color = "yellow"
                self._canvas.create_oval(x_place - r,
                                         y_place - r,
                                         x_place + r,
                                         y_place + r,
                                         fill=color)

    def callback(self, event):

        print('column=', math.floor(event.x / 100))
        column = math.floor(event.x / 100)
        if (server == True and self.game_obj.get_current_player() == 0) \
                or (server == False and self.game_obj.get_current_player() == 1):
            self.game_obj.make_move(column)
            self.color_circle(self.game_obj.get_current_player(), column)
            message = str(column)

            is_winner = self.game_obj.get_winner()

            if is_winner == 0 or is_winner == 1 or is_winner == 2:
                message += str(is_winner)
                self.popup_showinfo(is_winner)

            self.__communicator.send_message(message)
        # return column

    def color_circle(self, player, column):
        x_place = (100 * column) + 50

        y_place = (100 * self.game_obj.get_empty_row(column)) + 50
        r = 47.5
        if player == 0:
            self._canvas.create_oval(x_place - r,
                                     y_place - r,
                                     x_place + r,
                                     y_place + r,
                                     fill='red')
        if player == 1:
            self._canvas.create_oval(x_place - r,
                                     y_place - r,
                                     x_place + r,
                                     y_place + r,
                                     fill='yellow')
        pass


#    tk.Canvas.create_circle = _create_circle

    def __handle_message(self, text=None):
        """
        Specifies the event handler for the message getting event in the
        communicator. Prints a message when invoked (and invoked by the
        communicator when a message is received). The message will
        automatically disappear after a fixed interval.
        :param text: the text to be printed.
        :return: None.
        """
        if text:
            if len(text) > 1:
                self.popup_showinfo(text[1])
            else:
                column = int(text[0])
                print("The column i got is:", column)
                self.game_obj.make_move(column)
                self.color_circle(self.game_obj.get_current_player(), column)

                if self._ai_mode:
                    self.ai_move()

                self._root.after(self.MESSAGE_DISPLAY_TIMEOUT,
                                 self.__handle_message)

    # else:
    #self.__label["text"] = ""
        pass

    def ai_move(self):
        self.ai_obj.find_legal_move(self.game_obj, self.ai_move)
        column = self.game_obj.get_last_move()
        self.color_circle(self.game_obj.get_current_player(), column)

        message = str(column)

        is_winner = self.game_obj.get_winner()
        print("who is the winner: ", self.game_obj.get_winner())
        if is_winner == 0 or is_winner == 1 or is_winner == 2:
            message += str(is_winner)
            self.popup_showinfo(is_winner)

        self.__communicator.send_message(message)
Exemple #16
0
class FourInARow:
    """
    The high level application object, handling game events,
    turn order and legality, communication between instances 
    and controlling the objects that manage GUI, gameplay and AI.
    """
    PLAYERS = ARG_PLAYERS
    MSG_NOT_TURN = 'Not your turn!'
    MSG_DRAW = 'draw'
    MSG_WIN = 'winner!'
    MSG_LOSE = 'loser :('

    def __init__(self, root, player, port, ip):
        """
        The function initialize all object's private values.
        :param player: A string which decide if the player is human or ai.
        :param port: An integer between 0 to 65535. better use ~8000
        :param ip: The host IP. can be None if the player is the
        host or the host ip address.
        """
        self.__root = root
        self.__game = Game()
        # decide whether the player is AI or not
        if player == self.PLAYERS[1]:
            self.__is_ai = True
        else:
            self.__is_ai = False
            # Set both Players colors
        if ip:
            self.__my_color = Game.PLAYER_TWO
            self.__op_color = Game.PLAYER_ONE

        else:
            self.__my_color = Game.PLAYER_ONE
            self.__op_color = Game.PLAYER_TWO

        self.__communicator = Communicator(root, port, ip)
        self.__communicator.connect()
        self.__communicator.bind_action_to_message(self.handle_message)

        if self.__is_ai:  # If the player is AI we initialize an AI object
            self.__screen = Screen(root, self.__my_color, lambda y: None)
            self.__ai = AI()
            if self.__my_color == Game.PLAYER_ONE:
                self.ai_find_move(
                )  # and call ai_find_move to make the first move.
        else:
            self.__screen = Screen(root, self.__my_color, self.play_my_move)

    def ai_find_move(self):
        """
        The function handles the AI turn.
        It creates a copy of the game object and sends it to the AI instance.
        Then, it makes the next AI move using the AI find_legal_move method.
        :return: None
        """
        # creates a copy of the game instance and sends it to the AI.
        sim_game = Game()
        board, register, cell_set, counter = self.__game.get_attr_for_sim()
        sim_game.set_attr_for_sim(board, register, cell_set, counter)

        try:
            self.__ai.find_legal_move(sim_game, self.play_my_move)
        except:
            self.__screen.print_to_screen(self.__ai.NO_AI_MOVE,
                                          self.__my_color)

    def play_my_move(self, column):
        """
        The function handles a certain game move, both by the AI instance
        and when a GUI button is pressed, by calling the class one_turn method
        and sending the opponent a message using the communicator instance.
        :param column: column in board to play (0 <= int <= 6)
        :return: None
        """
        if self.one_turn(column, self.__my_color):
            self.__communicator.send_message(str(column))

    def one_turn(self, column, player):
        """
        The function handles one turn of both kinds of players,
        AI and human by preforming the following actions:
        1. Try to make the given move(column).
        2. Update the screen instance according to the move.
        3. Send the opponent an message about the move it made.
        4. Checks if the game ended using the game get_winner method.
        :param column: column in board (0 <= int <= 6)
        :param player: The player which made the turn. Player_one/Player_two.
        :return: True if the move was done (may be illegal move). None otherwise.
        """
        # The below if make sure that both players can play only when its their turn.
        if self.__game.get_current_player() == player:
            #Try to make the move, if the move is illegal raise an exception
            try:
                self.__game.make_move(column)
                row, col = self.__game.get_last_coord()
                move_done = True

                if self.__is_ai:
                    self.__screen.update_cell(row, col, player, anim=False)
                else:
                    self.__screen.update_cell(row, col, player, anim=True)

            except:
                self.__screen.print_to_screen(self.__game.ILLEGAL_MOVE_MSG,
                                              player)
                return

        else:
            self.__screen.print_to_screen(self.MSG_NOT_TURN, player)
            return
        # check if the game is ended by win/loss/draw.
        winner = self.__game.get_winner()
        if winner is not None:
            self.end_game(winner)

        return move_done

    def end_game(self, winner):
        """
        The function handles the situation where the game is done.
        Its using the screen instance to print the game result 
        (win/loss/draw) to the graphical interface.
        :param winner: The game result (PLAYER_ONE / PLAYER_TWO / DRAW).
        :return: None
        """
        win_coord, win_dir = self.__game.get_win_info(
        )  # Ask the game instance for the win_coord and direction
        self.__screen.win(
            win_coord, win_dir,
            winner)  # In order to display the winning sequence (FLASH!)

        if winner == Game.DRAW:
            self.__screen.print_to_screen(self.MSG_DRAW,
                                          self.__my_color,
                                          end=True)
            self.__screen.print_to_screen(self.MSG_DRAW,
                                          self.__op_color,
                                          end=True)

        elif winner == self.__my_color:
            self.__screen.print_to_screen(self.MSG_WIN,
                                          self.__my_color,
                                          end=True)
            self.__screen.print_to_screen(self.MSG_LOSE,
                                          self.__op_color,
                                          end=True)

        elif winner == self.__op_color:
            self.__screen.print_to_screen(self.MSG_WIN,
                                          self.__op_color,
                                          end=True)
            self.__screen.print_to_screen(self.MSG_LOSE,
                                          self.__my_color,
                                          end=True)

    def handle_message(self, message=None):
        """
        The function specifies the event handler for the message getting
        event in the communicator. When it is invoked, it calls the one_turn
        method in order to update the opponent move on its screen instance
        or end the game if needed. it invoked by the
        communicator when a message is received.
        :param message: The last move the opponent made (0 <= int <= 6) Default is None.
        :return: None
        """
        if message:
            self.one_turn(int(message[0]), self.__op_color)
            if self.__is_ai:  # If the player is AI we call ai_find_move to make the AI next move.
                if self.__game.get_win_info()[1] is None:
                    self.ai_find_move()
Exemple #17
0
upper_range = np.array([30, 255, 255], dtype=np.uint8)

while (cap.isOpened()):
    ret, img = cap.read()
    # cv2.rectangle(img,(300,300),(100,100),(0,255,0),0)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

    mask = cv2.inRange(img, lower_range, upper_range)
    img = cv2.bitwise_and(img, img, mask=mask)
    sq, click = find_squares(img)

    if sq:
        M = cv2.moments(sq[0])
        cx = int(M['m10'] / M['m00'])
        cy = int(M['m01'] / M['m00'])
        if click:
            com.send_message([cx, cy, 1])
        else:
            com.send_message([cx, cy, 1])

        cv2.drawContours(img, sq, -1, (0, 255, 0), 3)

        print 'Clicked ? %r' % click
    # print sq

    cv2.imshow('Img', img)

    k = cv2.waitKey(10)
    if k == 27:
        break