Skip to content

Commit

Permalink
almost done
Browse files Browse the repository at this point in the history
  • Loading branch information
codingWhale13 committed Mar 11, 2018
1 parent 6d74da7 commit f4ca4ed
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 114 deletions.
6 changes: 6 additions & 0 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ def __choose_name(self) -> str:
return choice(self.NAMES)

def __negamax(self, board, depth, alhpa, beta, multiplier) -> tuple:
pass
"""
if depth == 0 or self.__rules.check_game_over(board):
return (multiplier * score)
Expand All @@ -31,10 +33,14 @@ def __negamax(self, board, depth, alhpa, beta, multiplier) -> tuple:
break
return (best_value, -1)
"""

def get_move(self, board) -> int:
pass
"""
# "negamax" algorithm determines best move
return self.__negamax(board, self.__depth, -999, 999, 1)[1]
"""

@property
def name(self):
Expand Down
Binary file modified db.sqlite
Binary file not shown.
30 changes: 13 additions & 17 deletions fancy_print.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
class FancyPrint:
#HEADER = '\033[95m'
#OKBLUE = '\033[94m'
#UNDERLINE = '\033[4m'
# declare needed ANSI escape sequences for colorful output to console
__BLUE = '\033[94m'
__RED = '\033[91m'
__YELLOW = '\033[93m'
__END = '\033[0m'

def __init__(self) -> None:
# declare needed ANSI escape sequences for colorful output to console
self.__RED = '\033[91m'
self.__YELLOW = '\033[93m'
self.__BLUE = '\033[94m'
self.__BOLD = '\033[1m'
self.__END = '\033[0m'
# string "message" will be displayed in the corresponding color
# when highlighting a single character, "line_end" can be modified so that no line break is added after the message
def blue(self, message: str, line_end: str) -> None:
print(type(self).__BLUE + message + type(self).__END, end=line_end)

def red(self, message, line_end='\n') -> None:
print(self.__RED + message + self.__END, end=line_end)
def red(self, message: str, line_end: str) -> None:
print(type(self).__RED + message + type(self).__END, end=line_end)

def blue(self, message, line_end='\n') -> None:
print(self.__BLUE + message + self.__END, end=line_end)

def yellow(self, message, line_end='\n') -> None:
print(self.__YELLOW + message + self.__END, end=line_end) # TODO: bold?
def yellow(self, message: str, line_end: str) -> None:
print(type(self).__YELLOW + message + type(self).__END, end=line_end)
91 changes: 49 additions & 42 deletions game.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from board import Board
from bot import Bot
from fancy_print import FancyPrint
from gui import GUI
from player import Player
from rules import Rules
Expand All @@ -12,7 +11,6 @@ def __init__(self) -> None:
self.__board = Board()
self.__gui = GUI()
self.__rules = Rules()
self.__fancy_print = FancyPrint()
# player objects will be set in "initialize_match"
self.__player_1 = None
self.__player_2 = None
Expand All @@ -25,14 +23,14 @@ def __init__(self) -> None:
self.__play()

def __welcome(self) -> None:
self.__fancy_print.blue("Ready to play CONNECT FOUR?")
self.__fancy_print.blue("Here we go!")
self.__gui.text_blue("Ready to play CONNECT FOUR?")
self.__gui.text_blue("Here we go!")

def __human_player_wanted(self, player_number: int) -> bool:
# inform user about options
print("Player {} shall be:".format(player_number))
print("-> human (press 'H')")
print("-> a bot (press 'B')")
self.__gui.text("Player {} shall be:".format(player_number))
self.__gui.text("-> human (press 'H')")
self.__gui.text("-> a bot (press 'B')")
# ask for input until valid decision has been made
while True:
# let user choose type of player (human or bot) by pressing the corresponding key
Expand All @@ -41,26 +39,26 @@ def __human_player_wanted(self, player_number: int) -> bool:
return True
elif player_type == 'b':
return False
else:
self.__fancy_print.red("ERROR: Press 'H' or 'B'.")
self.__gui.text_red("ERROR: Press 'H' or 'B'.")

def __initialize_match(self) -> None:
# create either objects of "Player" or "Bot" for both players - this is decided in "human_player_wanted"
# initialization of player names and symbols for representation on board is done in "Player" and "Bot"
# initialization of player names and symbols for representation on board is done in "Player" or "Bot"
if self.__human_player_wanted(1):
self.__player_1 = Player(1, self.__player_1_default_symbol)
self.__player_1 = Player(1, self.__player_1_default_symbol, self.__gui)
else:
Bot(1, self.__player_1_default_symbol)
self.__player_1 = Bot(1, self.__player_1_default_symbol, self.__gui)

if self.__human_player_wanted(2):
self.__player_2 = Player(2, self.__player_2_default_symbol)
self.__player_2 = Player(2, self.__player_2_default_symbol, self.__gui)
else:
Bot(2, self.__player_2_default_symbol)
self.__player_2 = Bot(2, self.__player_2_default_symbol, self.__gui)

# ensure that players have unique names and symbols
if self.__player_1.name == self.__player_2.name or self.__player_1.symbol == self.__player_2.symbol:
if type(self.__player_1) == Player and type(self.__player_2) == Player:
# if both players are human, restart initialization
self.__fancy_print.red("ERROR: Both players have the same name or symbol! Try again.")
self.__gui.text_red("ERROR: Both players have the same name or symbol! Try again.")
self.__initialize_match()
return # abort current call of "initialize_match" since new one has been created
else:
Expand All @@ -83,23 +81,27 @@ def __initialize_match(self) -> None:
self.__id_to_symbol[self.__player_2.id] = self.__player_2.symbol

# summarize player information before exiting initialization
self.__fancy_print.blue("\nGreat. Let's start the game!")
print("Player 1 ({}): {} is '{}'.".format("HUMAN" if type(self.__player_1) == Player else "BOT",
self.__player_1.name, self.__player_1.symbol))
print("Player 2 ({}): {} is '{}'.\n".format("HUMAN" if type(self.__player_2) == Player else "BOT",
self.__player_2.name, self.__player_2.symbol))
self.__gui.text_blue("\nGreat. Let's start the game!")
self.__gui.text("Player 1 ({}): {} is '{}'.".format("HUMAN" if type(self.__player_1) == Player else "BOT",
self.__player_1.name, self.__player_1.symbol))
self.__gui.text("Player 2 ({}): {} is '{}'.\n".format("HUMAN" if type(self.__player_2) == Player else "BOT",
self.__player_2.name, self.__player_2.symbol))

def __move(self, player: Player) -> None:
# a move of a bot will always be valid - thus, only for a human player further checking is needed
if type(player) == Bot:
self.__board.do_move(player.get_move(), player.id)
self.__board.do_move(player.get_move(self.__gui), player.id)
else:
while True:
# "player.get_move" asks user for valid input (integer between 1 and 7)
desired_move = player.get_move()
# "player.get_move" asks user for valid input (when width is 7: integer between 0 and 6)
desired_move = player.get_move(self.__gui)
# "rules.is_move_possible" checks if desired move is not against the rules
if self.__rules.check_move(self.__board.get_board(), self.__board.height, desired_move):
break
else:
# let user know the move is not possible
# "desired_move" is zero-based and needs to be increased by one for display
self.__gui.text_red("Column {} is already full! Try again.".format(desired_move + 1))
# when a legal move is given, "board.do_move" organizes actually playing it
self.__board.do_move(desired_move, player.id)

Expand All @@ -109,45 +111,48 @@ def __play_match(self) -> None:
while True:
match_round += 1
# before asking a player what to do, show the board
self.__gui.show(self.__board.get_board(), self.__board.width, self.__board.height, self.__id_to_symbol)
self.__gui.show_board(self.__board.get_board(), self.__board.width, self.__board.height,
self.__id_to_symbol)
# depending on who's turn it is, let player do a move
if match_round % 2 == 1:
# prompt player to do a move with method "move"
self.__move(self.__player_1)
# check if player 1 has won by playing the latest move
# check if player 1 has won with his latest move
winning_line = self.__rules.check_win(self.__board.get_board(), self.__board.width, self.__board.height,
self.__board.get_last_move(), self.__player_1.id)
# "winning_line" is either None or a list of tuples that store positions of tokens which led to win
if winning_line:
# player 1 has won; show board (emphasizing winning line) one last time
self.__gui.show(self.__board.get_board(), self.__board.width, self.__board.height,
self.__id_to_symbol, winning_line)
self.__fancy_print.blue("{} has won. Good game!".format(self.__player_1.name))
if winning_line is not None:
# player 1 has won; show board - emphasizing the winning line - one last time before exiting method
self.__gui.show_board(self.__board.get_board(), self.__board.width, self.__board.height,
self.__id_to_symbol, winning_line)
self.__gui.text_blue("{} has won. Good game!".format(self.__player_1.name))
# increase the score of player 1 by one before exiting method
self.__player_1.score += 1
return
else:
# prompt player to do a move with method "move"
self.__move(self.__player_2)
# check if player 2 has won by playing the latest move
# check if player 2 has won with his latest move
winning_line = self.__rules.check_win(self.__board.get_board(), self.__board.width, self.__board.height,
self.__board.get_last_move(), self.__player_2.id)
# "winning_line" is either None or a list of tuples that store positions of tokens which led to win
if winning_line:
# player 2 has won; show board (emphasizing winning line) one last time
self.__gui.show(self.__board.get_board(), self.__board.width, self.__board.height,
self.__id_to_symbol, winning_line)
self.__fancy_print.blue("{} has won. Good game!".format(self.__player_2.name))
if winning_line is not None:
# player 2 has won; show board - emphasizing the winning line - one last time before exiting method
self.__gui.show_board(self.__board.get_board(), self.__board.width, self.__board.height,
self.__id_to_symbol, winning_line)
self.__gui.text_blue("{} has won. Good game!".format(self.__player_2.name))
# increase the score of player 2 by one before exiting method
self.__player_2.score += 1
return

# if board is full, show it one last time and let players know that match is a draw before exiting method
if self.__rules.check_game_over(self.__board.get_board(), self.__board.width, self.__board.height):
self.__gui.show(self.__board.get_board(), self.__board.width, self.__board.height, self.__id_to_symbol)
self.__fancy_print.blue("It's a draw!")
self.__gui.show_board(self.__board.get_board(), self.__board.width, self.__board.height,
self.__id_to_symbol)
self.__gui.text_blue("It's a draw!")
return

"""
def __settings(self) -> None:
while True:
input_change_player_settings = input("Do you want to change any player settings? (Y/N): ").lower()
Expand All @@ -157,21 +162,23 @@ def __settings(self) -> None:
self.__fancy_print.blue("Alright. Next match starts now!")
break
self.__fancy_print.red("ERROR: Press 'Y' or 'N'.")
"""

def __goodbye(self) -> None:
# self.__show_stats
self.__fancy_print.blue("Thank you for playing. Bye for now!")
# TODO: self.__show_stats
self.__gui.text_blue("Thank you for playing. Bye for now!")

def __keep_playing(self) -> bool:
# ask user for input until valid decision has been made
while True:
input_keep_playing = input("What a match! Would you like to play another one? (Y/N) ").lower()
if input_keep_playing == 'y':
self.__fancy_print.blue("Cool, next match starts now!")
self.__gui.text_blue("Cool, next match starts now!")
return True
if input_keep_playing == 'n':
self.__goodbye()
return False
self.__fancy_print.red("ERROR: Press 'Y' or 'N'.")
self.__gui.text_red("ERROR: Press 'Y' or 'N'.")

def __play(self) -> None:
# welcome players once in the beginning
Expand Down
50 changes: 32 additions & 18 deletions gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,48 @@ def __init__(self) -> None:
# for better visualization, an instance of "FancyPrint" is needed
self.__fancy_print = FancyPrint()

def show(self, board: dict, width: int, height: int, id_to_symbol: dict, winning_line: bool = False) -> None:
# this method simply prints the text; this makes the GUI more modular and exchangeable
def text(self, message, line_end: str = '\n'):
print(message, end=line_end)

# use "fancy_print" for displaying colorful text
def text_blue(self, message: str, line_end: str = '\n') -> None:
self.__fancy_print.blue(message, line_end)

def text_red(self, message: str, line_end: str = '\n') -> None:
self.__fancy_print.red(message, line_end)

def text_yellow(self, message: str, line_end: str = '\n') -> None:
self.__fancy_print.yellow(message, line_end)

def show_board(self, board: dict, width: int, height: int, id_to_symbol: dict, winning_line: list = None) -> None:
# firstly, print column names (usually from 1 to 7)
print(' ' + ' '.join(map(str, range(1, width + 1))))
self.text(' ' + ' '.join(map(str, range(1, width + 1))))
# if a player has won, highlight the winning line
if winning_line:
if winning_line is None:
# because (0, 0) is the bottom-left corner of board, y has to iterate backwards through rows
for y in range(height - 1, -1, -1):
# output left barrier
print(end="|")
# display left barrier
self.text("", '|')
# print player symbols instead of player id (unoccupied fields are represented as spaces) for each field
# columns are separated using the pipe symbol
for x in range(width):
if (x, y) in winning_line:
# highlight the symbol on this field to show the winning line
self.__fancy_print.yellow(id_to_symbol[board[(x, y)]], '|')
else:
# a standard field is printed with default font style
print(id_to_symbol[board[(x, y)]], end="|")
# print a line break so that next row starts in the next line
print()
self.text(id_to_symbol[board[(x, y)]], '|')
# add a line break so that next row starts in next line
self.text("")
else:
# because (0, 0) is the bottom-left corner of board, y has to iterate backwards through rows
for y in range(height - 1, -1, -1):
# output left barrier
print(end="|")
# display left barrier
self.text("", '|')
# print player symbols instead of player id (unoccupied fields are represented as spaces) for each field
# columns are separated using the pipe symbol
for x in range(width):
print(id_to_symbol[board[(x, y)]], end="|")
# print a line break so that next row starts in the next line
print()
if (x, y) in winning_line:
# highlight the symbol on this field to show part of the winning line
self.text_yellow(id_to_symbol[board[(x, y)]], '|')
else:
# a standard field is printed with default font style
self.text(id_to_symbol[board[(x, y)]], '|')
# add a line break so that next row starts in next line
self.text("")
1 change: 1 addition & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from game import Game

if __name__ == "__main__":
# main.py simply instantiates an Game object which will take care of the further game process
Game()
Loading

0 comments on commit f4ca4ed

Please sign in to comment.