class Connect4GetPlayerNameTest(unittest.TestCase): """Test the Get Player Name prompt from the View""" def setUp(self): self.view = View() def tearDown(self): del self.view @patch('builtins.input', return_value='Rob-E') def test_normal_name(self, inputted_value): """Ensure that the view will return a string that is input.""" name = self.view.get_player_name() self.assertEqual(name, 'Rob-E') @patch('builtins.input', return_value='') @patch('sys.stdout', new_callable=StringIO) def test_no_name(self, output, inputted_value): """ Ensure that the view chooses a random name if the user enters an empty string. :param output: catches the stdout text :param inputted_value: strings passed in by mock.patch :return: Returns None """ with open('bin/names.txt') as f: names = [line.rstrip() for line in f] name = self.view.get_player_name() self.assertIn(name, names, "Random name generation failed") @patch('builtins.input', return_value=long_name) @patch('sys.stdout', new_callable=StringIO) def test_long_name(self, output, inputted_value): """Ensure that the view will return a more reasonably sized name.""" name = self.view.get_player_name() long_name_error = "Wow, that's an impressive name.\n" \ "How about we call you Lorem? Hi, Lorem!\n\n" self.assertEqual(output.getvalue(), long_name_error, "The long name was not shortened") if len(name) > 20: self.assertTrue(False, "The name is still too long.") return self.assertIn(name, long_name)
def __init__(self): self.view = View() self.model = Game()
class Connect4: """The Controller for the Connect 4 game.""" def __init__(self): self.view = View() self.model = Game() def main(self): # Clear the console, and print the instructions. self.view.show_instructions() # Create the two players self.create_player('\u25cf') self.create_player('\u25cb') self.view.welcome_players(self.model.get_current_player().name) while not self.update_player(): move = -1 msg = "" while move < 0: self.view.print_board(self.model.get_board()) move = self.get_move(self.model.get_current_player().name, msg) if self.check_move(self.model.get_board(), move): row = self.model.update_board(move) self.model.get_board()[move][row] = ' ' self.view.animate_turn( move, row, self.model.get_current_player().token, self.model.get_board()) else: msg = "That move is not legal; the column is full." move = -1 is_won = self.winner_check(self.model.get_board(), self.model.get_current_player().token) if is_won: self.view.declare_awesome(self.model.get_current_player().name) break else: self.view.declare_sadness() def create_player(self, token): """ Create a player by prompting for their name, and then storing that in the model Game. :param token: color this player's pieces will be :return: Returns None """ name = self.view.get_player_name() self.model.add_player(name, token) def get_move(self, name, msg=""): """ Gets a move from the user to pass to the game. :return: the column of Player's choice (int) """ return self.view.get_player_move(name, msg) - 1 def check_move(self, board, column): """ Verifies that a chosen move is legal. :param board: current state of the game board :param column: which column the player wants :return: whether the move is legal (bool) """ # Check to make sure the column is not full. if board[column][5] == ' ': return True return False def winner_check(self, board, token): """ Looks to see if the player that just moved won. :param board: current state of the game board :return: whether the move is legal (bool) """ # Vertical # for each column, make it a string, look for substring of four in a row for col in board: if (token * 4) in ''.join(col): return True # Horizontal for i in range(6): if (token * 4) in ''.join(column[i] for column in board): return True # Diagonal Right for i, j in product(range(4), range(3)): if (token * 4) in ''.join(board[i + k][j + k] for k in range(4)): return True # Diagonal Left for i, j in product(range(4), range(3, 6)): if (token * 4) in ''.join(board[i + k][j - k] for k in range(4)): return True # No solution found, so return False return False def update_player(self): """ Asks the game to update the turn and whether the game has ended in a tie. :return: whether the game ended in a tie (bool) """ self.model.update_player() if self.model.turn == 42: return True return False
def setUp(self): self.view = View()
class Connect4GetPlayerMoveTest(unittest.TestCase): """Test the Get Player Move method from the View""" def setUp(self): self.view = View() def tearDown(self): del self.view @patch('builtins.input', return_value="1") def test_with_no_message(self, inputted_value): """ Ensures that the view correctly handles a standard move input. :param inputted_value: string passed in by mock.patch :return: Returns None """ return_value = self.view.get_player_move("Rob-E") self.assertEqual(return_value, 1, "get_player_move should return 1") @patch('builtins.input', return_value="3") @patch('sys.stdout', new_callable=StringIO) def test_with_message(self, output, inputted_value): """ Ensures that the view correctly handles a passed in message. :param inputted_value: string passed in by mock.patch :param output: string printed to stdout by the function :return: Returns None """ return_value = self.view.get_player_move("Rob-E", msg="Not so much.") self.assertEqual( output.getvalue(), "Not so much.\n", "Message is not printing correctly" ) self.assertEqual(return_value, 3, "get_player_move should return 3") @patch('builtins.input', side_effect=["", "4"]) @patch('sys.stdout', new_callable=StringIO) def test_with_no_response(self, output, inputted_value): """ Ensures that the view will reject an empty string as a move and press for a new answer. :param inputted_value: strings passed in by mock.patch :param output: strings printed to stdout by the function :return: Returns None """ return_value = self.view.get_player_move("Rob-E") self.assertEqual( output.getvalue(), "Please type only an integer from 1 to 7, Rob-E.\n", "Empty string warning is incorrect" ) self.assertEqual(return_value, 4, "get_player_move should return 4") @patch('builtins.input', side_effect=["8", "0", "2"]) @patch('sys.stdout', new_callable=StringIO) def test_with_invalid_int(self, output, inputted_value): """ Ensures that the view properly rejects invalid integers. :param inputted_value: strings passed in by mock.patch :return: Returns None """ return_value = self.view.get_player_move("Rob-E") self.assertEqual( output.getvalue(), "Please type only an integer from 1 to 7, Rob-E.\n" "Please type only an integer from 1 to 7, Rob-E.\n", "Fails to berate for integers outside of board's range" ) self.assertEqual( return_value, 2, "Fails to get correct integer after invalid one" ) @patch('builtins.input', side_effect=["seven", "6"]) @patch('sys.stdout', new_callable=StringIO) def test_with_invalid_int(self, output, inputted_value): """ Ensures that the view properly rejects invalid integers. :param inputted_value: strings passed in by mock.patch :return: Returns None """ return_value = self.view.get_player_move("Rob-E") self.assertEqual( output.getvalue(), "Please type only an integer from 1 to 7, Rob-E.\n", "Fails to berate for strs that cannot be cast as ints" ) self.assertEqual( return_value, 6, "Fails to get correct integer after invalid str" )
class StaticPrintStatements(unittest.TestCase): """Test that the needed static strings are printed correctly.""" def setUp(self): self.view = View() def tearDown(self): del self.view @patch('sys.stdout', new_callable=StringIO) def test_show_instructions(self, output): """ Prints the instructions for how to play the game. :return: Returns None """ self.view.show_instructions() instructions = "Let's play Connect 4! The first player to connect 4" \ "tokens of their color vertically, horizontally, " \ "or diagonally, wins. Players will alternate turns " \ "dropping checkers into the board, trying their " \ "best to win.\n\nOn a player's turn, they will be " \ "prompted to enter the column they want their piece " \ "dropped into.\n" self.assertEqual(instructions, output.getvalue()) @patch('sys.stdout', new_callable=StringIO) def test_declare_sadness(self, output): """ Prints a very sad message that the players have tied. :return: Returns None """ self.view.declare_sadness() sadness = "Well, you both should be filled with an immense sadness," \ " as there is no winner. You have tied.\n" self.assertEqual(sadness, output.getvalue()) @patch('sys.stdout', new_callable=StringIO) def test_declare_awesome(self, output): """ Prints that someone is AWESOME, because they won the game. :return: Returns None """ self.view.declare_awesome("Vlaad") awesome = "Woo! Who's awesome? Vlaad's AWESOME, cause Vlaad won!!\n" self.assertEqual(output.getvalue(), awesome) @patch('sys.stdout', new_callable=StringIO) def test_print_board(self, output): board = [['X', 'O', 'X', ' ', ' ', ' '], ['O', 'X', 'O', ' ', ' ', ' '], ['X', 'O', 'X', ' ', ' ', ' '], ['O', 'X', 'O', 'X', ' ', ' '], ['X', 'O', 'X', ' ', ' ', ' '], ['X', 'O', 'X', ' ', ' ', ' '], ['O', 'X', ' ', ' ', ' ', ' ']] self.view.print_board(board) board_output = "\rConnect 4 Game:\n\n"\ " 1 2 3 4 5 6 7 \n"\ " | | | | | | | |\n"\ " | | | | | | | |\n"\ " | | | |X| | | |\n"\ " |X|O|X|O|X|X| |\n"\ " |O|X|O|X|O|O|X|\n"\ " |X|O|X|O|X|X|O|\n"\ " |-------------|\n"\ " - -\n" self.assertEqual(board_output, output.getvalue())