# Tic-Tac-Toe - Session 5
By the end of this lesson, you will:
- Learn how to use the `board_string` function.
- be able to understand how the `value` function works and how it calculates the value of a game board for a given player.
### Ice Breaker 🧊
Everyone in the group remains silent. Put yourselves in order of birthday (date, month and year), without talking. After y'a'll have gotten yoursevles into what y'all believe is the correct order, go through the line and share your birthday.
## The value of a board
Last class, we wrote the collect_reachable function together to print all the possible boards that can be reached from a given board.
```python=
def collect_reachable(b: list, player: str) -> list:
# Base case
if is_board_full(b): return [b]
if is_winner(b, "X") or is_winner(b, "O"): return [b]
# Recursive case
# collect immediate children
next_boards = [move(b, row, col, player) for row in range(3) for col in range(3) if b[row][col] == '']
# collect all descendents of grandchild level or greater
collected_for_reached = []
if player == 'X': next_player = 'O'
else: next_player = 'X'
for next_board in next_boards:
collected_for_reached.extend(collect_reachable(next_board, next_player))
collected_for_reached.append(b)
return collected_for_reached
```
```python
example_board = [
["", "O", "O"],
["O", "X", "X"],
["", "", "X"]]
```
If we call the function and pass in `example_board` as an argument:
```python
result = collect_reachable(example_board, "X")
print(len(result))
for b in result:
print(b)
```
It generates 10 boards in total.

To turn our nested list into a familiar Tic-Tac-Toe board format
We will use this helper function:.
```python=
def board_string(board: list) -> str:
def tr(mark: int) -> str: return 'X' if mark == "X" else 'O' if mark == "O" else '_'
return '\n'.join([
''.join([tr(board[i][j])
for j in range(3)])
for i in range(3)])
```
We don't expect you to understand everything that is going on here. Just know that the `board_string` function lets us see the game board in a way that's easy for us to recognize and use.
#### Example:
If the function is provided with the following board:
```
b = [["X", "", "O"],
["O", "X", ""],
["", "O", "X"]]
```
the output would be
```
X_O
OX_
_OX
```
Let's say we comment out the line below,
```
collected_for_reached.append(b)
```
**Discussion:** what do you think is going to happen?
Let's find out what happens when we comment out this line.
**Result**:
- It will print only the leaf nodes.
- So boards that result in a win or a draw.
## Recap
So far, we've built the mechanisms of Tic-Tac-Toe:
1. Making a move
2. Checking terminal states
3. Generating possible boards
**Discussion**: But where do we go from here? What’s missing?
<details>
<summary> <B>Solution</B> </summary>
We need to write a function to determine the value of the board.
</details>
<br>
**Discussion:** why do we care about knowing the value of a board?
**Discussion:** How do we numerically represent the value of a board?
**Numerical Representation**:
- X wins: 1
- O wins: -1
- Draw: 0
### Group Activity:
Consider the following boards. Discuss in your groups what their respective values might be.
#### Board 1:
| **O** | **X** | |
|-------|-------|------|
| **X** | **X** | **X**|
| | **O** | **O**|
#### Board 2:
| **O** | **X** | |
|-------|-------|------|
| **X** | **O** | **X**|
| | **O** | **O**|
#### Board 3:
| **X** | **O** | **X** |
|-------|-------|-------|
| **O** | **X** | **O** |
| **O** | **X** | **O** |
#### Board 4:
| | **O** | **O**|
|-------|-------|------|
| **O** | **X** |**X** |
| | | **X**|
<details>
<summary> <B> Think, then Click</B> </summary>
1. Board 1 value = 1 (X wins)
2. Board 2 value = -1 (O wins)
3. Board 3 value = 0 (Draw)
4. Board 4 value = answer will be on the board
</details>
<br>
## Writing the Value Function
Nice let’s write this function that returns the value of each possible board
```python=
def value(board: list, view: str) -> int:
```
We first need to access all the possible boards that can be generated from this given board.
**Discussion:** What function do we need to call?
```python =
possible_boards = collect_reachable(board, view)
```
Nice, and now we are going to use a list comprehension to assign a value to each full reachable board.
```python
values = [?????? for next_board in possible_boards]
```
**Discussion:** Which function should I call to assign a value to each board?
Use recursion! Call the `value` function, which takes two arguments. The first argument is `next_board`. What is the second argument?
```python=
other_point_of_view: str = "O" if view == "X" else "X"
```
For minimax, you would typically evaluate each child node (or each next_board in this case) from the opposite player's point of view. You'll then either minimize or maximize the value depending on whose turn it is.
Nice, now what is missing?
**Discussion:How do we stop the function from calling itself indefinitely?**
We need to write our base cases. We want the function to stop once it reaches a terminal state. Meaning the board results in a draw or a win.
**Base Cases**:
```python=
if(is_winner(board, "X")):
return 1 # value for X: 1
if(is_winner(board, "O")):
return -1 # value for O: -1
if(is_board_full(board)):
return Draw # no value
```
## Testing
Let's run the function and see what we get.