## Wrapping up Tic Tac Toe - Learning to Program with Python 3 (basics)

Welcome to part 14 of the Python 3 basics series. Things are definitely cleaning up, we just have a few more things to conquer here. We need to stop players from playing over one another, we need to fix repetition in the win function, we need to fix the scalability of the column #s, and, finally, we need to actually return winners and either quit/restart the game!

To begin, let's solve the players playing over players issue. We just need to make sure the place the person intends to play is not currently occupied. That's do-able in a variety of ways. We'd like to let the user try again if the move is invalid. So far, we've been returning `False` if the player tries something that they can't, so let's just continue that in the `game_board` function. We can just add it near the top:

```def game_board(game_map, player=0, row=0, column=0, just_display=False):

try:
if game_map[row][column] != 0:
print("This space is occupied, try another!")
return False```

Okay, let's add some handling in the main loop for this:

```    while not game_won:
current_player = next(player_cycle)
played = False
while not played:
print(f"Player: {current_player}")
column_choice = int(input("Which column? "))
row_choice = int(input("Which row? "))
played = game_board(game, player=current_player, row=row_choice, column=column_choice)```

Let's test things up to this point now:

```C:\Users\H\Desktop\python3-updated-series>py -3.6 part14.py
0  1  2
0 [0, 0, 0]
1 [0, 0, 0]
2 [0, 0, 0]
Player: 1
Which column? 9
Which row? 9
Did you attempt to play a row or column outside the range of 0,1 or 2? (IndexError)
Player: 1
Which column? 1
Which row? 1
0  1  2
0 [0, 0, 0]
1 [0, 1, 0]
2 [0, 0, 0]
Player: 2
Which column? 1
Which row? 1
This space is occupied, try another!
Player: 2
Which column?```

Great, this seems to be working. Next up, let's fix the repeated "win" check bit.

First, let's just make a quick function:

```    def all_same(l):
if l.count(l) == len(l) and l != 0:
return True
else:
return False```

This will go inside our `win` function and then we can use this with everything here. Example:

```def win(current_game):

def all_same(l):
if l.count(l) == len(l) and l != 0:
return True
else:
return False

# horizontal
for row in game:
print(row)
if all_same(row):
print(f"Player {row} is the winner horizontally!")```

Full win function becomes:

```def win(current_game):

def all_same(l):
if l.count(l) == len(l) and l != 0:
return True
else:
return False

# horizontal
for row in game:
print(row)
if all_same(row):
print(f"Player {row} is the winner horizontally!")

# vertical
for col in range(len(game)):
check = []
for row in game:
check.append(row[col])
if all_same(check):
print(f"Player {check} is the winner vertically!")

# / diagonal
diags = []
for idx, reverse_idx in enumerate(reversed(range(len(game)))):
diags.append(game[idx][reverse_idx])

if all_same(diags):
print(f"Player {diags} has won Diagonally (/)")

# \ diagonal
diags = []
for ix in range(len(game)):
diags.append(game[ix][ix])

if all_same(diags):
print(f"Player {diags} has won Diagonally (\\)")```

We've not saved any lines. In fact, we added some, but this is much cleaner, and, let's say we changed something where no longer the value in the cell needed to not be 0, and instead maybe a space, or something like that. Now, we just need to change this in one place, rather than 4.

Now, let's actually return winners. Again, we can just use our `win` function for this to either return a `True` or `False`. After each of the winning print outs, we can add a `return True`. At the end, we `return False`. Once a function reaches a `return`, it's done running stuff. If we make it all the way to the end, that means no one won. Full function:

```def win(current_game):

def all_same(l):
if l.count(l) == len(l) and l != 0:
return True
else:
return False

# horizontal
for row in game:
print(row)
if all_same(row):
print(f"Player {row} is the winner horizontally!")
return True

# vertical
for col in range(len(game)):
check = []
for row in game:
check.append(row[col])
if all_same(check):
print(f"Player {check} is the winner vertically!")
return True

# / diagonal
diags = []
for idx, reverse_idx in enumerate(reversed(range(len(game)))):
diags.append(game[idx][reverse_idx])

if all_same(diags):
print(f"Player {diags} has won Diagonally (/)")
return True

# \ diagonal
diags = []
for ix in range(len(game)):
diags.append(game[ix][ix])

if all_same(diags):
print(f"Player {diags} has won Diagonally (\\)")
return True

return False```

Now, let's use the `win()` function in our main loop, with something like:

```        if win(game):
game_won = True
again = input("The game is over, would you like to play again? (y/n) ")
if again.lower() == "y":
print("restarting!")
elif again.lower() == "n":
print("Byeeeee!!!")
play = False
else:
print("Not a valid answer, but... c ya!")
play = False```

Full code now:

```import itertools

def win(current_game):

def all_same(l):
if l.count(l) == len(l) and l != 0:
return True
else:
return False

# horizontal
for row in game:
print(row)
if all_same(row):
print(f"Player {row} is the winner horizontally!")
return True

# vertical
for col in range(len(game)):
check = []
for row in game:
check.append(row[col])
if all_same(check):
print(f"Player {check} is the winner vertically!")
return True

# / diagonal
diags = []
for idx, reverse_idx in enumerate(reversed(range(len(game)))):
diags.append(game[idx][reverse_idx])

if all_same(diags):
print(f"Player {diags} has won Diagonally (/)")
return True

# \ diagonal
diags = []
for ix in range(len(game)):
diags.append(game[ix][ix])

if all_same(diags):
print(f"Player {diags} has won Diagonally (\\)")
return True

return False

def game_board(game_map, player=0, row=0, column=0, just_display=False):

try:
if game_map[row][column] != 0:
print("This space is occupied, try another!")
return False

print("   0  1  2")
if not just_display:
game_map[row][column] = player
for count, row in enumerate(game_map):
print(count, row)
return game_map
except IndexError:
print("Did you attempt to play a row or column outside the range of 0,1 or 2? (IndexError)")
return False
except Exception as e:
print(str(e))
return False

play = True
players = [1, 2]
while play:
game = [[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]

game_won = False
player_cycle = itertools.cycle([1, 2])
game_board(game, just_display=True)
while not game_won:
current_player = next(player_cycle)
played = False
while not played:
print(f"Player: {current_player}")
column_choice = int(input("Which column? "))
row_choice = int(input("Which row? "))
played = game_board(game, player=current_player, row=row_choice, column=column_choice)

if win(game):
game_won = True
again = input("The game is over, would you like to play again? (y/n) ")
if again.lower() == "y":
print("restarting!")
elif again.lower() == "n":
print("Byeeeee!!!")
play = False
else:
print("Not a valid answer, but... c ya!")
play = False
```

We're basically done, but we gotta fix the ` print(" 0 1 2")` bit to be dynamic.

How can we do this? We've seen that we can use `+` with strings, and we know about loops. Let's just make a quick basic example. We're going to start with 3 spaces, then we need to add a number and 2 spaces between them. Let's see...

Since we need to start with 3 spaces, and we know that we want to have 2 spaces between each number, we can do something like start with 1 space, then use a for loop to add 2 spaces and a number per loop. Something like:

```s = " "
for i in range(3):
s += "  "+str(i)

print(s)```

The `3` in range can just be dervied from the game's size later.

There's also a string method to achieve this same task called `.join`. Then we can use list comprehension, like:

```s = "   "+"  ".join([str(i) for i in range(3)])
print(s)```

If the list comprehension doesn't make sense to you, use our original example, that's fine!

So, either build s and print it in place of our original print there, or do:

`print("   "+"  ".join([str(i) for i in range(len(game_map))]))`

That's it. We're done. Full code:

```import itertools

def win(current_game):

def all_same(l):
if l.count(l) == len(l) and l != 0:
return True
else:
return False

# horizontal
for row in game:
print(row)
if all_same(row):
print(f"Player {row} is the winner horizontally!")
return True

# vertical
for col in range(len(game)):
check = []
for row in game:
check.append(row[col])
if all_same(check):
print(f"Player {check} is the winner vertically!")
return True

# / diagonal
diags = []
for idx, reverse_idx in enumerate(reversed(range(len(game)))):
diags.append(game[idx][reverse_idx])

if all_same(diags):
print(f"Player {diags} has won Diagonally (/)")
return True

# \ diagonal
diags = []
for ix in range(len(game)):
diags.append(game[ix][ix])

if all_same(diags):
print(f"Player {diags} has won Diagonally (\\)")
return True

return False

def game_board(game_map, player=0, row=0, column=0, just_display=False):

try:
if game_map[row][column] != 0:
print("This space is occupied, try another!")
return False

print("   "+"  ".join([str(i) for i in range(len(game_map))]))
if not just_display:
game_map[row][column] = player
for count, row in enumerate(game_map):
print(count, row)
return game_map
except IndexError:
print("Did you attempt to play a row or column outside the range of 0,1 or 2? (IndexError)")
return False
except Exception as e:
print(str(e))
return False

play = True
players = [1, 2]
while play:
game = [[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]

game_won = False
player_cycle = itertools.cycle([1, 2])
game_board(game, just_display=True)
while not game_won:
current_player = next(player_cycle)
played = False
while not played:
print(f"Player: {current_player}")
column_choice = int(input("Which column? "))
row_choice = int(input("Which row? "))
played = game_board(game, player=current_player, row=row_choice, column=column_choice)

if win(game):
game_won = True
again = input("The game is over, would you like to play again? (y/n) ")
if again.lower() == "y":
print("restarting!")
elif again.lower() == "n":
print("Byeeeee!!!")
play = False
else:
print("Not a valid answer, but... c ya!")
play = False
```

Now, finally, if you desired, you ... cound allow users to dynamically size the game as well, so here's just a bonus. For example:

```game_size = 5

game = []
for i in range(game_size):
row = []
for i in range(game_size):
row.append(0)
game.append(row)

print(game)```
`[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]`

All set. The rest of our code should handle this just fine. We can also actually do the above in all 1 line, again using list comprehension:

```game = [[0 for i in range(game_size)] for i in range(game_size)]
print(game)```

So, we can do the following in our loop:

```play = True
players = [1, 2]
while play:

game_size = int(input("What size game TicTacToe? "))
game = [[0 for i in range(game_size)] for i in range(game_size)]```

Then we have a varying-sized game. Pretty nifty!

Okay, that's enough of the basics of Python syntax. We're *definitely* missing a lot of stuff. For example, a super integral bit to python is the `dictionary`, which is a way to store values by keys, for example. It's just not come up yet, but it exists and is super useful. There are many other useful things like that, which we've just not covered.

That said, you should have a grasp of Python now.

The next tutorial: • Introduction to Python 3 (basics) - Learning to Program with Python 3

• Tuples, Strings, Loops - Learning to Program with Python 3 (basics)

• Lists and Tic Tac Toe Game - Learning to Program with Python 3 (basics)

• Built-in Functions - Learning to Program with Python 3 (basics)

• Indexes and Slices - Learning to Program with Python 3 (basics)

• Functions - Learning to Program with Python 3 (basics)

• Function Parameters and Typing - Learning to Program with Python 3 (basics)

• Mutability Revisited - Learning to Program with Python 3 (basics)

• Error Handling - Learning to Program with Python 3 (basics)

• Calculating Horizontal Winner (tic tac toe) - Learning to Program with Python 3 (basics)

• Vertical Winners - Learning to Program with Python 3 (basics)

• Diagonal Winners - Learning to Program with Python 3 (basics)

• Bringing Things Together - Learning to Program with Python 3 (basics)

• Wrapping up Tic Tac Toe - Learning to Program with Python 3 (basics)
• Conclusion - Learning to Program with Python 3 (basics)