Connect Four¶
Cleaning up code is an everyday activity of any prorammer. Clean programs are shorter, easier to understand and easier to change. The process of cleaning up is also called refactoring. More specifically, refactoring means improving the structure of a program without changing its functionality.
In this chapter you will:¶
area |
topic |
|---|---|
🚀 |
clean up a connect four game |
🔧 |
read code |
🔧 |
apply refactoring: extract a function |
🔧 |
apply refactoring: extract a loop |
🐞 |
remove redundant code |
Exercise 1¶
In connect_four.py you find a working implementation
of a simple “Connect Four” game.
Writing such a program for the first time is a huge success!
Run the game and see how it works.
Exercise 2: Code review¶
The first implementation often requires clean-up.
Read the code in connect_four.py
Do you see any problems in the code?
Exercise 3: Extract a function¶
In the code, there is a paragraph commented with "print the game board. This code paragraph has a well-defined functionality. Move it into a separate function.
Define a function at the beginning of the file:
def print_board(board):
...
Move the code paragraph into that function. Add a function call where you took the code from:
while True:
print_board(board):
# first player moves
...
Run the game and make sure it still works.
Exercise 4: Redundant code¶
The code for the print_board() function
appears a second time further below.
Replace the code by the same function call.
Because you already defined the function, you can use it multiple times. Your program should become shorter by a few lines.
Run the game and make sure it still works.
Exercise 5: A function with a parameter¶
The next paragraph from "first player moves" to "end: player moves" can also be moved to a function. This time, we introduce an extra function parameter: the symbol for the player.
Create a new function with the definition:
def player_move(board, symbol):
...
Replace the "X" in the code by the new variable symbol.
Use the function call:
player_move(board, "X")
Run the game and make sure it still works.
Then, see if there is a similar code paragraph that you could replace by a function call.
Exercise 6: More functions¶
See if there are any other redundant blocks of code. Move them into functions as well.
Then, run the game and make sure it still works.
Exercise 7: Extract a loop¶
Consider the following section of the code:
if board[0][x - 1] == ".":
y = 0
if board[1][x - 1] == ".":
y = 1
if board[2][x - 1] == ".":
y = 2
if board[3][x - 1] == ".":
y = 3
if board[4][x - 1] == ".":
y = 4
Here, there is redundant code on a smaller scale.
Each pair of lines differs only by a single number.
This code can be made shorter by using a for loop
that counts from 0 to 4.
What needs to be inserted into the two gaps?
for i in range(5):
if board[___][x - 1] == ".":
y = ___
Run the game and make sure it still works.
The Code¶
Here is the complete (unclean) code:
import sys
# create empty game board
board = [
[".", ".", ".", ".", ".", ".", "."],
[".", ".", ".", ".", ".", ".", "."],
[".", ".", ".", ".", ".", ".", "."],
[".", ".", ".", ".", ".", ".", "."],
[".", ".", ".", ".", ".", ".", "."],
]
while True:
# print the game board
print()
print("1 2 3 4 5 6 7")
for row in board:
line = ""
for cell in row:
line += cell + " "
print(line)
print()
# first player moves
x = input("enter a column (1-7) ")
x = int(x)
# find empty cell closest to bottom
if board[0][x - 1] == ".":
y = 0
if board[1][x - 1] == ".":
y = 1
if board[2][x - 1] == ".":
y = 2
if board[3][x - 1] == ".":
y = 3
if board[4][x - 1] == ".":
y = 4
board[y][x - 1] = "X"
# end: player moves
# check rows
for row in board:
for i in range(4):
if (
row[i] == "X"
and row[i + 1] == "X"
and row[i + 2] == "X"
and row[i + 3] == "X"
):
print("Player 1 (X) wins!")
sys.exit(0)
# check columns
for i in range(7):
if board[0][i] == "X" and board[1][i] == "X" and board[2][i] == "X" and board[3][i] == "X":
print("Player 1 (X) wins!")
sys.exit(0)
elif board[1][i] == "X" and board[2][i] == "X" and board[3][i] == "X" and board[4][i] == "X":
print("Player 1 (X) wins!")
sys.exit(0)
# check diagonals
for y in range(2):
for x in range(4):
if (
board[y][x] == "X"
and board[y + 1][x + 1] == "X"
and board[y + 2][x + 2] == "X"
and board[y + 3][x + 3] == "X"
):
print("Player 1 (X) wins!")
sys.exit(0)
for y in range(2):
for x in range(3, 7):
if (
board[y][x] == "X"
and board[y + 1][x - 1] == "X"
and board[y + 2][x - 2] == "X"
and board[y + 3][x - 3] == "X"
):
print("Player 1 (X) wins!")
sys.exit(0)
# print the game board
print()
print("1 2 3 4 5 6 7")
for row in board:
line = ""
for cell in row:
line += cell + " "
print(line)
print()
# second player moves
x = input("enter a column (1-7) ")
x = int(x)
# find empty cell closest to bottom
if board[0][x - 1] == ".":
y = 0
if board[1][x - 1] == ".":
y = 1
if board[2][x - 1] == ".":
y = 2
if board[3][x - 1] == ".":
y = 3
if board[4][x - 1] == ".":
y = 4
board[y][x - 1] = "O"
# check rows
for row in board:
for i in range(4):
if (
row[i] == "O"
and row[i + 1] == "O"
and row[i + 2] == "O"
and row[i + 3] == "O"
):
print("Player 2 (O) wins!")
sys.exit(0)
# check columns
for i in range(7):
if board[0][i] == "O" and board[1][i] == "O" and board[2][i] == "O" and board[3][i] == "O":
print("Player 2 (O) wins!")
sys.exit(0)
elif board[1][i] == "O" and board[2][i] == "O" and board[3][i] == "O" and board[4][i] == "O":
print("Player 2 (O) wins!")
sys.exit(0)
# check diagonals
for y in range(2):
for x in range(4):
if (
board[y][x] == "O"
and board[y + 1][x + 1] == "O"
and board[y + 2][x + 2] == "O"
and board[y + 3][x + 3] == "O"
):
print("Player 2 (O) wins!")
sys.exit(0)
for y in range(2):
for x in range(3, 7):
if (
board[y][x] == "O"
and board[y + 1][x - 1] == "O"
and board[y + 2][x - 2] == "O"
and board[y + 3][x - 3] == "O"
):
print("Player 2 (O) wins!")
sys.exit(0)