Two rival secret services compete against each other in the game Codenames: team red against team blue.

The game board consists of a number of cards that are placed face up on the table in a rectangular $$m \times n$$ grid with $$m$$ rows and $$n$$ columns. There are two variations of the game: one with images on the cards (as shown in the left $$4 \times 5$$ grid below) and one with words on the cards (as shown in the right $$5 \times 5$$ grid below). This also shows that the number of rows and columns of the game board may vary.

grid images
Variant of Codenames with images on the cards that make up the game board.
grid words
Variant of Codenames with words on the cards that make up the game board.

Each team chooses one player to be their spymaster. Both spymasters sit on the same side of the table. The other players sit across from their spymasters. They are field operatives that must find out where the spies of their team are located on the game board.

Each game has one key that shows where the spies are located on the game board, and can only be seen by both spymasters. The key corresponds to the cards on the table and also forms a $$m \times n$$ grid with the same number of rows and columns as the game board. Blue squares correspond to cards that the blue team must find (locations of blue spies). Red squares correspond to cards that the red team must find (locations of red spies). Pale squares have innocent bystanders, and the black square hides an assassin!

key
Key for a game of Codenames with a $$4 \times 5$$ grid (red begins).

A key always has one black square and the difference between the number of blue and red squares is equal to one. The team that has to find the largest number of spies comes first. For example, in the above key there are 7 blue squares and 8 red squares, so team red comes first.

Teams take turn, beginning with the starting team. The team taking turn may perform one of two possible actions: select a card or stop. The latter ends the turn and the other team takes turn. In the former case the team's spymaster reveals the color of the key square corresponding to the selected card by placing a colored card on top of the selected card. The color of that square determines which team takes turn next: if the color of the selected card matches the team's color, the team keeps the turn; otherwise the other team takes turn.

The game ends when all spies of a team have been found or if the card of the assassin is selected (black square in key). In the former case the team whose spies have all been found wins the game. In the latter case the team that selected the assassin loses the game.

During the game, a spymaster might give clues that relate to some cards of his own team. How these clues are given is not relevant for this assignment, and is explained in more detail in the rules of the game (images1, words2).

Assignment

In the grid of a game board and a key, the rows are numbered from top to bottom, and the columns from left to right, starting from zero. This allows to represent the position of a card or a square as a tuple $$(r, c)$$, with $$r$$ (int) the row index and $$c$$ (int) the column index.

A key is represented as a string (str) whose successive characters indicate the colors of the squares: an uppercase letter B for a blue square, an uppercase letter R for a red square, a dash (-) for a white square and an uppercase letter X for a black square where the assassin is located. The squares of the key are listed from left to right and from top to bottom.

Define a class GameBoard that can be used to represent game boards of the game Codenames. Three arguments must be passed when instantiating a new game board (GameBoard): i) the number of rows $$m$$ (int) of the grid, ii) the number of columns $$n$$ (int) of the grid and iii) a key (str). It may be assumed that a valid configuration of a game board and a key is passed, without the need to check this explicitly.

The class GameBoard must at least support the following methods:

If the game on the game board (GameBoard) is over, calling the methods turn, stop or select must raise an AssertionError with the message game over.

If a game board (GameBoard) is passed to the built-in function str, a string representation (str) of the game board must be returned. Its format can be derived from the example below. A card that has not yet been selected is represented by a question mark (?). A card that has been selected is represented by the character representing the corresponding square in the key. Representations of neighboring squares are separated by a single space.

Example

>>> codenames = GameBoard(4, 5, 'RRBBBRBRRBB-XR-B-RR-')
>>> print(codenames)
? ? ? ? ?
? ? ? ? ?
? ? ? ? ?
? ? ? ? ?
>>> codenames.turn()
'R'
>>> codenames.isover()
False
>>> print(codenames.select(2, 3))
? ? ? ? ?
? ? ? ? ?
? ? ? R ?
? ? ? ? ?
>>> codenames.turn()
'R'
>>> codenames.stop().turn()
'B'
>>> codenames.isover()
False
>>> print(codenames.select(2, 1))
? ? ? ? ?
? ? ? ? ?
? - ? R ?
? ? ? ? ?
>>> codenames.turn()
'R'
>>> print(codenames.select(3, 4))
? ? ? ? ?
? ? ? ? ?
? - ? R ?
? ? ? ? -
>>> codenames.turn()
'B'
>>> print(codenames.select(1, 1).select(3, 0).select(0, 2).stop())
? ? B ? ?
? B ? ? ?
? - ? R ?
B ? ? ? -
>>> codenames.turn()
'R'
>>> codenames.isover()
False
>>> print(codenames.select(2, 2))
? ? B ? ?
? B ? ? ?
? - X R ?
B ? ? ? -
>>> codenames.isover()
True
>>> codenames.turn()
Traceback (most recent call last):
AssertionError: game over
>>> codenames.stop()
Traceback (most recent call last):
AssertionError: game over
>>> codenames.select(1, 4)
Traceback (most recent call last):
AssertionError: game over