# Dots and Boxes

## Game logic

<details>

<summary>Game Logic code</summary>

```python
import random
def init_game(game_settings):
    size = game_settings.get('Size')
    if size == 'Any':
        size = random.choice(['2x2', '3x3', '7x7'])
    if size == '2x2':
        width = 2
        height = 2
    elif size == '3x3':
        width = 3
        height = 3
    elif size == '7x7':
        width = 7
        height = 7
    new_board = []
    for r in range(height):
        for c in range(width):
            new_board.append({
                'row': r,
                'column': c,
                'closed_box_player_idx': -1,
                'top_line': -1,
                'bottom_line': -1,
                'right_line': -1,
                'left_line': -1
                })
    state = {
        'player_turn_idx': 0,
        'board': new_board,
        'board_width': width,
        'board_height': height
    }
    return state

#User draws a line for the box in (row, column) at the loc location (i.e. top, bottom, left, right) of that box
def draw_line(state, row, column, loc):
    if row < 0 or row >= state['board_height']:
        raise InvalidActionError("invalid (row, column)")
    if column < 0 or column >= state['board_width']:
        raise InvalidActionError("invalid (row, column)")
    if loc != "top" and loc != "bottom" and loc != "left" and loc != "right":
        raise InvalidActionError("invalid location. choose top, bottom, left, or right")
    box_index = row * state['board_width'] + column
    if state['board'][box_index]['closed_box_player_idx'] != -1:
        raise InvalidActionError("this box is already closed")

    player_idx = state['player_turn_idx']
    is_this_box_closed = __update_box(state, player_idx, box_index, loc)
    is_other_box_closed = False

    #Find any neighboring boxes, and update them
    other_box_index = -1
    if loc == "top" and row - 1 > -1:
        opposite_edge = "bottom"
        other_box_index = (row - 1) * state['board_width'] + column
    elif loc == "bottom" and row + 1 < state['board_height']:
        opposite_edge = "top"
        other_box_index = (row + 1) * state['board_width'] + column
    elif loc == "left" and column - 1 > -1:
        opposite_edge = "right"
        other_box_index = row * state['board_width'] + (column - 1)
    elif loc == "right" and column + 1 < state['board_width']:
        opposite_edge = "left"
        other_box_index = row * state['board_width'] + (column + 1)
    if other_box_index != -1:
        is_other_box_closed = __update_box(state, player_idx, other_box_index, opposite_edge)

    if is_this_box_closed or is_other_box_closed:
        state['player_turn_idx'] = player_idx
    else:
        state['player_turn_idx'] = (player_idx + 1) % 2
    
    return state

def __update_box(state, player_idx, box_index, loc):
    INVALID_ACTION_LINE_DRAWN = "someone already drew a line here"
    if loc == "top":
        if state['board'][box_index]['top_line'] != -1:
            raise InvalidActionError(INVALID_ACTION_LINE_DRAWN)
        else:
            state['board'][box_index]['top_line'] = player_idx
    elif loc == "bottom":
        if state['board'][box_index]['bottom_line'] != -1:
            raise InvalidActionError(INVALID_ACTION_LINE_DRAWN)
        else:
            state['board'][box_index]['bottom_line'] = player_idx
    elif loc == "left":
        if state['board'][box_index]['left_line'] != -1:
            raise InvalidActionError(INVALID_ACTION_LINE_DRAWN)
        else:
            state['board'][box_index]['left_line'] = player_idx
    elif loc == "right":
        if state['board'][box_index]['right_line'] != -1:
            raise InvalidActionError(INVALID_ACTION_LINE_DRAWN)
        else:
            state['board'][box_index]['right_line'] = player_idx
    return __check_closed(state, player_idx, box_index)

# Return true if this box is closed
def __check_closed(state, player_idx, box_index):
    if state['board'][box_index]['top_line'] != -1 and state['board'][box_index]['bottom_line'] != -1 and state['board'][box_index]['right_line'] != -1 and state['board'][box_index]['left_line'] != -1:
        state['board'][box_index]['closed_box_player_idx'] = player_idx
        return True
    return False

def get_game_result(state):
    closed_boxes = [box['closed_box_player_idx'] for box in state['board']]
    if (-1 in closed_boxes):
        return {
            "game_result": "NoWinnerYet"
        }
    player1_score = closed_boxes.count(0)
    player2_score = closed_boxes.count(1)
    if (player1_score > player2_score):
        return {
            "game_result": "Winner",
            "winner_idx": 0
        }
    elif (player1_score < player2_score):
        return {
            "game_result": "Winner",
            "winner_idx": 1
        }
    return {
        "game_result": "Draw"
    }
```

</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;

  // The array is stored in row major order, so index 0 is the top left and 
  // last index is the bottom right. In a size 2x2 board, index 1 is the 
  // top right board. 
  repeated Box board = 2;

  // Number of horizontal boxes in the grid
  int32 board_width = 3;

  // Number of vertical boxes in the grid
  int32 board_height = 4;
}
```

```protobuf
/*
Represents a 1x1 box. Closing this box requires 4 lines. 
*/
message Box {
  // The row of this box in the grid of boxes
  int32 row = 1;

  // The column of this box in the grid of boxes
  int32 column = 2;

  // The player who closed this box. Values can be -1, 0, or 1.
  // -1 means the box isn't closed yet.
  int32 closed_box_player_idx = 3;

  // Values can be -1, 0, 1, representing who drew this line on this box.
  int32 top_line = 4;

  // Values can be -1, 0, 1, representing who drew this line on this box.
  int32 bottom_line = 5;

  // Values can be -1, 0, 1, representing who drew this line on this box.
  int32 right_line = 6;

  // Values can be -1, 0, 1, representing who drew this line on this box.
  int32 left_line = 7;
}
```

## Action schema

```javascript
enum Location {
  top = 0;
  bottom = 1;
  left = 2;
  right = 3;
}

draw_line(int row, int column, Location loc)
```

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


---

# 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/dots-and-boxes.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.
