oberon/brandubh.py

243 lines
9.0 KiB
Python

# https://boardgamegeek.com/thread/344992/brandubh-or-starting-examine-tafl-games-play
# https://en.wikipedia.org/wiki/Tafl_games
class Brandubh:
def __init__(self, data):
self.game_name = '\nBrandubh\n'
self.pieces = [' a ', ' d ']
self.winner = data['winner']
self.current_player = data['current_ps_turn']
self.piece = self.pieces[data['piece_index']]
self.instructions = """
Brandubh (Pronounced brran-doov)
Instructions:
The first player plays as the attacker (pieces marked 'a').
The second player plays as the defender ('d). The defender has a leader/king ('D).
Objective:
The first player's objective is to capture the second player's leader/king.
The second player's objective is to get the king to one of the corners of the board.
Movement:
All pieces move the same: as many spaces as a player would like in any orthagonal
direction (up, down, left, right). Pieces cannot move through or jump over other
pieces. Only the king can occupy the center square, though other pieces may move
THROUGH the central square.
Capture:
A player may capture an enemy by moving a piece against an enemy piece in a way
that flanks the enemy piece (surrounds on two sides on the same axis). A player
may move their piece between two enemy pieces without being captured. A piece
may also be captured if it is next to a corner piece and an enemy piece is moved
such that it flanks the piece using the corner as a friendly piece to complete
the capture. Either side may use the corners in this way. The king performs
captures the same as any other piece and can be captured the same way as any
other piece.
Gameplay:
Players alternate turns moving one piece per turn until either the king is dead
or the king escapes to a corner.
More information:
https://en.wikipedia.org/wiki/Tafl_games
http://dragonheelslair.com/en/rulesbrandubh.php
"""
self.p1 = data['p1']
self.p2 = data['p2']
self.board = data['board']
self.piece_index = data['piece_index']
def print_challenge_text(self):
print(self.game_name)
if self.winner:
print('{} vs. {}: {} won!'.format(self.p1, self.p2, self.winner))
else:
print('{} vs. {}: {}\'s turn {}'.format(self.p1, self.p2, self.current_player, self.piece))
def alpha_to_number(self, move):
table_str = {"a": 0, "b": 1, "c": 2, "d": 3, "e": 4, "f": 5, "g": 6}
table_num = {"1": 0, "2": 1, "3": 2, "4": 3, "5": 4, "6": 5, "7": 6}
if move[0] in table_str and move[1] in table_num:
return [table_str[move[0]], table_num[move[1]]]
return False
def print_board(self):
letters = ['A','B','C','D','E','F','G']
print('\n\n ' + ''.join([' ' + str(i) + ' ' for i in range(1,8)]))
for x in range(7):
print(' ' + letters[x] + ''.join(self.board[x]))
print('')
def get_input(self):
print("Enter your move as piece then destination (ex 'B1 B5')")
print('Or (q)uit, (f)orfeit, (instructions).\n')
while True:
move = input('> ')
if move in ['q', 'quit', 'exit']:
return False
elif move in ['f', 'forfeit']:
while True:
verify = input('Are you sure (y/n)? ')
if verify in ['y', 'yes']:
self.winner = self.p1 if self.p2 == self.current_player else self.p2
res = {"winner": self.winner, "board": self.board, "move": False, "message": "{} forfeit, {} wins!".format(self.current_player, self.winner)}
return res
elif verify in ['n', 'no']:
print('Forfeit canceled')
return False
elif move in ['i', 'instructions']:
print(self.instructions)
continue
elif len(move) != 5:
issue = 'many' if len(move) > 5 else 'few'
print("Invalid entry, too {} characters".format(issue))
continue
else:
moves = move.split(" ", 1)
if len(moves) != 2:
print("Invalid entry")
continue
moves[0] = list(moves[0].strip().lower())
moves[1] = list(moves[1].strip().lower())
start = self.alpha_to_number(moves[0])
end = self.alpha_to_number(moves[1])
piece = self.board[start[0]][start[1]]
if not start or not end:
print("Invalid coordinates")
continue
if piece.lower() != self.piece:
print("That is not a valid piece to move. Try again...")
continue
validation = self.validate_move(start, end, piece)
if not validation["success"]:
print(validation["error"])
continue
self.do_move(start, end)
self.do_captures(end)
status = self.game_over()
res = {'winner': self.winner, 'board': self.board, 'move': True}
if status:
res["winner"] = status
res["message"] = "{} won the match!".format(status)
else:
res['message'] = "Your move has been placed."
return res
def validate_move(self, beg, end, icon):
king = (icon == " D ")
if beg[0] < 0 or beg[1] < 0 or end[0] < 0 or end[1] < 0 or beg[0] > 6 or beg[1] > 6 or end[0] > 6 or end[1] > 6:
return {
"success": False,
"error": "That move would take you off the board!"
}
if beg[0] != end[0] and beg[1] != end[1]:
return {
"success": False,
"error": "You must move orthagonally..."
}
if not king and end in [[0,0],[0,6],[6,0],[6,6],[3,3]]:
return {
"success": False,
"error": "Only the king ('D') can move to that space!"
}
direction = self.get_direction(beg, end)
run = beg.copy()
while run != end:
run = [run[0] + direction[0], run[1] + direction[1]]
if self.board[run[0]][run[1]] in [' a ', ' d ', ' D ']:
return {
"success": False,
"error": "There is a piece in the way!"
}
return {"success": True, "error": ""}
def do_move(self, beg, end):
end_piece = self.board[end[0]][end[1]]
self.board[end[0]][end[1]] = self.board[beg[0]][beg[1]]
self.board[beg[0]][beg[1]] = end_piece
if beg == [3,3]:
self.board[3][3] = ' * '
def get_direction(self, b, e):
dir1 = e[0] - b[0]
dir2 = e[1] - b[1]
if dir1 > 0:
dir1 = 1
elif dir1 < 0:
dir1 = -1
if dir2 > 0:
dir2 = 1
elif dir2 < 0:
dir2 = -1
return [dir1, dir2]
def do_captures(self, pos):
enemy = self.pieces[abs(self.piece_index - 1)]
friend = self.piece
dirs = [[0,1],[0,-1],[1,0],[-1,0]]
for x in dirs:
start = pos.copy()
remove = []
steps = 0
while True:
prev = start.copy()
start[0] += x[0]
start[1] += x[1]
if start[0] < 0 or start[1] < 0 or start[0] > 6 or start[1] > 6:
break
steps += 1
if steps % 2 == 0:
if self.board[start[0]][start[1]].lower() in [friend, ' * ']:
if start == [3,3] and self.board[start[0]][start[1]] == ' * ':
break
self.board[prev[0]][prev[1]] = ' - '
continue
else:
break
else:
if self.board[start[0]][start[1]].lower() == enemy:
continue
else:
break
def game_over(self):
count = self.count_pieces()
if count[" D "] == 0:
return self.p1
elif count[" a "] == 0:
return self.p2
elif self.board[0][0] == " D " or self.board[0][6] == " D " or self.board[6][0] == " D " or self.board[6][6] == " D ":
return self.p2
return False
def count_pieces(self):
holder = {" a ": 0, " d ": 0, " D ": 0}
for x in self.board:
for y in x:
if y in [" a ", " d ", " D "]:
holder[y] += 1
return holder
def is_king(self, row, col):
piece = self.board[row][col]
if piece == ' D ':
return True
return False