zulip_bots: Migrate connect_four bot to new game_handler.
This commit is contained in:
parent
ec5be8fc7e
commit
3a438cafa9
|
@ -82,7 +82,13 @@ force_include = [
|
|||
"zulip_bots/zulip_bots/bots/salesforce/salesforce.py",
|
||||
"zulip_bots/zulip_bots/bots/salesforce/test_salesforce.py",
|
||||
"zulip_bots/zulip_bots/bots/idonethis/idonethis.py",
|
||||
"zulip_bots/zulip_bots/bots/idonethis/test_idonethis.py"
|
||||
"zulip_bots/zulip_bots/bots/idonethis/test_idonethis.py",
|
||||
"zulip_bots/zulip_bots/bots/connect_four/connect_four.py",
|
||||
"zulip_bots/zulip_bots/bots/connect_four/test_connect_four.py",
|
||||
"zulip_bots/zulip_bots/bots/tictactoe/tictactoe.py",
|
||||
"zulip_bots/zulip_bots/bots/tictactoe/test_tictactoe.py",
|
||||
"zulip_bots/zulip_bots/bots/game_handler_bot/game_handler_bot.py",
|
||||
"zulip_bots/zulip_bots/bots/game_handler_bot/test_game_handler_bot.py",
|
||||
]
|
||||
|
||||
parser = argparse.ArgumentParser(description="Run mypy on files tracked by git.")
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[connect_four]
|
||||
superusers = ["user@example.com"]
|
|
@ -1,10 +1,12 @@
|
|||
from zulip_bots.bots.connect_four.game_adapter import GameAdapter
|
||||
from zulip_bots.game_handler import GameAdapter
|
||||
from zulip_bots.bots.connect_four.controller import ConnectFourModel
|
||||
from typing import Any
|
||||
|
||||
|
||||
class ConnectFourMessageHandler(object):
|
||||
tokens = [':blue_circle:', ':red_circle:']
|
||||
|
||||
def parse_board(self, board):
|
||||
def parse_board(self, board: Any) -> str:
|
||||
# Header for the top of the board
|
||||
board_str = ':one: :two: :three: :four: :five: :six: :seven:'
|
||||
|
||||
|
@ -20,19 +22,17 @@ class ConnectFourMessageHandler(object):
|
|||
|
||||
return board_str
|
||||
|
||||
def get_player_color(self, turn):
|
||||
def get_player_color(self, turn: int) -> str:
|
||||
return self.tokens[turn]
|
||||
|
||||
def alert_move_message(self, original_player, move_info):
|
||||
column_number = move_info
|
||||
return '**' + original_player + ' moved in column ' + str(column_number + 1) + '**.'
|
||||
def alert_move_message(self, original_player: str, move_info: str) -> str:
|
||||
column_number = move_info.replace('move ', '')
|
||||
return original_player + ' moved in column ' + column_number
|
||||
|
||||
def confirm_move_message(self, move_info):
|
||||
column_number = move_info
|
||||
return 'You placed your token in column ' + str(column_number + 1) + '.'
|
||||
def game_start_message(self) -> str:
|
||||
return 'Type `move <column>` to place a token.\n\
|
||||
The first player to get 4 in a row wins!\n Good Luck!'
|
||||
|
||||
def invalid_move_message(self):
|
||||
return 'Please specify a column between 1 and 7 with at least one open spot.'
|
||||
|
||||
class ConnectFourBotHandler(GameAdapter):
|
||||
'''
|
||||
|
@ -42,7 +42,7 @@ class ConnectFourBotHandler(GameAdapter):
|
|||
Four
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
game_name = 'Connect Four'
|
||||
bot_name = 'connect_four'
|
||||
move_help_message = '* To make your move during a game, type\n' \
|
||||
|
@ -51,6 +51,15 @@ class ConnectFourBotHandler(GameAdapter):
|
|||
model = ConnectFourModel
|
||||
gameMessageHandler = ConnectFourMessageHandler
|
||||
|
||||
super(ConnectFourBotHandler, self).__init__(game_name, bot_name, move_help_message, move_regex, model, gameMessageHandler)
|
||||
super(ConnectFourBotHandler, self).__init__(
|
||||
game_name,
|
||||
bot_name,
|
||||
move_help_message,
|
||||
move_regex,
|
||||
model,
|
||||
gameMessageHandler,
|
||||
max_players=2
|
||||
)
|
||||
|
||||
|
||||
handler_class = ConnectFourBotHandler
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from copy import deepcopy
|
||||
from random import randint
|
||||
from functools import reduce
|
||||
from zulip_bots.game_handler import BadMoveException
|
||||
|
||||
|
||||
class ConnectFourModel(object):
|
||||
'''
|
||||
|
@ -8,18 +10,17 @@ class ConnectFourModel(object):
|
|||
Four logic for the Connect Four Bot
|
||||
'''
|
||||
|
||||
blank_board = [
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0]]
|
||||
def __init__(self):
|
||||
self.blank_board = [
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0]
|
||||
]
|
||||
|
||||
current_board = blank_board
|
||||
|
||||
def parse_move(self, move):
|
||||
return int(move) - 1
|
||||
self.current_board = self.blank_board
|
||||
|
||||
def update_board(self, board):
|
||||
self.current_board = deepcopy(board)
|
||||
|
@ -42,12 +43,18 @@ class ConnectFourModel(object):
|
|||
|
||||
return available_moves
|
||||
|
||||
def make_move(self, column_number, token_number):
|
||||
def make_move(self, move, player_number, is_computer=False):
|
||||
if player_number == 1:
|
||||
token_number = -1
|
||||
if player_number == 0:
|
||||
token_number = 1
|
||||
finding_move = True
|
||||
row = 5
|
||||
column = column_number
|
||||
column = int(move.replace('move ', '')) - 1
|
||||
|
||||
while finding_move:
|
||||
if row < 0:
|
||||
raise BadMoveException('Make sure your move is in a column with free space.')
|
||||
if self.current_board[row][column] == 0:
|
||||
self.current_board[row][column] = token_number
|
||||
finding_move = False
|
||||
|
@ -56,7 +63,7 @@ class ConnectFourModel(object):
|
|||
|
||||
return deepcopy(self.current_board)
|
||||
|
||||
def determine_game_over(self, first_player, second_player):
|
||||
def determine_game_over(self, players):
|
||||
def get_horizontal_wins(board):
|
||||
horizontal_sum = 0
|
||||
|
||||
|
@ -111,8 +118,9 @@ class ConnectFourModel(object):
|
|||
|
||||
return 0
|
||||
|
||||
first_player, second_player = players[0], players[1]
|
||||
# If all tokens in top row are filled (its a draw), product != 0
|
||||
top_row_multiple = reduce(lambda x, y: x*y, self.current_board[0])
|
||||
top_row_multiple = reduce(lambda x, y: x * y, self.current_board[0])
|
||||
|
||||
if top_row_multiple != 0:
|
||||
return 'draw'
|
||||
|
@ -126,12 +134,4 @@ class ConnectFourModel(object):
|
|||
elif winner == -1:
|
||||
return second_player
|
||||
|
||||
return False
|
||||
|
||||
def computer_move(self):
|
||||
# @TODO: Make the computer more intelligent
|
||||
# perhaps by implementing minimax
|
||||
available_moves = deepcopy(self.available_moves())
|
||||
final_move = available_moves[randint(0, len(available_moves) - 1)]
|
||||
|
||||
return final_move
|
||||
return ''
|
||||
|
|
|
@ -2,84 +2,4 @@
|
|||
|
||||
The Connect Four bot is a Zulip bot that will allow users
|
||||
to play a game of Connect Four against either another user,
|
||||
or the computer. All games are run within private messages
|
||||
sent between the user(s) and the bot.
|
||||
|
||||
Starting a new game with another user requires a simple command,
|
||||
and the desired opponent's zulip-related email adress:
|
||||
|
||||
```
|
||||
@<bot-name> start game with user@example.com
|
||||
```
|
||||
|
||||
Starting a game with the computer is even simpler:
|
||||
|
||||
```
|
||||
@<bot-name> start game with computer
|
||||
```
|
||||
|
||||
**See Usage for a complete list of commands**
|
||||
|
||||
*Due to design contraints, the Connect Four Bot
|
||||
can only run a single game at a time*
|
||||
|
||||
## Setup
|
||||
|
||||
To set moderators for the bot, modify the connect_four.conf
|
||||
file as shown:
|
||||
|
||||
superusers = ["user@example.com", "user@example2.com", ...]
|
||||
|
||||
Moderators can run ```force reset``` in case any user abuse the bot
|
||||
|
||||
## Usage
|
||||
|
||||
*All commands should be prefaced with* ```@<bot-name>```
|
||||
|
||||
1. ```help``` : provides the user with relevant
|
||||
commands for first time users.
|
||||
|
||||
2. ```status``` : due to design contraints, the
|
||||
bot can only run a single game at a time. This command allows
|
||||
the user to see the current status of the bot, including
|
||||
whether or not the bot is running a game, if the bot is waiting
|
||||
for a player to accept an invitation to play, as well as who
|
||||
is currently using the bot.
|
||||
|
||||
3. ```start game with user@example.com``` : provided
|
||||
that the bot is not running a game, this command can be used to
|
||||
invite another player to play a game of Connect Four with the user.
|
||||
Note that the user must be specified with their email adress, not
|
||||
their username.
|
||||
|
||||
4. ```start game with computer``` : provided that the bot is not
|
||||
running a game, this command will begin a single player game
|
||||
between the user and a computer player. Note that the currently
|
||||
implemented computer plays randomly.
|
||||
|
||||
5. ```accept``` : a command that can only be run by an invited
|
||||
player to accept an invitation to play Connect Four against
|
||||
another user.
|
||||
|
||||
6. ```decline``` : a command that can only be run by an invited
|
||||
player to decline an invitation to play Connect Four against
|
||||
another user.
|
||||
|
||||
7. ```cancel game``` : a command that can only be run by the
|
||||
inviter to withdraw their invitation to play. Especially
|
||||
useful if a player does not respond to an invitation for a
|
||||
long period of time.
|
||||
|
||||
8. ```move <column-number>``` : during a game, a player may run
|
||||
this command on their turn to place a token in the specified
|
||||
column.
|
||||
|
||||
9. ```quit``` : responds with a confirmation message that asks
|
||||
the user to confirm they wish to forfeit the game.
|
||||
|
||||
10. ```confirm quit``` : causes the user that runs this command
|
||||
to forfeit the game.
|
||||
|
||||
11. ```force reset``` : a command that can only be run by the bot
|
||||
owner and moderators (see 'Usage' for specifying). Destroys any
|
||||
game currently being run if users are abusing the bot
|
||||
or the computer.
|
||||
|
|
|
@ -1,547 +0,0 @@
|
|||
import re
|
||||
import json
|
||||
from copy import deepcopy
|
||||
|
||||
class InputVerification(object):
|
||||
def __init__(self, move_regex, superusers):
|
||||
self.move_regex = move_regex
|
||||
self.verified_commands = {
|
||||
'waiting': ['start game with computer', 'start game with \w+@\w+\.\w+'],
|
||||
'inviting': [['withdraw invitation'], ['accept', 'decline']],
|
||||
'playing': [[move_regex, 'quit', 'confirm quit'], ['quit', 'confirm quit']]
|
||||
}
|
||||
self.all_valid_commands = ['help', 'status', 'start game with computer', 'start game with \w+@\w+\.\w+',
|
||||
'withdraw invitation', 'accept', 'decline', self.move_regex, 'quit', 'confirm quit', 'force reset']
|
||||
self.superusers = superusers
|
||||
|
||||
verified_users = []
|
||||
|
||||
def permission_lacking_message(self, command):
|
||||
return 'Sorry, but you can\'t run the command ```' + command + '```'
|
||||
|
||||
def update_commands(self, turn):
|
||||
self.verified_commands['playing'] = [['quit', 'confirm quit'], ['quit', 'confirm quit']]
|
||||
self.verified_commands['playing'][turn].append(self.move_regex)
|
||||
|
||||
def reset_commands(self):
|
||||
self.verified_commands['playing'] = [[self.move_regex, 'quit', 'confirm quit'], ['quit', 'confirm quit']]
|
||||
|
||||
def regex_match_in_array(self, command_array, command):
|
||||
for command_regex in command_array:
|
||||
if re.compile(command_regex).match(command.lower()):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def valid_command(self, command):
|
||||
return self.regex_match_in_array(self.all_valid_commands, command)
|
||||
|
||||
def verify_user(self, user):
|
||||
return user in self.verified_users
|
||||
|
||||
def verify_command(self, user, command, state):
|
||||
if state != 'waiting':
|
||||
command_array = self.verified_commands[state][self.verified_users.index(user)]
|
||||
else:
|
||||
command_array = self.verified_commands[state]
|
||||
|
||||
return self.regex_match_in_array(command_array, command)
|
||||
|
||||
def verify_superuser(self, user):
|
||||
return user in self.superusers
|
||||
|
||||
class StateManager(object):
|
||||
def __init__(self, main_bot_handler):
|
||||
self.users = None
|
||||
self.state = ''
|
||||
self.user_messages = []
|
||||
self.opponent_messages = []
|
||||
self.main_bot_handler = main_bot_handler
|
||||
|
||||
# Updates to the main bot handler that all state managers must use
|
||||
def basic_updates(self):
|
||||
if self.users is not None:
|
||||
self.main_bot_handler.inputVerification.verified_users = self.users
|
||||
|
||||
if self.state:
|
||||
self.main_bot_handler.state = self.state
|
||||
|
||||
self.main_bot_handler.user_messages = self.user_messages
|
||||
|
||||
self.main_bot_handler.opponent_messages = self.opponent_messages
|
||||
|
||||
def reset_self(self):
|
||||
self.users = None
|
||||
self.user_messages = []
|
||||
self.opponent_messages = []
|
||||
self.state = ''
|
||||
|
||||
class GameCreator(StateManager):
|
||||
def __init__(self, main_bot_handler):
|
||||
super(GameCreator, self).__init__(main_bot_handler)
|
||||
self.gameHandler = None
|
||||
self.invitationHandler = None
|
||||
|
||||
def handle_message(self, content, sender):
|
||||
if content == 'start game with computer':
|
||||
self.users = [sender]
|
||||
self.state = 'playing'
|
||||
self.gameHandler = GameHandler(self.main_bot_handler, 'one_player', self.main_bot_handler.model())
|
||||
|
||||
self.user_messages.append('**You started a new game with the computer!**')
|
||||
self.user_messages.append(self.main_bot_handler.gameMessageHandler.parse_board(self.main_bot_handler.model().blank_board))
|
||||
self.user_messages.append(self.gameHandler.your_turn_message())
|
||||
|
||||
elif re.compile('\w+@\w+\.\w+').search(content):
|
||||
opponent = re.compile('(\w+@\w+\.\w+)').search(content).group(1)
|
||||
|
||||
if opponent == sender:
|
||||
self.user_messages.append('You can\'t play against yourself!')
|
||||
self.update_main_bot_handler()
|
||||
return
|
||||
|
||||
self.users = [sender, opponent]
|
||||
self.state = 'inviting'
|
||||
self.gameHandler = GameHandler(self.main_bot_handler, 'two_player', self.main_bot_handler.model())
|
||||
self.invitationHandler = InvitationHandler(self.main_bot_handler)
|
||||
|
||||
self.user_messages.append(self.invitationHandler.confirm_new_invitation(opponent))
|
||||
|
||||
self.opponent_messages.append(self.invitationHandler.alert_new_invitation(sender))
|
||||
|
||||
self.update_main_bot_handler()
|
||||
|
||||
def update_main_bot_handler(self):
|
||||
self.basic_updates()
|
||||
|
||||
self.main_bot_handler.player_cache = self.users
|
||||
|
||||
self.main_bot_handler.gameHandler = deepcopy(self.gameHandler)
|
||||
|
||||
if self.invitationHandler:
|
||||
self.main_bot_handler.invitationHandler = deepcopy(self.invitationHandler)
|
||||
|
||||
self.reset_self()
|
||||
|
||||
class GameHandler(StateManager):
|
||||
def __init__(self, main_bot_handler, game_type, model, board = 'blank', turn = 0):
|
||||
super(GameHandler, self).__init__(main_bot_handler)
|
||||
self.game_type = game_type
|
||||
self.turn = turn
|
||||
self.game_ended = False
|
||||
self.model = model
|
||||
self.board = model.blank_board if board == 'blank' else board
|
||||
self.model.update_board(board)
|
||||
|
||||
def your_turn_message(self):
|
||||
return '**It\'s your move!**\n' +\
|
||||
'type ```move <column-number>``` to make your move\n\n' +\
|
||||
'You are ' + self.main_bot_handler.gameMessageHandler.get_player_color(self.turn)
|
||||
|
||||
def wait_turn_message(self, opponent):
|
||||
return 'Waiting for ' + opponent + ' to move'
|
||||
|
||||
def invalid_move_message(self):
|
||||
return 'That\'s an invalid move. ' + self.main_bot_handler.gameMessageHandler.invalid_move_message()
|
||||
|
||||
def append_game_over_messages(self, result):
|
||||
if result == 'draw':
|
||||
self.user_messages.append('**It\'s a draw!**')
|
||||
self.opponent_messages.append('**It\'s a draw!**')
|
||||
else:
|
||||
if result != 'the Computer':
|
||||
self.user_messages.append('**Congratulations, you win! :tada:**')
|
||||
self.opponent_messages.append('Sorry, but ' + result + ' won :cry:')
|
||||
else:
|
||||
self.user_messages.append('Sorry, but ' + result + ' won :cry:')
|
||||
|
||||
def get_player_token(self, sender):
|
||||
player = self.main_bot_handler.inputVerification.verified_users.index(sender)
|
||||
# This computation will return 1 for player 0, and -1 for player 1, as is expected
|
||||
return (-2) * player + 1
|
||||
|
||||
def toggle_turn(self):
|
||||
self.turn = (-1) * self.turn + 1
|
||||
|
||||
def end_game(self):
|
||||
self.state = 'waiting'
|
||||
self.game_ended = True
|
||||
self.users = []
|
||||
|
||||
def handle_move(self, move_info, token_number, player_one, player_two, computer_play = False):
|
||||
if not self.model.validate_move(move_info):
|
||||
self.user_messages.append(self.invalid_move_message())
|
||||
return
|
||||
|
||||
self.board = self.model.make_move(move_info, token_number)
|
||||
|
||||
if not computer_play:
|
||||
self.user_messages.append(self.main_bot_handler.gameMessageHandler.confirm_move_message(move_info))
|
||||
self.user_messages.append(self.main_bot_handler.gameMessageHandler.parse_board(self.model.current_board))
|
||||
|
||||
self.opponent_messages.append(self.main_bot_handler.gameMessageHandler.alert_move_message(self.sender, move_info))
|
||||
self.opponent_messages.append(self.main_bot_handler.gameMessageHandler.parse_board(self.model.current_board))
|
||||
|
||||
else:
|
||||
self.user_messages.append(self.main_bot_handler.gameMessageHandler.alert_move_message('the Computer', move_info))
|
||||
self.user_messages.append(self.main_bot_handler.gameMessageHandler.parse_board(self.model.current_board))
|
||||
|
||||
game_over = self.model.determine_game_over(player_one, player_two)
|
||||
|
||||
if game_over:
|
||||
self.append_game_over_messages(game_over)
|
||||
self.end_game()
|
||||
|
||||
else:
|
||||
self.toggle_turn()
|
||||
|
||||
self.main_bot_handler.inputVerification.update_commands(self.turn)
|
||||
|
||||
if not computer_play:
|
||||
self.user_messages.append(self.wait_turn_message(self.opponent))
|
||||
|
||||
self.opponent_messages.append(self.your_turn_message())
|
||||
|
||||
else:
|
||||
self.user_messages.append(self.your_turn_message())
|
||||
|
||||
def handle_message(self, content, sender):
|
||||
self.sender = sender
|
||||
move_regex = self.main_bot_handler.inputVerification.move_regex
|
||||
|
||||
if self.game_type == 'two_player':
|
||||
opponent_array = deepcopy(self.main_bot_handler.inputVerification.verified_users)
|
||||
opponent_array.remove(sender)
|
||||
self.opponent = opponent_array[0]
|
||||
else:
|
||||
self.opponent = 'the Computer'
|
||||
|
||||
if content == 'quit':
|
||||
self.user_messages.append('Are you sure you want to quit? You will forfeit the game!\n' +
|
||||
'Type ```confirm quit``` to forfeit.')
|
||||
|
||||
elif content == 'confirm quit':
|
||||
self.end_game()
|
||||
|
||||
self.user_messages.append('**You have forfeit the game**\nSorry, but you lost :cry:')
|
||||
|
||||
self.opponent_messages.append('**' + sender + ' has forfeit the game**\nCongratulations, you win! :tada:')
|
||||
|
||||
elif re.compile(move_regex).match(content):
|
||||
player_one = player_one = self.main_bot_handler.inputVerification.verified_users[0]
|
||||
player_two = 'the Computer' if self.game_type == 'one_player' else self.main_bot_handler.inputVerification.verified_users[1]
|
||||
|
||||
human_move = re.compile(move_regex).search(content).group(1)
|
||||
human_move = self.model.parse_move(human_move)
|
||||
human_token_number = self.get_player_token(sender)
|
||||
|
||||
self.handle_move(human_move, human_token_number, player_one, player_two)
|
||||
|
||||
if not self.game_ended and self.game_type == 'one_player':
|
||||
computer_move = self.model.computer_move()
|
||||
computer_token_number = -1
|
||||
|
||||
self.handle_move(computer_move, computer_token_number, player_one, player_two, computer_play = True)
|
||||
|
||||
self.update_main_bot_handler()
|
||||
|
||||
def update_main_bot_handler(self):
|
||||
if self.game_type == 'one_player':
|
||||
self.opponent_messages = []
|
||||
|
||||
self.basic_updates()
|
||||
|
||||
if self.game_ended:
|
||||
self.main_bot_handler.gameHandler = None
|
||||
|
||||
self.reset_self()
|
||||
|
||||
class InvitationHandler(StateManager):
|
||||
def __init__(self, main_bot_handler):
|
||||
super(InvitationHandler, self).__init__(main_bot_handler)
|
||||
self.game_cancelled = False
|
||||
self.gameHandler = object
|
||||
self.game_name = main_bot_handler.game_name
|
||||
|
||||
def confirm_new_invitation(self, opponent):
|
||||
return 'You\'ve sent an invitation to play ' + self.game_name + ' with ' +\
|
||||
opponent + '. I\'ll let you know when they respond to the invitation'
|
||||
|
||||
def alert_new_invitation(self, challenger):
|
||||
# Since the first player invites, the challenger is always the first player
|
||||
return '**' + challenger + ' has invited you to play a game of ' + self.game_name + '.**\n' +\
|
||||
'Type ```accept``` to accept the game invitation\n' +\
|
||||
'Type ```decline``` to decline the game invitation.'
|
||||
|
||||
def handle_message(self, content, sender):
|
||||
challenger = self.main_bot_handler.inputVerification.verified_users[0]
|
||||
opponent = self.main_bot_handler.inputVerification.verified_users[1]
|
||||
|
||||
if content.lower() == 'accept':
|
||||
self.state = 'playing'
|
||||
|
||||
self.user_messages.append('You accepted the invitation to play with ' + challenger)
|
||||
self.user_messages.append(self.main_bot_handler.gameHandler.wait_turn_message(challenger))
|
||||
|
||||
self.opponent_messages.append('**' + opponent + ' has accepted your invitation to play**')
|
||||
self.opponent_messages.append(self.main_bot_handler.gameMessageHandler.parse_board(self.main_bot_handler.model().blank_board))
|
||||
self.opponent_messages.append(self.main_bot_handler.gameHandler.your_turn_message())
|
||||
|
||||
elif content.lower() == 'decline':
|
||||
self.state = 'waiting'
|
||||
self.users = []
|
||||
self.gameHandler = None
|
||||
|
||||
self.user_messages.append('You declined the invitation to play with ' + challenger)
|
||||
|
||||
self.opponent_messages.append('**' + opponent + ' has declined your invitation to play**\n' +
|
||||
'Invite another player by typing ```start game with user@example.com```')
|
||||
|
||||
elif content.lower() == 'withdraw invitation':
|
||||
self.state = 'waiting'
|
||||
self.users = []
|
||||
self.gameHandler = None
|
||||
|
||||
self.user_messages.append('Your invitation to play ' + opponent + ' has been withdrawn')
|
||||
|
||||
self.opponent_messages.append('**' + challenger + ' has withdrawn his invitation to play you**\n' +
|
||||
'Type ``` start game with ' + challenger + '``` if you would like to play them.')
|
||||
|
||||
self.update_main_bot_handler()
|
||||
|
||||
def update_main_bot_handler(self):
|
||||
self.basic_updates()
|
||||
|
||||
self.main_bot_handler.invitationHandler = None
|
||||
|
||||
if self.gameHandler is None:
|
||||
self.main_bot_handler.gameHandler = self.gameHandler
|
||||
|
||||
self.reset_self()
|
||||
|
||||
class GameAdapter(object):
|
||||
'''
|
||||
Class that serves as a template to easily
|
||||
create one and two player games
|
||||
'''
|
||||
|
||||
def __init__(self, game_name, bot_name, move_help_message, move_regex, model, gameMessageHandler):
|
||||
self.game_name = game_name
|
||||
self.bot_name = bot_name
|
||||
self.move_help_message = move_help_message
|
||||
self.model = model
|
||||
self.gameMessageHandler = gameMessageHandler()
|
||||
self.inputVerification = InputVerification(move_regex, [])
|
||||
|
||||
def get_stored_data(self):
|
||||
return self.bot_handler.storage.get(self.bot_name)
|
||||
|
||||
def update_data(self):
|
||||
self.state = self.data['state']
|
||||
|
||||
if 'users' in self.data:
|
||||
self.inputVerification.verified_users = self.data['users']
|
||||
else:
|
||||
self.inputVerification.verified_users = []
|
||||
|
||||
if self.state == 'inviting':
|
||||
self.invitationHandler = InvitationHandler(self)
|
||||
self.gameHandler = GameHandler(self, self.data['game_type'], self.model())
|
||||
|
||||
elif self.state == 'playing':
|
||||
self.gameHandler = GameHandler(self, self.data['game_type'], self.model(),
|
||||
board = self.data['board'], turn = self.data['turn'])
|
||||
self.inputVerification.update_commands(self.data['turn'])
|
||||
|
||||
def put_stored_data(self):
|
||||
self.data = {}
|
||||
|
||||
self.data['state'] = self.state
|
||||
|
||||
if self.inputVerification.verified_users:
|
||||
self.data['users'] = self.inputVerification.verified_users
|
||||
|
||||
if self.state == 'inviting':
|
||||
self.data['game_type'] = self.gameHandler.game_type
|
||||
|
||||
elif self.state == 'playing':
|
||||
self.data['game_type'] = self.gameHandler.game_type
|
||||
self.data['board'] = self.gameHandler.board
|
||||
self.data['turn'] = self.gameHandler.turn
|
||||
|
||||
self.bot_handler.storage.put(self.bot_name, self.data)
|
||||
|
||||
# Stores the current state of the game. Either 'waiting 'inviting' or 'playing'
|
||||
state = 'waiting'
|
||||
|
||||
# Stores the users, in case one of the state managers modifies the verified users
|
||||
player_cache = []
|
||||
|
||||
# Object-wide storage to the bot_handler to allow custom message-sending function
|
||||
bot_handler = None
|
||||
|
||||
invitationHandler = None
|
||||
gameHandler = None
|
||||
gameCreator = None
|
||||
|
||||
user_messages = []
|
||||
opponent_messages = []
|
||||
|
||||
# Stores a compact version of all data the bot is managing
|
||||
data = {'state': 'waiting'}
|
||||
|
||||
def status_message(self):
|
||||
prefix = '**' + self.game_name + ' Game Status**\n' +\
|
||||
'*If you suspect users are abusing the bot,' +\
|
||||
' please alert the bot owner*\n\n'
|
||||
|
||||
if self.state == 'playing':
|
||||
if self.gameHandler.game_type == 'one_player':
|
||||
message = 'The bot is currently running a single player game' +\
|
||||
' for ' + self.inputVerification.verified_users[0] + '.'
|
||||
|
||||
elif self.gameHandler.game_type == 'two_player':
|
||||
message = 'The bot is currently running a two player game ' +\
|
||||
'between ' + self.inputVerification.verified_users[0] +\
|
||||
' and ' + self.inputVerification.verified_users[1] + '.'
|
||||
|
||||
elif self.state == 'inviting':
|
||||
message = self.inputVerification.verified_users[0] + '\'s' +\
|
||||
' invitation to play ' + self.inputVerification.verified_users[1] +\
|
||||
' is still pending. Wait for the game to finish to play a game.'
|
||||
|
||||
elif self.state == 'waiting':
|
||||
message = '**The bot is not running a game right now!**\n' + \
|
||||
'Type ```start game with user@example.com``` ' +\
|
||||
'to start a game with another user,\n' +\
|
||||
'or type ```start game with computer``` ' +\
|
||||
'to start a game with the computer'
|
||||
|
||||
return prefix + message
|
||||
|
||||
def help_message(self):
|
||||
return '**' + self.game_name + ' Bot Help:**\n' + \
|
||||
'*Preface all commands with @bot-name*\n\n' + \
|
||||
'* To see the current status of the game, type\n' + \
|
||||
'```status```\n' + \
|
||||
'* To start a game against the computer, type\n' + \
|
||||
'```start game with computer```\n' +\
|
||||
'* To start a game against another player, type\n' + \
|
||||
'```start game with user@example.com```\n' + \
|
||||
'* To quit a game at any time, type\n' + \
|
||||
'```quit```\n' + \
|
||||
'* To withdraw an invitation, type\n' + \
|
||||
'```cancel game```\n' + \
|
||||
self.move_help_message
|
||||
|
||||
def send_message(self, user, content):
|
||||
self.bot_handler.send_message(dict(
|
||||
type = 'private',
|
||||
to = user,
|
||||
content = content
|
||||
))
|
||||
|
||||
# Sends messages returned from helper classes, where user, is the user who sent the bot the original messages
|
||||
def send_message_arrays(self, user):
|
||||
if self.opponent_messages:
|
||||
opponent_array = deepcopy(self.player_cache)
|
||||
opponent_array.remove(user)
|
||||
opponent = opponent_array[0]
|
||||
|
||||
for message in self.user_messages:
|
||||
self.send_message(user, message)
|
||||
|
||||
for message in self.opponent_messages:
|
||||
self.send_message(opponent, message)
|
||||
|
||||
self.user_messages = []
|
||||
self.opponent_messages = []
|
||||
|
||||
def parse_message(self, message):
|
||||
content = message['content'].strip()
|
||||
sender = message['sender_email']
|
||||
return (content, sender)
|
||||
|
||||
def usage(self):
|
||||
return '''
|
||||
Bot that allows users to play another user
|
||||
or the computer in a game of ''' + self.game_name + '''
|
||||
|
||||
To see the entire list of commands, type
|
||||
@bot-name help
|
||||
'''
|
||||
|
||||
def initialize(self, bot_handler):
|
||||
self.config_info = bot_handler.get_config_info('connect_four')
|
||||
if self.config_info:
|
||||
self.inputVerification.superusers = json.loads(self.config_info['superusers'])
|
||||
self.gameCreator = GameCreator(self)
|
||||
self.inputVerification.reset_commands()
|
||||
|
||||
if not bot_handler.storage.contains(self.bot_name):
|
||||
bot_handler.storage.put(self.bot_name, self.data)
|
||||
|
||||
def force_reset(self, sender):
|
||||
for user in self.inputVerification.verified_users:
|
||||
self.send_message(user, 'A bot moderator determined you were abusing the bot, and quit your game.'
|
||||
' Please make sure you finish all your games in a timely fashion.')
|
||||
|
||||
self.send_message(sender, 'The game has been force reset')
|
||||
|
||||
self.data = data = {'state': 'waiting'}
|
||||
self.update_data()
|
||||
self.put_stored_data()
|
||||
|
||||
def handle_message(self, message, bot_handler):
|
||||
self.bot_handler = bot_handler
|
||||
|
||||
self.data = self.get_stored_data()
|
||||
self.update_data()
|
||||
|
||||
self.player_cache = self.inputVerification.verified_users
|
||||
content, sender = self.parse_message(message)
|
||||
|
||||
if not self.inputVerification.valid_command(content.lower()):
|
||||
self.send_message(sender, 'Sorry, but I couldn\'t understand your input.\n'
|
||||
'Type ```help``` to see a full list of commands.')
|
||||
return
|
||||
|
||||
elif self.inputVerification.verify_superuser(sender) and content.lower() == 'force reset':
|
||||
self.force_reset(sender)
|
||||
return
|
||||
|
||||
elif content.lower() == 'help' or content == '':
|
||||
self.send_message(sender, self.help_message())
|
||||
return
|
||||
|
||||
elif content.lower() == 'status':
|
||||
self.send_message(sender, self.status_message())
|
||||
return
|
||||
|
||||
elif self.state == 'waiting':
|
||||
if not self.inputVerification.verify_command(sender, content.lower(), 'waiting'):
|
||||
self.send_message(sender, self.inputVerification.permission_lacking_message(content))
|
||||
|
||||
self.gameCreator.handle_message(content, sender)
|
||||
|
||||
elif not self.inputVerification.verify_user(sender):
|
||||
self.send_message(sender, 'Sorry, but other users are already using the bot.'
|
||||
'Type ```status``` to see the current status of the bot.')
|
||||
return
|
||||
|
||||
elif self.state == 'inviting':
|
||||
if not self.inputVerification.verify_command(sender, content.lower(), 'inviting'):
|
||||
self.send_message(sender, self.inputVerification.permission_lacking_message(content))
|
||||
return
|
||||
|
||||
self.invitationHandler.handle_message(content, sender)
|
||||
|
||||
elif self.state == 'playing':
|
||||
if not self.inputVerification.verify_command(sender, content.lower(), 'playing'):
|
||||
self.send_message(sender, self.inputVerification.permission_lacking_message(content))
|
||||
return
|
||||
|
||||
self.gameHandler.handle_message(content, sender)
|
||||
|
||||
self.send_message_arrays(sender)
|
||||
self.put_stored_data()
|
|
@ -3,19 +3,27 @@ from zulip_bots.test_lib import BotTestCase
|
|||
from contextlib import contextmanager
|
||||
from unittest.mock import MagicMock
|
||||
from zulip_bots.bots.connect_four.connect_four import *
|
||||
from typing import Dict, Any, List
|
||||
|
||||
|
||||
class TestConnectFourBot(BotTestCase):
|
||||
bot_name = 'connect_four'
|
||||
|
||||
def make_request_message(self, content, user='foo@example.com'):
|
||||
def make_request_message(
|
||||
self,
|
||||
content: str,
|
||||
user: str='foo@example.com',
|
||||
user_name: str='foo'
|
||||
) -> Dict[str, str]:
|
||||
message = dict(
|
||||
sender_email=user,
|
||||
content=content,
|
||||
sender_full_name=user_name
|
||||
)
|
||||
return message
|
||||
|
||||
# Function that serves similar purpose to BotTestCase.verify_dialog, but allows for multiple responses to be handled
|
||||
def verify_response(self, request, expected_response, response_number, data=None, computer_move=None, user = 'foo@example.com'):
|
||||
def verify_response(self, request: str, expected_response: str, response_number: int, user: str='foo@example.com') -> None:
|
||||
'''
|
||||
This function serves a similar purpose
|
||||
to BotTestCase.verify_dialog, but allows
|
||||
|
@ -26,13 +34,6 @@ class TestConnectFourBot(BotTestCase):
|
|||
bot, bot_handler = self._get_handlers()
|
||||
message = self.make_request_message(request, user)
|
||||
bot_handler.reset_transcript()
|
||||
stash = ConnectFourModel.computer_move
|
||||
|
||||
if data:
|
||||
bot.get_stored_data = MagicMock(return_value = data)
|
||||
|
||||
if computer_move is not None:
|
||||
ConnectFourModel.computer_move = MagicMock(return_value = computer_move)
|
||||
|
||||
bot.handle_message(message, bot_handler)
|
||||
|
||||
|
@ -45,47 +46,53 @@ class TestConnectFourBot(BotTestCase):
|
|||
first_response = responses[response_number]
|
||||
self.assertEqual(expected_response, first_response['content'])
|
||||
|
||||
ConnectFourModel.computer_move = stash
|
||||
def help_message(self) -> str:
|
||||
return '''** Connect Four Bot Help:**
|
||||
*Preface all commands with @**test-bot***
|
||||
* To start a game in a stream (*recommended*), type
|
||||
`start game`
|
||||
* To start a game against another player, type
|
||||
`start game with @<player-name>`
|
||||
* To quit a game at any time, type
|
||||
`quit`
|
||||
* To end a game with a draw, type
|
||||
`draw`
|
||||
* To forfeit a game, type
|
||||
`forfeit`
|
||||
* To see the leaderboard, type
|
||||
`leaderboard`
|
||||
* To withdraw an invitation, type
|
||||
`cancel game`
|
||||
* To make your move during a game, type
|
||||
```move <column-number>```'''
|
||||
|
||||
def help_message(self):
|
||||
return '**Connect Four Bot Help:**\n' + \
|
||||
'*Preface all commands with @bot-name*\n\n' + \
|
||||
'* To see the current status of the game, type\n' + \
|
||||
'```status```\n' + \
|
||||
'* To start a game against the computer, type\n' + \
|
||||
'```start game with computer```\n' + \
|
||||
'* To start a game against another player, type\n' + \
|
||||
'```start game with user@example.com```\n' + \
|
||||
'* To quit a game at any time, type\n' + \
|
||||
'```quit```\n' + \
|
||||
'* To withdraw an invitation, type\n' + \
|
||||
'```cancel game```\n' +\
|
||||
'* To make your move during a game, type\n' + \
|
||||
'```move <column-number>```'
|
||||
def test_static_responses(self) -> None:
|
||||
self.verify_response('help', self.help_message(), 0)
|
||||
|
||||
def no_game_status(self):
|
||||
return '**Connect Four Game Status**\n' + \
|
||||
'*If you suspect users are abusing the bot, please alert the bot owner*\n\n' +\
|
||||
'**The bot is not running a game right now!**\n' +\
|
||||
'Type ```start game with user@example.com``` to start a game with another user,\n' +\
|
||||
'or type ```start game with computer``` to start a game with the computer'
|
||||
|
||||
def inviting_status(self):
|
||||
return '**Connect Four Game Status**\n' +\
|
||||
'*If you suspect users are abusing the bot, please alert the bot owner*\n\n' +\
|
||||
'foo@example.com\'s invitation to play foo2@example.com' +\
|
||||
' is still pending. Wait for the game to finish to play a game.'
|
||||
|
||||
def one_player_status(self):
|
||||
return '**Connect Four Game Status**\n' +\
|
||||
'*If you suspect users are abusing the bot, please alert the bot owner*\n\n' +\
|
||||
'The bot is currently running a single player game for foo@example.com.'
|
||||
|
||||
def two_player_status(self):
|
||||
return '**Connect Four Game Status**\n' +\
|
||||
'*If you suspect users are abusing the bot, please alert the bot owner*\n\n' +\
|
||||
'The bot is currently running a two player game ' +\
|
||||
'between foo@example.com and foo2@example.com.'
|
||||
def test_game_message_handler_responses(self) -> None:
|
||||
board = ':one: :two: :three: :four: :five: :six: :seven:\n\n' + '\
|
||||
:heavy_large_circle: :heavy_large_circle: :heavy_large_circle: :heavy_large_circle: \
|
||||
:heavy_large_circle: :heavy_large_circle: :heavy_large_circle: \n\n\
|
||||
:heavy_large_circle: :heavy_large_circle: :heavy_large_circle: :heavy_large_circle: \
|
||||
:heavy_large_circle: :heavy_large_circle: :heavy_large_circle: \n\n\
|
||||
:heavy_large_circle: :heavy_large_circle: :heavy_large_circle: :heavy_large_circle: \
|
||||
:heavy_large_circle: :heavy_large_circle: :heavy_large_circle: \n\n\
|
||||
:blue_circle: :red_circle: :heavy_large_circle: :heavy_large_circle: :heavy_large_circle: \
|
||||
:heavy_large_circle: :heavy_large_circle: \n\n\
|
||||
:blue_circle: :red_circle: :heavy_large_circle: :heavy_large_circle: :heavy_large_circle: \
|
||||
:heavy_large_circle: :heavy_large_circle: \n\n\
|
||||
:blue_circle: :red_circle: :heavy_large_circle: :heavy_large_circle: :heavy_large_circle: \
|
||||
:heavy_large_circle: :heavy_large_circle: '
|
||||
bot, bot_handler = self._get_handlers()
|
||||
self.assertEqual(bot.gameMessageHandler.parse_board(
|
||||
self.almost_win_board), board)
|
||||
self.assertEqual(
|
||||
bot.gameMessageHandler.get_player_color(1), ':red_circle:')
|
||||
self.assertEqual(bot.gameMessageHandler.alert_move_message(
|
||||
'foo', 'move 6'), 'foo moved in column 6')
|
||||
self.assertEqual(bot.gameMessageHandler.game_start_message(
|
||||
), 'Type `move <column>` to place a token.\n\
|
||||
The first player to get 4 in a row wins!\n Good Luck!')
|
||||
|
||||
blank_board = [
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
|
@ -111,60 +118,12 @@ class TestConnectFourBot(BotTestCase):
|
|||
[0, 0, 0, 0, 0, 0, -1],
|
||||
[0, 0, 0, 0, 0, 0, 1]]
|
||||
|
||||
start_two_player_data = {'state': 'playing', 'game_type': 'two_player', 'board': blank_board, 'users': ['foo@example.com', 'foo2@example.com'], 'turn': 0}
|
||||
start_one_player_data = {'state': 'playing', 'game_type': 'one_player', 'board': blank_board, 'users': ['foo@example.com'], 'turn': 0}
|
||||
end_two_player_data = {'state': 'playing', 'game_type': 'two_player', 'board': almost_win_board, 'users': ['foo@example.com', 'foo2@example.com'], 'turn': 0}
|
||||
end_one_player_data = {'state': 'playing', 'game_type': 'one_player', 'board': almost_win_board, 'users': ['foo@example.com'], 'turn': 0}
|
||||
inviting_two_player_data = {'state': 'inviting', 'game_type': 'two_player', 'board': blank_board, 'users': ['foo@example.com', 'foo2@example.com'], 'turn': 0}
|
||||
draw_data = {'state': 'playing', 'game_type': 'one_player', 'board': almost_draw_board, 'users': ['foo@example.com', 'foo2@example.com'], 'turn': 0}
|
||||
|
||||
def test_static_messages(self):
|
||||
self.verify_response('help', self.help_message(), 0)
|
||||
self.verify_response('status', self.no_game_status(), 0)
|
||||
self.verify_response('status', self.inviting_status(), 0, data=self.inviting_two_player_data)
|
||||
self.verify_response('status', self.one_player_status(), 0, data=self.start_one_player_data)
|
||||
self.verify_response('status', self.two_player_status(), 0, data=self.start_two_player_data)
|
||||
|
||||
def test_start_game(self):
|
||||
self.verify_response('start game with computer', '**You started a new game with the computer!**', 0)
|
||||
self.verify_response('start game with user@example.com', 'You\'ve sent an invitation to play Connect Four with user@example.com. I\'ll let you know when they respond to the invitation', 0)
|
||||
self.verify_response('start game with foo@example.com', 'You can\'t play against yourself!', 0)
|
||||
|
||||
def test_invitation(self):
|
||||
self.verify_response('accept', 'You accepted the invitation to play with foo@example.com', 0, data=self.inviting_two_player_data, user = 'foo2@example.com')
|
||||
self.verify_response('decline', 'You declined the invitation to play with foo@example.com', 0, data=self.inviting_two_player_data, user = 'foo2@example.com')
|
||||
self.verify_response('withdraw invitation', 'Your invitation to play foo2@example.com has been withdrawn', 0, data=self.inviting_two_player_data)
|
||||
|
||||
def test_move(self):
|
||||
self.verify_response('move 8', 'That\'s an invalid move. Please specify a column '
|
||||
'between 1 and 7 with at least one open spot.', 0, data=self.start_two_player_data)
|
||||
self.verify_response('move 1', 'You placed your token in column 1.', 0, data=self.start_two_player_data)
|
||||
self.verify_response('move 1', '**the Computer moved in column 1**.', 3, data=self.start_one_player_data, computer_move=0)
|
||||
|
||||
def test_game_over(self):
|
||||
self.verify_response('move 1', '**Congratulations, you win! :tada:**', 2, data=self.end_two_player_data)
|
||||
self.verify_response('move 3', 'Sorry, but the Computer won :cry:', 5, data=self.end_one_player_data, computer_move=1)
|
||||
self.verify_response('move 7', '**It\'s a draw!**', 2, data = self.draw_data)
|
||||
|
||||
def test_quit(self):
|
||||
self.verify_response('quit', 'Are you sure you want to quit? You will forfeit the game!\n'
|
||||
'Type ```confirm quit``` to forfeit.', 0, data=self.start_two_player_data)
|
||||
self.verify_response('confirm quit', '**You have forfeit the game**\nSorry, but you lost :cry:', 0, data=self.start_two_player_data)
|
||||
|
||||
def test_force_reset(self):
|
||||
with self.mock_config_info({'superusers': '["foo@example.com"]'}):
|
||||
self.verify_response('force reset', 'The game has been force reset', 1, data=self.start_one_player_data)
|
||||
|
||||
def test_privilege_check(self):
|
||||
self.verify_response('move 4', 'Sorry, but you can\'t run the command ```move 4```', 0, data=self.inviting_two_player_data)
|
||||
self.verify_response('start game with computer', 'Sorry, but other users are already using the bot.'
|
||||
'Type ```status``` to see the current status of the bot.', 0, data=self.inviting_two_player_data, user = 'foo3@example.com')
|
||||
self.verify_response('quit', 'Sorry, but you can\'t run the command ```quit```', 0)
|
||||
self.verify_response('accept', 'Sorry, but you can\'t run the command ```accept```', 0, data=self.end_two_player_data)
|
||||
self.verify_response('force reset', 'Sorry, but you can\'t run the command ```force reset```', 0)
|
||||
|
||||
def test_connect_four_logic(self):
|
||||
def confirmAvailableMoves(good_moves, bad_moves, board):
|
||||
def test_connect_four_logic(self) -> None:
|
||||
def confirmAvailableMoves(
|
||||
good_moves: List[int],
|
||||
bad_moves: List[int],
|
||||
board: List[List[int]]
|
||||
) -> None:
|
||||
connectFourModel.update_board(board)
|
||||
|
||||
for move in good_moves:
|
||||
|
@ -173,19 +132,26 @@ class TestConnectFourBot(BotTestCase):
|
|||
for move in bad_moves:
|
||||
self.assertFalse(connectFourModel.validate_move(move))
|
||||
|
||||
def confirmMove(column_number, token_number, initial_board, final_board):
|
||||
def confirmMove(
|
||||
column_number: int,
|
||||
token_number: int,
|
||||
initial_board: List[List[int]],
|
||||
final_board: List[List[int]]
|
||||
) -> None:
|
||||
connectFourModel.update_board(initial_board)
|
||||
test_board = connectFourModel.make_move(column_number, token_number)
|
||||
test_board = connectFourModel.make_move(
|
||||
'move ' + str(column_number), token_number)
|
||||
|
||||
self.assertEqual(test_board, final_board)
|
||||
|
||||
def confirmGameOver(board, result):
|
||||
def confirmGameOver(board: List[List[int]], result: str) -> None:
|
||||
connectFourModel.update_board(board)
|
||||
game_over = connectFourModel.determine_game_over('first_player', 'second_player')
|
||||
game_over = connectFourModel.determine_game_over(
|
||||
['first_player', 'second_player'])
|
||||
|
||||
self.assertEqual(game_over, result)
|
||||
|
||||
def confirmWinStates(array):
|
||||
def confirmWinStates(array: List[List[List[List[int]]]]) -> None:
|
||||
for board in array[0]:
|
||||
confirmGameOver(board, 'first_player')
|
||||
|
||||
|
@ -428,7 +394,8 @@ class TestConnectFourBot(BotTestCase):
|
|||
|
||||
# Test Available Move Logic
|
||||
connectFourModel.update_board(blank_board)
|
||||
self.assertEqual(connectFourModel.available_moves(), [0, 1, 2, 3, 4, 5, 6])
|
||||
self.assertEqual(connectFourModel.available_moves(),
|
||||
[0, 1, 2, 3, 4, 5, 6])
|
||||
|
||||
connectFourModel.update_board(single_column_board)
|
||||
self.assertEqual(connectFourModel.available_moves(), [3])
|
||||
|
@ -437,7 +404,7 @@ class TestConnectFourBot(BotTestCase):
|
|||
self.assertEqual(connectFourModel.available_moves(), [])
|
||||
|
||||
# Test Move Logic
|
||||
confirmMove(0, 1, blank_board,
|
||||
confirmMove(1, 0, blank_board,
|
||||
[[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
|
@ -445,7 +412,7 @@ class TestConnectFourBot(BotTestCase):
|
|||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 0, 0, 0, 0, 0, 0]])
|
||||
|
||||
confirmMove(0, -1, blank_board,
|
||||
confirmMove(1, 1, blank_board,
|
||||
[[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
|
@ -453,7 +420,7 @@ class TestConnectFourBot(BotTestCase):
|
|||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[-1, 0, 0, 0, 0, 0, 0]])
|
||||
|
||||
confirmMove(0, 1, diagonal_board,
|
||||
confirmMove(1, 0, diagonal_board,
|
||||
[[0, 0, 0, 0, 0, 0, 1],
|
||||
[0, 0, 0, 0, 0, 1, 1],
|
||||
[0, 0, 0, 0, 1, 1, 1],
|
||||
|
@ -461,7 +428,7 @@ class TestConnectFourBot(BotTestCase):
|
|||
[0, 0, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1]])
|
||||
|
||||
confirmMove(1, 1, diagonal_board,
|
||||
confirmMove(2, 0, diagonal_board,
|
||||
[[0, 0, 0, 0, 0, 0, 1],
|
||||
[0, 0, 0, 0, 0, 1, 1],
|
||||
[0, 0, 0, 0, 1, 1, 1],
|
||||
|
@ -469,7 +436,7 @@ class TestConnectFourBot(BotTestCase):
|
|||
[0, 1, 1, 1, 1, 1, 1],
|
||||
[0, 1, 1, 1, 1, 1, 1]])
|
||||
|
||||
confirmMove(2, 1, diagonal_board,
|
||||
confirmMove(3, 0, diagonal_board,
|
||||
[[0, 0, 0, 0, 0, 0, 1],
|
||||
[0, 0, 0, 0, 0, 1, 1],
|
||||
[0, 0, 0, 0, 1, 1, 1],
|
||||
|
@ -477,7 +444,7 @@ class TestConnectFourBot(BotTestCase):
|
|||
[0, 0, 1, 1, 1, 1, 1],
|
||||
[0, 1, 1, 1, 1, 1, 1]])
|
||||
|
||||
confirmMove(3, 1, diagonal_board,
|
||||
confirmMove(4, 0, diagonal_board,
|
||||
[[0, 0, 0, 0, 0, 0, 1],
|
||||
[0, 0, 0, 0, 0, 1, 1],
|
||||
[0, 0, 0, 1, 1, 1, 1],
|
||||
|
@ -485,7 +452,7 @@ class TestConnectFourBot(BotTestCase):
|
|||
[0, 0, 1, 1, 1, 1, 1],
|
||||
[0, 1, 1, 1, 1, 1, 1]])
|
||||
|
||||
confirmMove(4, 1, diagonal_board,
|
||||
confirmMove(5, 0, diagonal_board,
|
||||
[[0, 0, 0, 0, 0, 0, 1],
|
||||
[0, 0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 0, 1, 1, 1],
|
||||
|
@ -493,7 +460,7 @@ class TestConnectFourBot(BotTestCase):
|
|||
[0, 0, 1, 1, 1, 1, 1],
|
||||
[0, 1, 1, 1, 1, 1, 1]])
|
||||
|
||||
confirmMove(5, 1, diagonal_board,
|
||||
confirmMove(6, 0, diagonal_board,
|
||||
[[0, 0, 0, 0, 0, 1, 1],
|
||||
[0, 0, 0, 0, 0, 1, 1],
|
||||
[0, 0, 0, 0, 1, 1, 1],
|
||||
|
@ -502,7 +469,7 @@ class TestConnectFourBot(BotTestCase):
|
|||
[0, 1, 1, 1, 1, 1, 1]])
|
||||
|
||||
# Test Game Over Logic:
|
||||
confirmGameOver(blank_board, False)
|
||||
confirmGameOver(blank_board, '')
|
||||
confirmGameOver(full_board, 'draw')
|
||||
|
||||
# Test Win States:
|
||||
|
@ -510,13 +477,3 @@ class TestConnectFourBot(BotTestCase):
|
|||
confirmWinStates(vertical_win_boards)
|
||||
confirmWinStates(major_diagonal_win_boards)
|
||||
confirmWinStates(minor_diagonal_win_boards)
|
||||
|
||||
# Test Computer Move:
|
||||
connectFourModel.update_board(blank_board)
|
||||
self.assertTrue(connectFourModel.computer_move() in [0, 1, 2, 3, 4, 5, 6])
|
||||
|
||||
connectFourModel.update_board(single_column_board)
|
||||
self.assertEqual(connectFourModel.computer_move(), 3)
|
||||
|
||||
connectFourModel.update_board(diagonal_board)
|
||||
self.assertTrue(connectFourModel.computer_move() in [0, 1, 2, 3, 4, 5])
|
||||
|
|
Loading…
Reference in a new issue