parent
b0b372c95d
commit
243f7bbe5d
|
@ -14,7 +14,7 @@ from .constants import EMPTY_BOARD
|
||||||
|
|
||||||
|
|
||||||
class MerelsStorage():
|
class MerelsStorage():
|
||||||
def __init__(self, storage):
|
def __init__(self, topic_name, storage):
|
||||||
"""Instantiate storage field.
|
"""Instantiate storage field.
|
||||||
|
|
||||||
The current database has this form:
|
The current database has this form:
|
||||||
|
@ -30,29 +30,6 @@ class MerelsStorage():
|
||||||
"""
|
"""
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
|
|
||||||
def create_new_game(self, topic_name):
|
|
||||||
""" Creates a new merels game if it doesn't exists yet.
|
|
||||||
|
|
||||||
:param topic_name: Name of the topic
|
|
||||||
:return: True, if the game is successfully created, False if otherwise.
|
|
||||||
"""
|
|
||||||
|
|
||||||
parameters = ("X", 0, 0, EMPTY_BOARD, "", 0)
|
|
||||||
|
|
||||||
# Checks whether the game exists yet
|
|
||||||
# If it exists
|
|
||||||
|
|
||||||
try:
|
|
||||||
if not self.storage.contains(topic_name) or self.storage.get(
|
|
||||||
topic_name) == "":
|
|
||||||
self.storage.put(topic_name, json.dumps(parameters))
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
except KeyError:
|
|
||||||
self.storage.put(topic_name, json.dumps(parameters))
|
|
||||||
return True
|
|
||||||
|
|
||||||
def update_game(self, topic_name, turn, x_taken, o_taken, board, hill_uid,
|
def update_game(self, topic_name, turn, x_taken, o_taken, board, hill_uid,
|
||||||
take_mode):
|
take_mode):
|
||||||
""" Updates the current status of the game to the database.
|
""" Updates the current status of the game to the database.
|
||||||
|
|
|
@ -7,13 +7,13 @@ freely import another modules.
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from zulip_bots.game_handler import BadMoveException
|
||||||
|
|
||||||
from . import database
|
from . import database
|
||||||
from . import mechanics
|
from . import mechanics
|
||||||
|
|
||||||
COMMAND_PATTERN = re.compile(
|
COMMAND_PATTERN = re.compile(
|
||||||
"^(\\w*).*(\\d,\\d).*(\\d,\\d)|^(\\w+).*(\\d,\\d)")
|
"^(\\w*).*(\\d,\\d).*(\\d,\\d)|^(\\w+).*(\\d,\\d)")
|
||||||
|
|
||||||
|
|
||||||
def getInfo():
|
def getInfo():
|
||||||
""" Gets the info on starting the game
|
""" Gets the info on starting the game
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ def getInfo():
|
||||||
return "To start a game, mention me and add `create`. A game will start " \
|
return "To start a game, mention me and add `create`. A game will start " \
|
||||||
"in that topic. "
|
"in that topic. "
|
||||||
|
|
||||||
|
|
||||||
def getHelp():
|
def getHelp():
|
||||||
""" Gets the help message
|
""" Gets the help message
|
||||||
|
|
||||||
|
@ -30,8 +29,6 @@ def getHelp():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return """Commands:
|
return """Commands:
|
||||||
create: Create a new game if it doesn't exist
|
|
||||||
reset: Reset a current game
|
|
||||||
put (v,h): Put a man into the grid in phase 1
|
put (v,h): Put a man into the grid in phase 1
|
||||||
move (v,h) -> (v,h): Moves a man from one point to -> another point
|
move (v,h) -> (v,h): Moves a man from one point to -> another point
|
||||||
take (v,h): Take an opponent's man from the grid in phase 2/3
|
take (v,h): Take an opponent's man from the grid in phase 2/3
|
||||||
|
@ -39,76 +36,65 @@ take (v,h): Take an opponent's man from the grid in phase 2/3
|
||||||
v: vertical position of grid
|
v: vertical position of grid
|
||||||
h: horizontal position of grid"""
|
h: horizontal position of grid"""
|
||||||
|
|
||||||
|
|
||||||
def unknown_command():
|
def unknown_command():
|
||||||
"""Returns an unknown command info
|
"""Returns an unknown command info
|
||||||
|
|
||||||
:return: A string containing info about available commands
|
:return: A string containing info about available commands
|
||||||
"""
|
"""
|
||||||
return "Unknown command. Available commands: create, reset, help, " \
|
message = "Unknown command. Available commands: " \
|
||||||
"put (v,h), take (v,h), move (v,h) -> (v,h)"
|
"put (v,h), take (v,h), move (v,h) -> (v,h)"
|
||||||
|
raise BadMoveException(message)
|
||||||
|
|
||||||
|
# def beat(message, topic_name, merels_storage):
|
||||||
def beat(message, topic_name, merels_storage):
|
def beat(message, topic_name, merels_storage):
|
||||||
""" This gets triggered every time a user send a message in any topic
|
""" This gets triggered every time a user send a message in any topic
|
||||||
|
|
||||||
:param message: User's message
|
:param message: User's message
|
||||||
:param topic_name: User's current topic
|
:param topic_name: User's current topic
|
||||||
:param merels_storage: Merels' storage
|
:param merels_storage: Merels' storage
|
||||||
:return: A response string to reply to that topic, if any. If not, it
|
:return: a tuple of response string and message, non-empty string
|
||||||
returns an empty string
|
we want to keep the turn of the same played,
|
||||||
|
an empty string otherwise.
|
||||||
"""
|
"""
|
||||||
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
merels = database.MerelsStorage(merels_storage)
|
|
||||||
|
|
||||||
if "create" in message.lower():
|
|
||||||
return mechanics.create_room(topic_name, merels_storage)
|
|
||||||
|
|
||||||
if "help" in message.lower():
|
|
||||||
return getHelp()
|
|
||||||
|
|
||||||
if "reset" in message.lower():
|
|
||||||
if merels.get_game_data(topic_name) is not None:
|
|
||||||
return mechanics.reset_game(topic_name, merels_storage)
|
|
||||||
else:
|
|
||||||
return "No game created yet."
|
|
||||||
|
|
||||||
match = COMMAND_PATTERN.match(message)
|
match = COMMAND_PATTERN.match(message)
|
||||||
|
same_player_move = "" # message indicating move of the same player
|
||||||
|
|
||||||
if match is None:
|
if match is None:
|
||||||
return unknown_command()
|
return unknown_command()
|
||||||
|
|
||||||
# Matches when user types the command in format of: "command v,h -> v,
|
|
||||||
# h" or something similar that has three arguments
|
|
||||||
|
|
||||||
if merels.get_game_data(topic_name) is not None:
|
|
||||||
if match.group(1) is not None and match.group(
|
if match.group(1) is not None and match.group(
|
||||||
2) is not None and match.group(3) is not None:
|
2) is not None and match.group(3) is not None:
|
||||||
responses = ""
|
|
||||||
|
|
||||||
|
responses = ""
|
||||||
command = match.group(1)
|
command = match.group(1)
|
||||||
|
|
||||||
if command.lower() == "move":
|
if command.lower() == "move":
|
||||||
|
|
||||||
p1 = [int(x) for x in match.group(2).split(",")]
|
p1 = [int(x) for x in match.group(2).split(",")]
|
||||||
p2 = [int(x) for x in match.group(3).split(",")]
|
p2 = [int(x) for x in match.group(3).split(",")]
|
||||||
|
|
||||||
# Note that it doesn't have to be "move 1,1 -> 1,2".
|
|
||||||
# It can also just be "move 1,1 1,2"
|
|
||||||
if mechanics.get_take_status(topic_name, merels_storage) == 1:
|
if mechanics.get_take_status(topic_name, merels_storage) == 1:
|
||||||
responses += "Take is required to proceed. Please try " \
|
|
||||||
"again.\n"
|
raise BadMoveException("Take is required to proceed."
|
||||||
else:
|
" Please try again.\n")
|
||||||
|
|
||||||
responses += mechanics.move_man(topic_name, p1, p2,
|
responses += mechanics.move_man(topic_name, p1, p2,
|
||||||
merels_storage) + "\n"
|
merels_storage) + "\n"
|
||||||
responses += after_event_checkup(responses, topic_name,
|
no_moves = after_event_checkup(responses, topic_name, merels_storage)
|
||||||
merels_storage)
|
|
||||||
|
|
||||||
mechanics.update_hill_uid(topic_name, merels_storage)
|
mechanics.update_hill_uid(topic_name, merels_storage)
|
||||||
else:
|
|
||||||
responses += unknown_command()
|
|
||||||
|
|
||||||
responses += mechanics.display_game(topic_name,
|
responses += mechanics.display_game(topic_name, merels_storage) + "\n"
|
||||||
merels_storage) + "\n"
|
|
||||||
return responses
|
if no_moves != "":
|
||||||
|
same_player_move = no_moves
|
||||||
|
|
||||||
|
else:
|
||||||
|
return unknown_command()
|
||||||
|
|
||||||
|
if mechanics.get_take_status(topic_name, merels_storage) == 1:
|
||||||
|
same_player_move = "Take is required to proceed.\n"
|
||||||
|
return responses, same_player_move
|
||||||
|
|
||||||
elif match.group(4) is not None and match.group(5) is not None:
|
elif match.group(4) is not None and match.group(5) is not None:
|
||||||
command = match.group(4)
|
command = match.group(4)
|
||||||
p1 = [int(x) for x in match.group(5).split(",")]
|
p1 = [int(x) for x in match.group(5).split(",")]
|
||||||
|
@ -118,46 +104,43 @@ def beat(message, topic_name, merels_storage):
|
||||||
responses = ""
|
responses = ""
|
||||||
|
|
||||||
if mechanics.get_take_status(topic_name, merels_storage) == 1:
|
if mechanics.get_take_status(topic_name, merels_storage) == 1:
|
||||||
responses += "Take is required to proceed. Please try " \
|
raise BadMoveException("Take is required to proceed."
|
||||||
"again.\n"
|
" Please try again.\n")
|
||||||
else:
|
|
||||||
responses += mechanics.put_man(topic_name, p1[0], p1[1],
|
responses += mechanics.put_man(topic_name, p1[0], p1[1],
|
||||||
merels_storage) + "\n"
|
merels_storage) + "\n"
|
||||||
responses += after_event_checkup(responses, topic_name,
|
no_moves = after_event_checkup(responses, topic_name, merels_storage)
|
||||||
merels_storage)
|
|
||||||
|
|
||||||
mechanics.update_hill_uid(topic_name, merels_storage)
|
mechanics.update_hill_uid(topic_name, merels_storage)
|
||||||
responses += mechanics.display_game(topic_name,
|
|
||||||
merels_storage) + "\n"
|
responses += mechanics.display_game(topic_name, merels_storage) + "\n"
|
||||||
return responses
|
|
||||||
|
if no_moves != "":
|
||||||
|
same_player_move = no_moves
|
||||||
|
if mechanics.get_take_status(topic_name, merels_storage) == 1:
|
||||||
|
same_player_move = "Take is required to proceed.\n"
|
||||||
|
return responses, same_player_move
|
||||||
# take 5,3
|
# take 5,3
|
||||||
elif command == "take":
|
elif command == "take":
|
||||||
responses = ""
|
responses = ""
|
||||||
if mechanics.get_take_status(topic_name, merels_storage) == 1:
|
if mechanics.get_take_status(topic_name, merels_storage) == 1:
|
||||||
responses += mechanics.take_man(topic_name, p1[0], p1[1],
|
responses += mechanics.take_man(topic_name, p1[0], p1[1],
|
||||||
merels_storage) + "\n"
|
merels_storage) + "\n"
|
||||||
if not ("Failed" in responses):
|
if "Failed" in responses:
|
||||||
mechanics.update_toggle_take_mode(topic_name,
|
raise BadMoveException(responses)
|
||||||
merels_storage)
|
mechanics.update_toggle_take_mode(topic_name, merels_storage)
|
||||||
mechanics.update_change_turn(topic_name,
|
no_moves = after_event_checkup(responses, topic_name, merels_storage)
|
||||||
merels_storage)
|
|
||||||
|
|
||||||
mechanics.update_hill_uid(topic_name, merels_storage)
|
mechanics.update_hill_uid(topic_name, merels_storage)
|
||||||
responses += check_win(topic_name, merels_storage)
|
|
||||||
if not ("win" in responses.lower()):
|
|
||||||
responses += mechanics.display_game(topic_name,
|
|
||||||
merels_storage) \
|
|
||||||
+ "\n"
|
|
||||||
|
|
||||||
return responses
|
responses += mechanics.display_game(topic_name, merels_storage) + "\n"
|
||||||
|
|
||||||
|
if no_moves != "":
|
||||||
|
same_player_move = no_moves
|
||||||
|
return responses, same_player_move
|
||||||
else:
|
else:
|
||||||
return "Taking is not possible."
|
raise BadMoveException("Taking is not possible.")
|
||||||
else:
|
else:
|
||||||
return unknown_command()
|
return unknown_command()
|
||||||
else:
|
|
||||||
return "No game created yet. You cannot do any of the game commands." \
|
|
||||||
" Create the game first."
|
|
||||||
|
|
||||||
|
|
||||||
def check_take_mode(response, topic_name, merels_storage):
|
def check_take_mode(response, topic_name, merels_storage):
|
||||||
"""This checks whether the previous action can result in a take mode for
|
"""This checks whether the previous action can result in a take mode for
|
||||||
|
@ -175,7 +158,6 @@ def check_take_mode(response, topic_name, merels_storage):
|
||||||
else:
|
else:
|
||||||
mechanics.update_change_turn(topic_name, merels_storage)
|
mechanics.update_change_turn(topic_name, merels_storage)
|
||||||
|
|
||||||
|
|
||||||
def check_any_moves(topic_name, merels_storage):
|
def check_any_moves(topic_name, merels_storage):
|
||||||
"""Check whether the player can make any moves, if can't switch to another
|
"""Check whether the player can make any moves, if can't switch to another
|
||||||
player
|
player
|
||||||
|
@ -191,7 +173,6 @@ def check_any_moves(topic_name, merels_storage):
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def after_event_checkup(response, topic_name, merels_storage):
|
def after_event_checkup(response, topic_name, merels_storage):
|
||||||
"""After doing certain moves in the game, it will check for take mode
|
"""After doing certain moves in the game, it will check for take mode
|
||||||
availability and check for any possible moves
|
availability and check for any possible moves
|
||||||
|
@ -205,7 +186,6 @@ def after_event_checkup(response, topic_name, merels_storage):
|
||||||
check_take_mode(response, topic_name, merels_storage)
|
check_take_mode(response, topic_name, merels_storage)
|
||||||
return check_any_moves(topic_name, merels_storage)
|
return check_any_moves(topic_name, merels_storage)
|
||||||
|
|
||||||
|
|
||||||
def check_win(topic_name, merels_storage):
|
def check_win(topic_name, merels_storage):
|
||||||
"""Checks whether the current grid has a winner, if it does, finish the
|
"""Checks whether the current grid has a winner, if it does, finish the
|
||||||
game and remove it from the database
|
game and remove it from the database
|
||||||
|
@ -214,7 +194,7 @@ def check_win(topic_name, merels_storage):
|
||||||
:param merels_storage: Merels' storage
|
:param merels_storage: Merels' storage
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
|
|
||||||
win = mechanics.who_won(topic_name, merels_storage)
|
win = mechanics.who_won(topic_name, merels_storage)
|
||||||
if win != "None":
|
if win != "None":
|
||||||
|
|
|
@ -11,7 +11,7 @@ from .interface import construct_grid
|
||||||
|
|
||||||
class GameData():
|
class GameData():
|
||||||
def __init__(self, game_data=(
|
def __init__(self, game_data=(
|
||||||
'test', 'X', 0, 0, 'NNNNNNNNNNNNNNNNNNNNNNNN', '', 0)):
|
'merels', 'X', 0, 0, 'NNNNNNNNNNNNNNNNNNNNNNNN', '', 0)):
|
||||||
self.topic_name = game_data[0]
|
self.topic_name = game_data[0]
|
||||||
self.turn = game_data[1]
|
self.turn = game_data[1]
|
||||||
self.x_taken = game_data[2]
|
self.x_taken = game_data[2]
|
||||||
|
@ -41,7 +41,7 @@ class GameData():
|
||||||
"""
|
"""
|
||||||
return construct_grid(self.board)
|
return construct_grid(self.board)
|
||||||
|
|
||||||
def get_x_piece_possesed_not_on_grid(self):
|
def get_x_piece_possessed_not_on_grid(self):
|
||||||
"""Gets the amount of X pieces that the player X still have, but not
|
"""Gets the amount of X pieces that the player X still have, but not
|
||||||
put yet on the grid
|
put yet on the grid
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ class GameData():
|
||||||
"""
|
"""
|
||||||
return 9 - self.x_taken - mechanics.get_piece("X", self.grid())
|
return 9 - self.x_taken - mechanics.get_piece("X", self.grid())
|
||||||
|
|
||||||
def get_o_piece_possesed_not_on_grid(self):
|
def get_o_piece_possessed_not_on_grid(self):
|
||||||
"""Gets the amount of X pieces that the player O still have, but not
|
"""Gets the amount of X pieces that the player O still have, but not
|
||||||
put yet on the grid
|
put yet on the grid
|
||||||
|
|
||||||
|
@ -64,8 +64,8 @@ class GameData():
|
||||||
:return: A phase number (1, 2, or 3)
|
:return: A phase number (1, 2, or 3)
|
||||||
"""
|
"""
|
||||||
return mechanics.get_phase_number(self.grid(), self.turn,
|
return mechanics.get_phase_number(self.grid(), self.turn,
|
||||||
self.get_x_piece_possesed_not_on_grid(),
|
self.get_x_piece_possessed_not_on_grid(),
|
||||||
self.get_o_piece_possesed_not_on_grid())
|
self.get_o_piece_possessed_not_on_grid())
|
||||||
|
|
||||||
def switch_turn(self):
|
def switch_turn(self):
|
||||||
"""Switches turn between X and O
|
"""Switches turn between X and O
|
||||||
|
|
|
@ -10,7 +10,7 @@ from . import constants
|
||||||
from . import database
|
from . import database
|
||||||
from . import game_data
|
from . import game_data
|
||||||
from . import interface
|
from . import interface
|
||||||
|
from zulip_bots.game_handler import BadMoveException
|
||||||
|
|
||||||
def is_in_grid(vertical_pos, horizontal_pos):
|
def is_in_grid(vertical_pos, horizontal_pos):
|
||||||
"""Checks whether the cell actually exists or not
|
"""Checks whether the cell actually exists or not
|
||||||
|
@ -228,7 +228,7 @@ def who_won(topic_name, merels_storage):
|
||||||
is winning
|
is winning
|
||||||
"""
|
"""
|
||||||
|
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
data = game_data.GameData(merels.get_game_data(topic_name))
|
data = game_data.GameData(merels.get_game_data(topic_name))
|
||||||
|
|
||||||
if data.get_phase() > 1:
|
if data.get_phase() > 1:
|
||||||
|
@ -275,7 +275,7 @@ def create_room(topic_name, merels_storage):
|
||||||
:param merels_storage: Merels' storage
|
:param merels_storage: Merels' storage
|
||||||
:return: A response string
|
:return: A response string
|
||||||
"""
|
"""
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
|
|
||||||
if merels.create_new_game(topic_name):
|
if merels.create_new_game(topic_name):
|
||||||
response = ""
|
response = ""
|
||||||
|
@ -297,7 +297,7 @@ def display_game(topic_name, merels_storage):
|
||||||
:param merels_storage: Merels' storage
|
:param merels_storage: Merels' storage
|
||||||
:return: A response string
|
:return: A response string
|
||||||
"""
|
"""
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
|
|
||||||
data = game_data.GameData(merels.get_game_data(topic_name))
|
data = game_data.GameData(merels.get_game_data(topic_name))
|
||||||
|
|
||||||
|
@ -309,9 +309,9 @@ def display_game(topic_name, merels_storage):
|
||||||
take = "No"
|
take = "No"
|
||||||
|
|
||||||
response += interface.graph_grid(data.grid()) + "\n"
|
response += interface.graph_grid(data.grid()) + "\n"
|
||||||
response += """Phase {}, {}'s turn. Take mode: {}.
|
response += """Phase {}. Take mode: {}.
|
||||||
X taken: {}, O taken: {}.
|
X taken: {}, O taken: {}.
|
||||||
""".format(data.get_phase(), data.turn, take, data.x_taken, data.o_taken)
|
""".format(data.get_phase(), take, data.x_taken, data.o_taken)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -323,7 +323,7 @@ def reset_game(topic_name, merels_storage):
|
||||||
:param merels_storage: Merels' storage
|
:param merels_storage: Merels' storage
|
||||||
:return: A response string
|
:return: A response string
|
||||||
"""
|
"""
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
|
|
||||||
merels.remove_game(topic_name)
|
merels.remove_game(topic_name)
|
||||||
return "Game removed.\n" + create_room(topic_name,
|
return "Game removed.\n" + create_room(topic_name,
|
||||||
|
@ -339,7 +339,7 @@ def move_man(topic_name, p1, p2, merels_storage):
|
||||||
:param merels_storage: Merels' storage
|
:param merels_storage: Merels' storage
|
||||||
:return: A response string
|
:return: A response string
|
||||||
"""
|
"""
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
data = game_data.GameData(merels.get_game_data(topic_name))
|
data = game_data.GameData(merels.get_game_data(topic_name))
|
||||||
|
|
||||||
# Get the grid
|
# Get the grid
|
||||||
|
@ -361,8 +361,7 @@ def move_man(topic_name, p1, p2, merels_storage):
|
||||||
return "Moved a man from ({0}, {1}) -> ({2}, {3}) for {4}.".format(
|
return "Moved a man from ({0}, {1}) -> ({2}, {3}) for {4}.".format(
|
||||||
p1[0], p1[1], p2[0], p2[1], data.turn)
|
p1[0], p1[1], p2[0], p2[1], data.turn)
|
||||||
else:
|
else:
|
||||||
return "Failed: That's not a legal move. Please try again."
|
raise BadMoveException("Failed: That's not a legal move. Please try again.")
|
||||||
|
|
||||||
|
|
||||||
def put_man(topic_name, v, h, merels_storage):
|
def put_man(topic_name, v, h, merels_storage):
|
||||||
"""Puts a man into the specified cell in topic_name
|
"""Puts a man into the specified cell in topic_name
|
||||||
|
@ -373,7 +372,7 @@ def put_man(topic_name, v, h, merels_storage):
|
||||||
:param merels_storage: MerelsDatabase object
|
:param merels_storage: MerelsDatabase object
|
||||||
:return: A response string
|
:return: A response string
|
||||||
"""
|
"""
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
data = game_data.GameData(merels.get_game_data(topic_name))
|
data = game_data.GameData(merels.get_game_data(topic_name))
|
||||||
|
|
||||||
# Get the grid
|
# Get the grid
|
||||||
|
@ -393,7 +392,7 @@ def put_man(topic_name, v, h, merels_storage):
|
||||||
data.take_mode)
|
data.take_mode)
|
||||||
return "Put a man to ({0}, {1}) for {2}.".format(v, h, data.turn)
|
return "Put a man to ({0}, {1}) for {2}.".format(v, h, data.turn)
|
||||||
else:
|
else:
|
||||||
return "Failed: That's not a legal put. Please try again."
|
raise BadMoveException("Failed: That's not a legal put. Please try again.")
|
||||||
|
|
||||||
|
|
||||||
def take_man(topic_name, v, h, merels_storage):
|
def take_man(topic_name, v, h, merels_storage):
|
||||||
|
@ -405,7 +404,7 @@ def take_man(topic_name, v, h, merels_storage):
|
||||||
:param merels_storage: Merels' storage
|
:param merels_storage: Merels' storage
|
||||||
:return: A response string
|
:return: A response string
|
||||||
"""
|
"""
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
data = game_data.GameData(merels.get_game_data(topic_name))
|
data = game_data.GameData(merels.get_game_data(topic_name))
|
||||||
|
|
||||||
# Get the grid
|
# Get the grid
|
||||||
|
@ -431,7 +430,7 @@ def take_man(topic_name, v, h, merels_storage):
|
||||||
data.take_mode)
|
data.take_mode)
|
||||||
return "Taken a man from ({0}, {1}) for {2}.".format(v, h, data.turn)
|
return "Taken a man from ({0}, {1}) for {2}.".format(v, h, data.turn)
|
||||||
else:
|
else:
|
||||||
return "Failed: That's not a legal take. Please try again."
|
raise BadMoveException("Failed: That's not a legal take. Please try again.")
|
||||||
|
|
||||||
|
|
||||||
def update_hill_uid(topic_name, merels_storage):
|
def update_hill_uid(topic_name, merels_storage):
|
||||||
|
@ -442,7 +441,7 @@ def update_hill_uid(topic_name, merels_storage):
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
data = game_data.GameData(merels.get_game_data(topic_name))
|
data = game_data.GameData(merels.get_game_data(topic_name))
|
||||||
|
|
||||||
data.hill_uid = get_hills_numbers(data.grid())
|
data.hill_uid = get_hills_numbers(data.grid())
|
||||||
|
@ -460,7 +459,7 @@ def update_change_turn(topic_name, merels_storage):
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
data = game_data.GameData(merels.get_game_data(topic_name))
|
data = game_data.GameData(merels.get_game_data(topic_name))
|
||||||
|
|
||||||
data.switch_turn()
|
data.switch_turn()
|
||||||
|
@ -478,7 +477,7 @@ def update_toggle_take_mode(topic_name, merels_storage):
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
data = game_data.GameData(merels.get_game_data(topic_name))
|
data = game_data.GameData(merels.get_game_data(topic_name))
|
||||||
|
|
||||||
data.toggle_take_mode()
|
data.toggle_take_mode()
|
||||||
|
@ -496,7 +495,7 @@ def get_take_status(topic_name, merels_storage):
|
||||||
:return: 1 or 0
|
:return: 1 or 0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
data = game_data.GameData(merels.get_game_data(topic_name))
|
data = game_data.GameData(merels.get_game_data(topic_name))
|
||||||
|
|
||||||
return data.take_mode
|
return data.take_mode
|
||||||
|
@ -528,7 +527,7 @@ def can_take_mode(topic_name, merels_storage):
|
||||||
:return: True if this turn can trigger take mode, False if otherwise
|
:return: True if this turn can trigger take mode, False if otherwise
|
||||||
"""
|
"""
|
||||||
|
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
data = game_data.GameData(merels.get_game_data(topic_name))
|
data = game_data.GameData(merels.get_game_data(topic_name))
|
||||||
|
|
||||||
current_hill_uid = data.hill_uid
|
current_hill_uid = data.hill_uid
|
||||||
|
@ -570,7 +569,7 @@ def can_make_any_move(topic_name, merels_storage):
|
||||||
:return: True if the player has a way, False if there isn't
|
:return: True if the player has a way, False if there isn't
|
||||||
"""
|
"""
|
||||||
|
|
||||||
merels = database.MerelsStorage(merels_storage)
|
merels = database.MerelsStorage(topic_name, merels_storage)
|
||||||
data = game_data.GameData(merels.get_game_data(topic_name))
|
data = game_data.GameData(merels.get_game_data(topic_name))
|
||||||
|
|
||||||
if data.get_phase() != 1:
|
if data.get_phase() != 1:
|
||||||
|
|
|
@ -1,42 +1,102 @@
|
||||||
from zulip_bots.bots.merels.libraries import game
|
from typing import List, Any
|
||||||
|
from zulip_bots.bots.merels.libraries import (
|
||||||
|
game,
|
||||||
|
mechanics,
|
||||||
|
database,
|
||||||
|
game_data
|
||||||
|
)
|
||||||
|
from zulip_bots.game_handler import GameAdapter, SamePlayerMove
|
||||||
|
|
||||||
|
class Storage(object):
|
||||||
|
data = {}
|
||||||
|
|
||||||
class MerelsBot(object):
|
def __init__(self, topic_name):
|
||||||
"""
|
self.data[topic_name] = '["X", 0, 0, "NNNNNNNNNNNNNNNNNNNNNNNN", "", 0]'
|
||||||
Simulate the merels game to the chat
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
def put(self, topic_name, value: str):
|
||||||
pass
|
self.data[topic_name] = value
|
||||||
|
|
||||||
def usage(self):
|
def get(self, topic_name):
|
||||||
|
return self.data[topic_name]
|
||||||
|
|
||||||
|
class MerelsModel(object):
|
||||||
|
|
||||||
|
def __init__(self, board: Any=None) -> None:
|
||||||
|
self.topic = "merels"
|
||||||
|
self.storage = Storage(self.topic)
|
||||||
|
self.current_board = mechanics.display_game(self.topic, self.storage)
|
||||||
|
self.token = ['O', 'X']
|
||||||
|
|
||||||
|
def determine_game_over(self, players: List[str]) -> str:
|
||||||
|
if self.contains_winning_move(self.current_board):
|
||||||
|
return 'current turn'
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def contains_winning_move(self, board: Any) -> bool:
|
||||||
|
merels = database.MerelsStorage(self.topic, self.storage)
|
||||||
|
data = game_data.GameData(merels.get_game_data(self.topic))
|
||||||
|
|
||||||
|
if data.get_phase() > 1:
|
||||||
|
if (mechanics.get_piece("X", data.grid()) <= 2) or\
|
||||||
|
(mechanics.get_piece("O", data.grid()) <= 2):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def make_move(self, move: str, player_number: int, computer_move: bool=False) -> Any:
|
||||||
|
if self.storage.get(self.topic) == '["X", 0, 0, "NNNNNNNNNNNNNNNNNNNNNNNN", "", 0]':
|
||||||
|
self.storage.put(
|
||||||
|
self.topic,
|
||||||
|
'["{}", 0, 0, "NNNNNNNNNNNNNNNNNNNNNNNN", "", 0]'.format(
|
||||||
|
self.token[player_number]
|
||||||
|
))
|
||||||
|
self.current_board, same_player_move = game.beat(move, self.topic, self.storage)
|
||||||
|
if same_player_move != "":
|
||||||
|
raise SamePlayerMove(same_player_move)
|
||||||
|
return self.current_board
|
||||||
|
|
||||||
|
class MerelsMessageHandler(object):
|
||||||
|
tokens = [':o_button:', ':cross_mark_button:']
|
||||||
|
|
||||||
|
def parse_board(self, board: Any) -> str:
|
||||||
|
return board
|
||||||
|
|
||||||
|
def get_player_color(self, turn: int) -> str:
|
||||||
|
return self.tokens[turn]
|
||||||
|
|
||||||
|
def alert_move_message(self, original_player: str, move_info: str) -> str:
|
||||||
|
return original_player + " :"
|
||||||
|
|
||||||
|
def game_start_message(self) -> str:
|
||||||
|
return game.getHelp()
|
||||||
|
|
||||||
|
class MerelsHandler(GameAdapter):
|
||||||
|
'''
|
||||||
|
You can play merels! Make sure your message starts with
|
||||||
|
"@mention-bot".
|
||||||
|
'''
|
||||||
|
META = {
|
||||||
|
'name': 'merels',
|
||||||
|
'description': 'Lets you play merels against any player.',
|
||||||
|
}
|
||||||
|
|
||||||
|
def usage(self) -> str:
|
||||||
return game.getInfo()
|
return game.getInfo()
|
||||||
|
|
||||||
def handle_message(self, message, bot_handler):
|
def __init__(self) -> None:
|
||||||
room_name = self.compose_room_name(message)
|
game_name = 'Merels'
|
||||||
content = message['content']
|
bot_name = 'merels'
|
||||||
|
move_help_message = ""
|
||||||
|
move_regex = '.*'
|
||||||
|
model = MerelsModel
|
||||||
|
gameMessageHandler = MerelsMessageHandler
|
||||||
|
super(MerelsHandler, self).__init__(
|
||||||
|
game_name,
|
||||||
|
bot_name,
|
||||||
|
move_help_message,
|
||||||
|
move_regex,
|
||||||
|
model,
|
||||||
|
gameMessageHandler,
|
||||||
|
supports_computer=False
|
||||||
|
)
|
||||||
|
|
||||||
response = game.beat(content, room_name, bot_handler.storage)
|
handler_class = MerelsHandler
|
||||||
|
|
||||||
bot_handler.send_reply(message, response)
|
|
||||||
|
|
||||||
def compose_room_name(self, message):
|
|
||||||
room_name = "test"
|
|
||||||
if "type" in message:
|
|
||||||
if message['type'] == "stream":
|
|
||||||
if 'subject' in message:
|
|
||||||
realm = message['sender_realm_str']
|
|
||||||
stream = message['display_recipient']
|
|
||||||
topic = message['subject']
|
|
||||||
room_name = "{}-{}-{}".format(realm, stream, topic)
|
|
||||||
else:
|
|
||||||
# type == "private"
|
|
||||||
realm = message['sender_realm_str']
|
|
||||||
users_list = [recipient['email'] for recipient in message[
|
|
||||||
'display_recipient']]
|
|
||||||
users = "-".join(sorted(users_list))
|
|
||||||
room_name = "{}-{}".format(realm, users)
|
|
||||||
return room_name
|
|
||||||
|
|
||||||
|
|
||||||
handler_class = MerelsBot
|
|
||||||
|
|
|
@ -17,61 +17,12 @@ class TestFollowUpBot(zulip_bots.test_lib.BotTestCase):
|
||||||
message = dict(
|
message = dict(
|
||||||
content='magic',
|
content='magic',
|
||||||
type='stream',
|
type='stream',
|
||||||
|
sender_email="boo@email.com",
|
||||||
|
sender_full_name="boo"
|
||||||
)
|
)
|
||||||
|
|
||||||
res = self.get_response(message)
|
res = self.get_response(message)
|
||||||
|
|
||||||
self.assertEqual(res['content'],
|
self.assertEqual(res['content'],
|
||||||
'Unknown command. Available commands: create, '
|
'You are not in a game at the moment.'
|
||||||
'reset, help, put (v,h), take (v,h), move (v,'
|
' Type `help` for help.')
|
||||||
'h) -> (v,h)')
|
|
||||||
|
|
||||||
def test_help_command(self):
|
|
||||||
message = dict(
|
|
||||||
content='help',
|
|
||||||
type='stream',
|
|
||||||
)
|
|
||||||
|
|
||||||
res = self.get_response(message)
|
|
||||||
|
|
||||||
self.assertEqual(res['content'], "Commands:\ncreate: Create a new "
|
|
||||||
"game if it doesn't exist\nreset: "
|
|
||||||
"Reset a current game\nput (v,"
|
|
||||||
"h): Put a man into the grid in "
|
|
||||||
"phase 1\nmove (v,h) -> (v,"
|
|
||||||
"h): Moves a man from one point to "
|
|
||||||
"-> another point\ntake (v,h): Take "
|
|
||||||
"an opponent's man from the grid in "
|
|
||||||
"phase 2/3\n\nv: vertical position "
|
|
||||||
"of grid\nh: horizontal position of "
|
|
||||||
"grid")
|
|
||||||
|
|
||||||
def test_create_new_game(self):
|
|
||||||
message = dict(
|
|
||||||
content='create',
|
|
||||||
type='stream',
|
|
||||||
subject='test'
|
|
||||||
)
|
|
||||||
|
|
||||||
with mock.patch.object(zulip_bots.bots.merels.merels.MerelsBot,
|
|
||||||
'compose_room_name',
|
|
||||||
return_value="test"):
|
|
||||||
res = self.get_response(message)
|
|
||||||
|
|
||||||
self.assertEqual(res['content'], '''A room has been created in test. Starting game now.
|
|
||||||
` 0 1 2 3 4 5 6
|
|
||||||
0 [ ]---------------[ ]---------------[ ]
|
|
||||||
| | |
|
|
||||||
1 | [ ]---------[ ]---------[ ] |
|
|
||||||
| | | | |
|
|
||||||
2 | | [ ]---[ ]---[ ] | |
|
|
||||||
| | | | | |
|
|
||||||
3 [ ]---[ ]---[ ] [ ]---[ ]---[ ]
|
|
||||||
| | | | | |
|
|
||||||
4 | | [ ]---[ ]---[ ] | |
|
|
||||||
| | | | |
|
|
||||||
5 | [ ]---------[ ]---------[ ] |
|
|
||||||
| | |
|
|
||||||
6 [ ]---------------[ ]---------------[ ]`
|
|
||||||
Phase 1, X's turn. Take mode: No.
|
|
||||||
X taken: 0, O taken: 0.\n ''')
|
|
||||||
|
|
Loading…
Reference in a new issue