# Reversi

## Game logic

<details>

<summary>Game Logic code</summary>

```python
def init_game(game_settings):
    state = {
        'player_turn_idx': 0,
        # Initial board has 4 pieces already placed
        'game_board': [-1, -1, -1, -1, -1, -1, -1, -1,
                    -1, -1, -1, -1, -1, -1, -1, -1,
                    -1, -1, -1, -1, -1, -1, -1, -1,
                    -1, -1, -1, 0, 1, -1, -1, -1,
                    -1, -1, -1, 1, 0, -1, -1, -1,
                    -1, -1, -1, -1, -1, -1, -1, -1,
                    -1, -1, -1, -1, -1, -1, -1, -1,
                    -1, -1, -1, -1, -1, -1, -1, -1],
        # First move can be in these four places
        # key is valid index to place piece, directions are directions to modify board after placing piece
        'valid_moves': [
            {
                'index': 20, 
                'valid_directions': ['BELOW']
            },
            {
                'index': 29, 
                'valid_directions': ['LEFT']
            },
            {
                'index': 34, 
                'valid_directions': ['RIGHT']
            },
            {
                'index': 43, 
                'valid_directions': ['ABOVE']
            }]
    }
    return state

def place_piece(state, index):
    """
    Places a piece in the game state's user_board parameter.
    If an index is outside the user_boards range or its not the user's turn,
    an exception is thrown.

    state: a reversi game model state created from init_game
    index: the index in user_board that the piece will be placed
    """
    assert type(index) == int, repr(index) + " is not an integer."
    if index < 0 or index > 63:
        raise InvalidActionError("Invalid index.")
    if state['game_board'][index] != -1:
        raise InvalidActionError("A piece has already been placed here.")
    if len(state['valid_moves']) == 0:
        raise InvalidActionError("The game is done.")


    offset_dict = {
        'ABOVE': -8, 
        'BELOW': 8,
        'LEFT': -1, 
        'RIGHT': 1,
        'ABOVE_LEFT': -9, 
        'ABOVE_RIGHT': -7,
        'BELOW_LEFT': 7, 
        'BELOW_RIGHT': 9}

    player_piece = state['player_turn_idx']
    opp_piece = (player_piece + 1) % 2

    valid_move = None
    for pair in state['valid_moves']:
        if index == pair['index']:
            valid_move = pair # Use to modify board
            break
    if valid_move == None:
        raise InvalidActionError("This is not a valid place to put a piece.")
    state['game_board'][valid_move['index']] = player_piece
    state = _modify_board(state, player_piece, valid_move, offset_dict)

    state = _modify_valid_moves(state, opp_piece, player_piece, offset_dict)
    if len(state['valid_moves']) == 0: # If opponent no moves, then you go again (if possible)
        state = _modify_valid_moves(state, player_piece, opp_piece, offset_dict)
    else: # Opponent has valid move
        state['player_turn_idx'] = opp_piece
    # At this point, valid_moves will only be 0 if both users have no moves
    return state

def get_game_result(state):
    """ Returns if a the game represented by state is won, ongoing, or a tie """
    if len(state['valid_moves']) != 0:
        return {
            "game_result": "NoWinnerYet"
        }
    else:
        board = state['game_board']
        num_user1_pieces = sum([x == 0 for x in board])
        num_user2_pieces = sum([x == 1 for x in board])
        if num_user1_pieces == num_user2_pieces: # Board is filled
            return {
                "game_result": "Draw"
            }
        else:
            if num_user1_pieces > num_user2_pieces:
                return {
                    "game_result": "Winner",
                    "winner_idx": 0
                }
            else:
                return {
                    "game_result": "Winner",
                    "winner_idx": 1
                }

def _modify_board(state, player_piece, valid_move, offset_dict):
    """ Changes the color of the pieces in a certain direction for a valid move
    If this function is called, we know that placing a piece at index is valid. """
    for direction in valid_move['valid_directions']:
        increase_interval = offset_dict[direction]
        iterator = increase_interval + valid_move['index']
        while (state['game_board'][iterator] != player_piece):
            state['game_board'][iterator] = player_piece
            iterator += increase_interval
    return state

def _modify_valid_moves(state, player_piece, opp_piece, offset_dict):
    """ Modify state['valid_moves'] for the next move. """
    # TODO implement BFS starting at index's key
    del state['valid_moves'][:] # Empty valid moves list
    for board_pos in range(64):
        if state['game_board'][board_pos] == -1:
            valid_directions_for_index = []
            for direction_enum in offset_dict:
                if _validate_move_generic(state, player_piece, opp_piece, board_pos, offset_dict[direction_enum]):
                    valid_directions_for_index.append(direction_enum)

            if len(valid_directions_for_index) > 0:
                state['valid_moves'].append({
                    'index': board_pos, 
                    'valid_directions': valid_directions_for_index
                })
    return state

def _validate_move_generic(state, player_piece, opp_piece, board_pos, offset_amount):
    """ Check if placing a piece at board_pos is valid when looking at one
    of the eight directions. """
    if _is_out_of_bound(board_pos, offset_amount) or state['game_board'][board_pos + offset_amount] != opp_piece:
        return False
    iterator = board_pos + (offset_amount * 2)
    while _is_within_iterator_bound(board_pos, iterator, offset_amount):
        if state['game_board'][iterator] == player_piece:
            return True
        elif state['game_board'][iterator] == -1:
            return False # Placement of piece will not surround an opponent piece
        iterator += offset_amount
    return False

def _is_out_of_bound(board_pos, offset_amount):
    """ Determine if index is on the boundary of the board. If so, there is no
    need to validate the move in certain directions.
    Assume index is already between 0 and 63, inclusive. """
    if offset_amount == -8: return True if board_pos <= 7 else False
    if offset_amount == 8: return True if board_pos >= 56 else False
    if offset_amount == -1: return True if board_pos % 8 == 0 else False
    if offset_amount == 1: return True if (board_pos - 7) % 8 == 0 else False
    if offset_amount == -9:
        return True if board_pos % 8 == 0 or board_pos <= 7 else False
    if offset_amount == 7:
        return True if board_pos % 8 == 0 or board_pos >= 56 else False
    if offset_amount == -7:
        return True if (board_pos - 7) % 8 == 0 or board_pos <= 7 else False
    if offset_amount == 9:
        return True if (board_pos - 7) % 8 == 0 or board_pos >= 56 else False

def _is_within_iterator_bound(board_pos, iterator, offset_amount):
    if offset_amount == -8: return iterator >= 0
    if offset_amount == 8: return iterator <= 63
    if offset_amount == -1: return iterator >= (board_pos // 8) * 8
    if offset_amount == 1: return iterator <= ((board_pos // 8) * 8) + 7
    if offset_amount == -7: return iterator >= 2 and iterator % 8 != 0
    if offset_amount == -9: return iterator >= 0 and (iterator - 7) % 8 != 0
    if offset_amount == 7: return iterator <= 61 and (iterator - 7) % 8 != 0
    if offset_amount == 9: return iterator <= 63 and iterator % 8 != 0
```

</details>

## Game state schema

```protobuf
message State {
  // Required field to indicate the player who should be making the next move
  // Values = 0 or 1    
  int32 player_turn_idx = 1;

  // An array of size 64 stored in row major order, so index 0 is top left, 
  // index 7 is top right, index 56 is bottom left, and index 63 is bottom right.
  // Values can be -1, 0, or 1, where -1 represents an empty spot, 0 is a piece
  // made by player_idx 0 and 1 is a piece made by player_idx 1.
  repeated int32 game_board = 2;

  // Precomputed array of all the valid moves. Players should select an index from
  // this list to make a move. 
  repeated ValidMove valid_moves = 3;
}
```

```protobuf
/*
Each ValidMove is an index that the current player can place his piece on.
It also contains information about the all directions that will flip the 
opponent pieces. 
*/
message ValidMove {
  int32 index = 1;
  repeated direction valid_directions = 2;
}
```

```protobuf
// The 8 directions to flip opponent disks.
enum direction {
  ABOVE = 0;
  BELOW = 1;
  LEFT = 2;
  RIGHT = 3;
  ABOVE_LEFT = 4;
  ABOVE_RIGHT = 5;
  BELOW_LEFT = 6;
  BELOW_RIGHT = 7;
}
```

## Action schema

```javascript
place_piece(int index)
```

[Start building now](https://botpot.ai/game/reversi)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://doc.botpot.ai/games/explore/reversi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
