Connect4 variant in Python
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
12
down vote
favorite
I built a variant of connect4 in Python. The game runs just in the command line.
It is on a 7ÃÂ7 board and the players take turns putting tokens in a column (the token falls down) OR turn the board by 90ð to the left or right and the tokens fall down again, creating a different board setting, but putting in no token this turn.
I would like to have some feedback on the implementation of the game logic and my Python coding style:
from string import ascii_uppercase
class TFBoard():
def __init__(self):
self.width = 7
self.height = 7
self.columns = range(1, self.height + 1)
self.rows = ascii_uppercase[:self.width]
self.matrix = [[0 for h in range(0, self.height)]
for w in range(0, self.width)]
self.endGame = False
self.winner = 0
self.playerTurn = 1
self.turnNumber = 1
def flatMatrix(self):
return [value for array in self.matrix for value in array]
def drawBoard(self):
col = ["33[0m", "33[91m", "33[31m", "33[97m", "33[92m"]
def drawInLoops(i, j):
if i == self.height:
if j == 0:
# bottom left corner
print(" ", end=f"")
else:
# bottom letter row
print(f"col[1]self.columns[j-1]col[0]", end=f" ")
else:
if j == 0:
# left number column
print(f"col[1]self.rows[-i-1]col[0]", end=" ")
else:
# squares
# drawn matrix is 1 higher and wider than self.matrix
print(f"col[2][col[0]", end="")
piece = self.matrix[j - 1][self.height - i - 1]
if piece == 1:
piece = f"col[3]Xcol[0]"
elif piece == 2:
piece = f"col[4]Ocol[0]"
elif piece == 0:
piece = " "
print(f"piece", end="")
print(f"col[2]]col[0]", end="")
for i in range(0, self.height + 1):
for j in range(0, self.width + 1):
drawInLoops(i, j)
print("")
print("") # new line after board for better looks
def putToken(self, player, column):
# find first nonzero entrie in column from top
notification = f"New Player player token in column column+1"
for i in range(self.height - 1, -1, -1):
if i == 0 and board.matrix[column][i] == 0:
self.matrix[column][0] = player
print(f"notification, row self.rows[i]")
return
if board.matrix[column][i] != 0:
if i == self.height - 1:
print(f"COLUMN FULL!nCan't place token in column i.")
else:
self.matrix[column][i + 1] = player
print(f"notification, row self.rows[i+1]")
return
def checkWin(self):
# check for win by column
def checkColumns():
# go through columns
for c in range(7):
column = self.matrix[c]
for r in range(4):
# start points r
start = column[r]
if start != 0 and all(token == start for token in column[r:r + 4]):
print(f"Win by column for player start")
print(f"self.rows[r]c+1-self.rows[r+3]c+1")
self.endGame = True
# check for win by row
def checkRows():
# go through rows
for r in range(self.height - 1, -1, -1):
# write rows as lists
row = [self.matrix[c][r] for c in range(7)]
for c in range(4):
start = row[c]
if start != 0 and all(token == start for token in row[c:c + 4]):
print(f"Win by row for player start")
print(f"self.rows[r]c+1-self.rows[r]c+4")
self.endGame = True
def checkbltrDiagonals():
for c in range(4):
for r in range(4):
diagonal = [self.matrix[c + x][r + x] for x in range(4)]
start = diagonal[0]
if start != 0 and all(token == start for token in diagonal):
print(f"Win by diagonal bltr for player start")
print(f"self.rows[r]c+1-self.rows[r+3]c+4")
self.endGame = True
def checktlbrDiagonals():
for c in range(4):
for r in range(3, 7):
diagonal = [self.matrix[c + x][r - x] for x in range(4)]
start = diagonal[0]
if start != 0 and all(token == start for token in diagonal):
print(f"Win by diagonal tlbr for player start")
print(f"self.rows[r]c+1-self.rows[r-3]c+4")
self.endGame = True
checkColumns()
checkRows()
checktlbrDiagonals()
checkbltrDiagonals()
def checkDraw(self):
if all(x != 0 for x in self.flatMatrix()):
return True
def applyGravity(self):
for i in range(7):
self.matrix[i] = [x for x in self.matrix[i] if x != 0]
self.matrix[i] += [0 for _ in range(7 - len(self.matrix[i]))]
def rotateLeft(self):
self.matrix = list(list(x) for x in zip(*self.matrix))[::-1]
def rotateRight(self):
self.matrix = list(list(x)[::-1] for x in zip(*self.matrix))
def gameLoop(self):
print("---NEW GAME")
self.drawBoard()
while self.endGame is False:
print(f"---Turn self.turnNumber:")
turn = input(f"Player self.playerTurn, make your move.n(1,2,3,4,5,6,7,L,R)n---")
if turn not in ["1", "2", "3", "4", "5", "6", "7", "l", "L", "r", "R"]:
print(f"WRONG INPUT turn!nInput must be L, R or an integer between 1 and 7.")
continue
if turn == "L" or turn == "l":
self.rotateLeft()
self.applyGravity()
elif turn == "R" or turn == "r":
self.rotateRight()
self.applyGravity()
elif int(turn) in [1, 2, 3, 4, 5, 6, 7]:
self.putToken(self.playerTurn, int(turn) - 1)
self.checkWin()
if self.endGame is True:
self.winner = self.playerTurn
break
if self.checkDraw():
break
self.playerTurn = 1 if self.playerTurn == 2 else 2
self.turnNumber += 1
self.drawBoard()
if self.winner == 0:
print(f"DRAW!")
else:
print(f"Player self.winner won in self.turnNumber turns!")
self.drawBoard()
return self.winner
board = TFBoard()
board.gameLoop()
python python-3.x game console connect-four
add a comment |Â
up vote
12
down vote
favorite
I built a variant of connect4 in Python. The game runs just in the command line.
It is on a 7ÃÂ7 board and the players take turns putting tokens in a column (the token falls down) OR turn the board by 90ð to the left or right and the tokens fall down again, creating a different board setting, but putting in no token this turn.
I would like to have some feedback on the implementation of the game logic and my Python coding style:
from string import ascii_uppercase
class TFBoard():
def __init__(self):
self.width = 7
self.height = 7
self.columns = range(1, self.height + 1)
self.rows = ascii_uppercase[:self.width]
self.matrix = [[0 for h in range(0, self.height)]
for w in range(0, self.width)]
self.endGame = False
self.winner = 0
self.playerTurn = 1
self.turnNumber = 1
def flatMatrix(self):
return [value for array in self.matrix for value in array]
def drawBoard(self):
col = ["33[0m", "33[91m", "33[31m", "33[97m", "33[92m"]
def drawInLoops(i, j):
if i == self.height:
if j == 0:
# bottom left corner
print(" ", end=f"")
else:
# bottom letter row
print(f"col[1]self.columns[j-1]col[0]", end=f" ")
else:
if j == 0:
# left number column
print(f"col[1]self.rows[-i-1]col[0]", end=" ")
else:
# squares
# drawn matrix is 1 higher and wider than self.matrix
print(f"col[2][col[0]", end="")
piece = self.matrix[j - 1][self.height - i - 1]
if piece == 1:
piece = f"col[3]Xcol[0]"
elif piece == 2:
piece = f"col[4]Ocol[0]"
elif piece == 0:
piece = " "
print(f"piece", end="")
print(f"col[2]]col[0]", end="")
for i in range(0, self.height + 1):
for j in range(0, self.width + 1):
drawInLoops(i, j)
print("")
print("") # new line after board for better looks
def putToken(self, player, column):
# find first nonzero entrie in column from top
notification = f"New Player player token in column column+1"
for i in range(self.height - 1, -1, -1):
if i == 0 and board.matrix[column][i] == 0:
self.matrix[column][0] = player
print(f"notification, row self.rows[i]")
return
if board.matrix[column][i] != 0:
if i == self.height - 1:
print(f"COLUMN FULL!nCan't place token in column i.")
else:
self.matrix[column][i + 1] = player
print(f"notification, row self.rows[i+1]")
return
def checkWin(self):
# check for win by column
def checkColumns():
# go through columns
for c in range(7):
column = self.matrix[c]
for r in range(4):
# start points r
start = column[r]
if start != 0 and all(token == start for token in column[r:r + 4]):
print(f"Win by column for player start")
print(f"self.rows[r]c+1-self.rows[r+3]c+1")
self.endGame = True
# check for win by row
def checkRows():
# go through rows
for r in range(self.height - 1, -1, -1):
# write rows as lists
row = [self.matrix[c][r] for c in range(7)]
for c in range(4):
start = row[c]
if start != 0 and all(token == start for token in row[c:c + 4]):
print(f"Win by row for player start")
print(f"self.rows[r]c+1-self.rows[r]c+4")
self.endGame = True
def checkbltrDiagonals():
for c in range(4):
for r in range(4):
diagonal = [self.matrix[c + x][r + x] for x in range(4)]
start = diagonal[0]
if start != 0 and all(token == start for token in diagonal):
print(f"Win by diagonal bltr for player start")
print(f"self.rows[r]c+1-self.rows[r+3]c+4")
self.endGame = True
def checktlbrDiagonals():
for c in range(4):
for r in range(3, 7):
diagonal = [self.matrix[c + x][r - x] for x in range(4)]
start = diagonal[0]
if start != 0 and all(token == start for token in diagonal):
print(f"Win by diagonal tlbr for player start")
print(f"self.rows[r]c+1-self.rows[r-3]c+4")
self.endGame = True
checkColumns()
checkRows()
checktlbrDiagonals()
checkbltrDiagonals()
def checkDraw(self):
if all(x != 0 for x in self.flatMatrix()):
return True
def applyGravity(self):
for i in range(7):
self.matrix[i] = [x for x in self.matrix[i] if x != 0]
self.matrix[i] += [0 for _ in range(7 - len(self.matrix[i]))]
def rotateLeft(self):
self.matrix = list(list(x) for x in zip(*self.matrix))[::-1]
def rotateRight(self):
self.matrix = list(list(x)[::-1] for x in zip(*self.matrix))
def gameLoop(self):
print("---NEW GAME")
self.drawBoard()
while self.endGame is False:
print(f"---Turn self.turnNumber:")
turn = input(f"Player self.playerTurn, make your move.n(1,2,3,4,5,6,7,L,R)n---")
if turn not in ["1", "2", "3", "4", "5", "6", "7", "l", "L", "r", "R"]:
print(f"WRONG INPUT turn!nInput must be L, R or an integer between 1 and 7.")
continue
if turn == "L" or turn == "l":
self.rotateLeft()
self.applyGravity()
elif turn == "R" or turn == "r":
self.rotateRight()
self.applyGravity()
elif int(turn) in [1, 2, 3, 4, 5, 6, 7]:
self.putToken(self.playerTurn, int(turn) - 1)
self.checkWin()
if self.endGame is True:
self.winner = self.playerTurn
break
if self.checkDraw():
break
self.playerTurn = 1 if self.playerTurn == 2 else 2
self.turnNumber += 1
self.drawBoard()
if self.winner == 0:
print(f"DRAW!")
else:
print(f"Player self.winner won in self.turnNumber turns!")
self.drawBoard()
return self.winner
board = TFBoard()
board.gameLoop()
python python-3.x game console connect-four
add a comment |Â
up vote
12
down vote
favorite
up vote
12
down vote
favorite
I built a variant of connect4 in Python. The game runs just in the command line.
It is on a 7ÃÂ7 board and the players take turns putting tokens in a column (the token falls down) OR turn the board by 90ð to the left or right and the tokens fall down again, creating a different board setting, but putting in no token this turn.
I would like to have some feedback on the implementation of the game logic and my Python coding style:
from string import ascii_uppercase
class TFBoard():
def __init__(self):
self.width = 7
self.height = 7
self.columns = range(1, self.height + 1)
self.rows = ascii_uppercase[:self.width]
self.matrix = [[0 for h in range(0, self.height)]
for w in range(0, self.width)]
self.endGame = False
self.winner = 0
self.playerTurn = 1
self.turnNumber = 1
def flatMatrix(self):
return [value for array in self.matrix for value in array]
def drawBoard(self):
col = ["33[0m", "33[91m", "33[31m", "33[97m", "33[92m"]
def drawInLoops(i, j):
if i == self.height:
if j == 0:
# bottom left corner
print(" ", end=f"")
else:
# bottom letter row
print(f"col[1]self.columns[j-1]col[0]", end=f" ")
else:
if j == 0:
# left number column
print(f"col[1]self.rows[-i-1]col[0]", end=" ")
else:
# squares
# drawn matrix is 1 higher and wider than self.matrix
print(f"col[2][col[0]", end="")
piece = self.matrix[j - 1][self.height - i - 1]
if piece == 1:
piece = f"col[3]Xcol[0]"
elif piece == 2:
piece = f"col[4]Ocol[0]"
elif piece == 0:
piece = " "
print(f"piece", end="")
print(f"col[2]]col[0]", end="")
for i in range(0, self.height + 1):
for j in range(0, self.width + 1):
drawInLoops(i, j)
print("")
print("") # new line after board for better looks
def putToken(self, player, column):
# find first nonzero entrie in column from top
notification = f"New Player player token in column column+1"
for i in range(self.height - 1, -1, -1):
if i == 0 and board.matrix[column][i] == 0:
self.matrix[column][0] = player
print(f"notification, row self.rows[i]")
return
if board.matrix[column][i] != 0:
if i == self.height - 1:
print(f"COLUMN FULL!nCan't place token in column i.")
else:
self.matrix[column][i + 1] = player
print(f"notification, row self.rows[i+1]")
return
def checkWin(self):
# check for win by column
def checkColumns():
# go through columns
for c in range(7):
column = self.matrix[c]
for r in range(4):
# start points r
start = column[r]
if start != 0 and all(token == start for token in column[r:r + 4]):
print(f"Win by column for player start")
print(f"self.rows[r]c+1-self.rows[r+3]c+1")
self.endGame = True
# check for win by row
def checkRows():
# go through rows
for r in range(self.height - 1, -1, -1):
# write rows as lists
row = [self.matrix[c][r] for c in range(7)]
for c in range(4):
start = row[c]
if start != 0 and all(token == start for token in row[c:c + 4]):
print(f"Win by row for player start")
print(f"self.rows[r]c+1-self.rows[r]c+4")
self.endGame = True
def checkbltrDiagonals():
for c in range(4):
for r in range(4):
diagonal = [self.matrix[c + x][r + x] for x in range(4)]
start = diagonal[0]
if start != 0 and all(token == start for token in diagonal):
print(f"Win by diagonal bltr for player start")
print(f"self.rows[r]c+1-self.rows[r+3]c+4")
self.endGame = True
def checktlbrDiagonals():
for c in range(4):
for r in range(3, 7):
diagonal = [self.matrix[c + x][r - x] for x in range(4)]
start = diagonal[0]
if start != 0 and all(token == start for token in diagonal):
print(f"Win by diagonal tlbr for player start")
print(f"self.rows[r]c+1-self.rows[r-3]c+4")
self.endGame = True
checkColumns()
checkRows()
checktlbrDiagonals()
checkbltrDiagonals()
def checkDraw(self):
if all(x != 0 for x in self.flatMatrix()):
return True
def applyGravity(self):
for i in range(7):
self.matrix[i] = [x for x in self.matrix[i] if x != 0]
self.matrix[i] += [0 for _ in range(7 - len(self.matrix[i]))]
def rotateLeft(self):
self.matrix = list(list(x) for x in zip(*self.matrix))[::-1]
def rotateRight(self):
self.matrix = list(list(x)[::-1] for x in zip(*self.matrix))
def gameLoop(self):
print("---NEW GAME")
self.drawBoard()
while self.endGame is False:
print(f"---Turn self.turnNumber:")
turn = input(f"Player self.playerTurn, make your move.n(1,2,3,4,5,6,7,L,R)n---")
if turn not in ["1", "2", "3", "4", "5", "6", "7", "l", "L", "r", "R"]:
print(f"WRONG INPUT turn!nInput must be L, R or an integer between 1 and 7.")
continue
if turn == "L" or turn == "l":
self.rotateLeft()
self.applyGravity()
elif turn == "R" or turn == "r":
self.rotateRight()
self.applyGravity()
elif int(turn) in [1, 2, 3, 4, 5, 6, 7]:
self.putToken(self.playerTurn, int(turn) - 1)
self.checkWin()
if self.endGame is True:
self.winner = self.playerTurn
break
if self.checkDraw():
break
self.playerTurn = 1 if self.playerTurn == 2 else 2
self.turnNumber += 1
self.drawBoard()
if self.winner == 0:
print(f"DRAW!")
else:
print(f"Player self.winner won in self.turnNumber turns!")
self.drawBoard()
return self.winner
board = TFBoard()
board.gameLoop()
python python-3.x game console connect-four
I built a variant of connect4 in Python. The game runs just in the command line.
It is on a 7ÃÂ7 board and the players take turns putting tokens in a column (the token falls down) OR turn the board by 90ð to the left or right and the tokens fall down again, creating a different board setting, but putting in no token this turn.
I would like to have some feedback on the implementation of the game logic and my Python coding style:
from string import ascii_uppercase
class TFBoard():
def __init__(self):
self.width = 7
self.height = 7
self.columns = range(1, self.height + 1)
self.rows = ascii_uppercase[:self.width]
self.matrix = [[0 for h in range(0, self.height)]
for w in range(0, self.width)]
self.endGame = False
self.winner = 0
self.playerTurn = 1
self.turnNumber = 1
def flatMatrix(self):
return [value for array in self.matrix for value in array]
def drawBoard(self):
col = ["33[0m", "33[91m", "33[31m", "33[97m", "33[92m"]
def drawInLoops(i, j):
if i == self.height:
if j == 0:
# bottom left corner
print(" ", end=f"")
else:
# bottom letter row
print(f"col[1]self.columns[j-1]col[0]", end=f" ")
else:
if j == 0:
# left number column
print(f"col[1]self.rows[-i-1]col[0]", end=" ")
else:
# squares
# drawn matrix is 1 higher and wider than self.matrix
print(f"col[2][col[0]", end="")
piece = self.matrix[j - 1][self.height - i - 1]
if piece == 1:
piece = f"col[3]Xcol[0]"
elif piece == 2:
piece = f"col[4]Ocol[0]"
elif piece == 0:
piece = " "
print(f"piece", end="")
print(f"col[2]]col[0]", end="")
for i in range(0, self.height + 1):
for j in range(0, self.width + 1):
drawInLoops(i, j)
print("")
print("") # new line after board for better looks
def putToken(self, player, column):
# find first nonzero entrie in column from top
notification = f"New Player player token in column column+1"
for i in range(self.height - 1, -1, -1):
if i == 0 and board.matrix[column][i] == 0:
self.matrix[column][0] = player
print(f"notification, row self.rows[i]")
return
if board.matrix[column][i] != 0:
if i == self.height - 1:
print(f"COLUMN FULL!nCan't place token in column i.")
else:
self.matrix[column][i + 1] = player
print(f"notification, row self.rows[i+1]")
return
def checkWin(self):
# check for win by column
def checkColumns():
# go through columns
for c in range(7):
column = self.matrix[c]
for r in range(4):
# start points r
start = column[r]
if start != 0 and all(token == start for token in column[r:r + 4]):
print(f"Win by column for player start")
print(f"self.rows[r]c+1-self.rows[r+3]c+1")
self.endGame = True
# check for win by row
def checkRows():
# go through rows
for r in range(self.height - 1, -1, -1):
# write rows as lists
row = [self.matrix[c][r] for c in range(7)]
for c in range(4):
start = row[c]
if start != 0 and all(token == start for token in row[c:c + 4]):
print(f"Win by row for player start")
print(f"self.rows[r]c+1-self.rows[r]c+4")
self.endGame = True
def checkbltrDiagonals():
for c in range(4):
for r in range(4):
diagonal = [self.matrix[c + x][r + x] for x in range(4)]
start = diagonal[0]
if start != 0 and all(token == start for token in diagonal):
print(f"Win by diagonal bltr for player start")
print(f"self.rows[r]c+1-self.rows[r+3]c+4")
self.endGame = True
def checktlbrDiagonals():
for c in range(4):
for r in range(3, 7):
diagonal = [self.matrix[c + x][r - x] for x in range(4)]
start = diagonal[0]
if start != 0 and all(token == start for token in diagonal):
print(f"Win by diagonal tlbr for player start")
print(f"self.rows[r]c+1-self.rows[r-3]c+4")
self.endGame = True
checkColumns()
checkRows()
checktlbrDiagonals()
checkbltrDiagonals()
def checkDraw(self):
if all(x != 0 for x in self.flatMatrix()):
return True
def applyGravity(self):
for i in range(7):
self.matrix[i] = [x for x in self.matrix[i] if x != 0]
self.matrix[i] += [0 for _ in range(7 - len(self.matrix[i]))]
def rotateLeft(self):
self.matrix = list(list(x) for x in zip(*self.matrix))[::-1]
def rotateRight(self):
self.matrix = list(list(x)[::-1] for x in zip(*self.matrix))
def gameLoop(self):
print("---NEW GAME")
self.drawBoard()
while self.endGame is False:
print(f"---Turn self.turnNumber:")
turn = input(f"Player self.playerTurn, make your move.n(1,2,3,4,5,6,7,L,R)n---")
if turn not in ["1", "2", "3", "4", "5", "6", "7", "l", "L", "r", "R"]:
print(f"WRONG INPUT turn!nInput must be L, R or an integer between 1 and 7.")
continue
if turn == "L" or turn == "l":
self.rotateLeft()
self.applyGravity()
elif turn == "R" or turn == "r":
self.rotateRight()
self.applyGravity()
elif int(turn) in [1, 2, 3, 4, 5, 6, 7]:
self.putToken(self.playerTurn, int(turn) - 1)
self.checkWin()
if self.endGame is True:
self.winner = self.playerTurn
break
if self.checkDraw():
break
self.playerTurn = 1 if self.playerTurn == 2 else 2
self.turnNumber += 1
self.drawBoard()
if self.winner == 0:
print(f"DRAW!")
else:
print(f"Player self.winner won in self.turnNumber turns!")
self.drawBoard()
return self.winner
board = TFBoard()
board.gameLoop()
python python-3.x game console connect-four
edited Feb 11 at 6:24
asked Feb 6 at 17:24
Tweakimp
2621212
2621212
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
2
down vote
accepted
As there is no answer yet - some observations in no specific order
you miss the top level main guard which allows the module to be imported
if __name__ == '__main__':
board = TFBoard()
board.gameLoop()
your class is not really a class
It is not intended to be copied, compared, it does not communicate with other program parts. It even has a blocking loop. It is just a namespace like container. however modules do already fulfill the namespace. you could safely remove all class
and self
stuff and run the application like
if __name__ == '__main__':
init()
gameLoop()
If you want to design a class you have to remove the gameLoop
from it. then you notice there is a little problem with the rotate
which forces you to expose internal implementation details to the main loop by requiring a call to applyGravity
as well. by the way putToken
does the job correctly, one input resulting in one call to the board.
ensure functions/methods do what the name promises
rotate
does not rotate the board, but a matrix only. it would make a perfect name for a method of a matrix class. a board method should do the complete job and apply the gravity as well.
reuse what you have
you could insert tokens on the top only and use applyGravity
to let it fall down. not only this resembles physics, but also there is less code to test. putToken
shows high complexity and repeated code.
handle errors
your putToken
does detect an error if a column is full. however it does not forward this to the loop, so the player is turned over.
separate I/O from core
do not do I/O from core functions, but from main loop only. this requires providing state and error information to the loop by return values or raising exceptions. or by providing getters for state. this will immediately lead to better design and testable functions.
there shall be no magic numbers in the code, define constants
when you decide to go for a 9x9 instead of a 7x7 board, there shall be only two edits. you make it worse by having those numbers as attributes, but you do use them only for drawing. when checking for wins, applying gravity or handling input you have raw numbers in the code. this is the worst possible combination, pretend to handle your constants properly in the __init__
but ignoring these later on. seriously, this is an absolute fail. also have a definition for 4
and write correct expressions for all the dependent values subtracting it from height
or width
.
some python sugar
self.matrix[i] += [0 for _ in range(7 - len(self.matrix[i]))]
may be written shorter (and more readable)
self.matrix[i] += [0] * (7 - len(self.matrix[i]))
learn to write unit tests
python provides built-in unit test support. it is really easy to use and of great use. what you invest in unit testing you get returned in better design, less errors, better maintainability and probably you even save time you use for debugging otherwise. again seriously, you will be a much better programmer when you have done your first little project with unit testing.
finally
my answer is intended to help you, not to intimidate you. try to understand and get better. if you have questions regarding my points, please feel free to ask.
Thank you for your answer. I made it a class to call it multiple times by another module, for example when I let random players play against each other multiple times. The matrix is the list of lists that stores the game data, the board is the thing that is drawn into the console. The code reuse is totally true, this is something I just dont see in my own code. In case of a full coloumn, the player is not turned over because the player number does not change. There is a continue statement for that case. i would like to go throught a different file if you are up for it. We could use the SO chat
â Tweakimp
Feb 16 at 19:49
I'm off in a minute. Feel free to start a chat, I definitely will answer, but unfortunately not now.
â stefan
Feb 16 at 20:04
I dont know id this works... chat.stackexchange.com/rooms/73272/tweakimp
â Tweakimp
Feb 16 at 20:33
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
As there is no answer yet - some observations in no specific order
you miss the top level main guard which allows the module to be imported
if __name__ == '__main__':
board = TFBoard()
board.gameLoop()
your class is not really a class
It is not intended to be copied, compared, it does not communicate with other program parts. It even has a blocking loop. It is just a namespace like container. however modules do already fulfill the namespace. you could safely remove all class
and self
stuff and run the application like
if __name__ == '__main__':
init()
gameLoop()
If you want to design a class you have to remove the gameLoop
from it. then you notice there is a little problem with the rotate
which forces you to expose internal implementation details to the main loop by requiring a call to applyGravity
as well. by the way putToken
does the job correctly, one input resulting in one call to the board.
ensure functions/methods do what the name promises
rotate
does not rotate the board, but a matrix only. it would make a perfect name for a method of a matrix class. a board method should do the complete job and apply the gravity as well.
reuse what you have
you could insert tokens on the top only and use applyGravity
to let it fall down. not only this resembles physics, but also there is less code to test. putToken
shows high complexity and repeated code.
handle errors
your putToken
does detect an error if a column is full. however it does not forward this to the loop, so the player is turned over.
separate I/O from core
do not do I/O from core functions, but from main loop only. this requires providing state and error information to the loop by return values or raising exceptions. or by providing getters for state. this will immediately lead to better design and testable functions.
there shall be no magic numbers in the code, define constants
when you decide to go for a 9x9 instead of a 7x7 board, there shall be only two edits. you make it worse by having those numbers as attributes, but you do use them only for drawing. when checking for wins, applying gravity or handling input you have raw numbers in the code. this is the worst possible combination, pretend to handle your constants properly in the __init__
but ignoring these later on. seriously, this is an absolute fail. also have a definition for 4
and write correct expressions for all the dependent values subtracting it from height
or width
.
some python sugar
self.matrix[i] += [0 for _ in range(7 - len(self.matrix[i]))]
may be written shorter (and more readable)
self.matrix[i] += [0] * (7 - len(self.matrix[i]))
learn to write unit tests
python provides built-in unit test support. it is really easy to use and of great use. what you invest in unit testing you get returned in better design, less errors, better maintainability and probably you even save time you use for debugging otherwise. again seriously, you will be a much better programmer when you have done your first little project with unit testing.
finally
my answer is intended to help you, not to intimidate you. try to understand and get better. if you have questions regarding my points, please feel free to ask.
Thank you for your answer. I made it a class to call it multiple times by another module, for example when I let random players play against each other multiple times. The matrix is the list of lists that stores the game data, the board is the thing that is drawn into the console. The code reuse is totally true, this is something I just dont see in my own code. In case of a full coloumn, the player is not turned over because the player number does not change. There is a continue statement for that case. i would like to go throught a different file if you are up for it. We could use the SO chat
â Tweakimp
Feb 16 at 19:49
I'm off in a minute. Feel free to start a chat, I definitely will answer, but unfortunately not now.
â stefan
Feb 16 at 20:04
I dont know id this works... chat.stackexchange.com/rooms/73272/tweakimp
â Tweakimp
Feb 16 at 20:33
add a comment |Â
up vote
2
down vote
accepted
As there is no answer yet - some observations in no specific order
you miss the top level main guard which allows the module to be imported
if __name__ == '__main__':
board = TFBoard()
board.gameLoop()
your class is not really a class
It is not intended to be copied, compared, it does not communicate with other program parts. It even has a blocking loop. It is just a namespace like container. however modules do already fulfill the namespace. you could safely remove all class
and self
stuff and run the application like
if __name__ == '__main__':
init()
gameLoop()
If you want to design a class you have to remove the gameLoop
from it. then you notice there is a little problem with the rotate
which forces you to expose internal implementation details to the main loop by requiring a call to applyGravity
as well. by the way putToken
does the job correctly, one input resulting in one call to the board.
ensure functions/methods do what the name promises
rotate
does not rotate the board, but a matrix only. it would make a perfect name for a method of a matrix class. a board method should do the complete job and apply the gravity as well.
reuse what you have
you could insert tokens on the top only and use applyGravity
to let it fall down. not only this resembles physics, but also there is less code to test. putToken
shows high complexity and repeated code.
handle errors
your putToken
does detect an error if a column is full. however it does not forward this to the loop, so the player is turned over.
separate I/O from core
do not do I/O from core functions, but from main loop only. this requires providing state and error information to the loop by return values or raising exceptions. or by providing getters for state. this will immediately lead to better design and testable functions.
there shall be no magic numbers in the code, define constants
when you decide to go for a 9x9 instead of a 7x7 board, there shall be only two edits. you make it worse by having those numbers as attributes, but you do use them only for drawing. when checking for wins, applying gravity or handling input you have raw numbers in the code. this is the worst possible combination, pretend to handle your constants properly in the __init__
but ignoring these later on. seriously, this is an absolute fail. also have a definition for 4
and write correct expressions for all the dependent values subtracting it from height
or width
.
some python sugar
self.matrix[i] += [0 for _ in range(7 - len(self.matrix[i]))]
may be written shorter (and more readable)
self.matrix[i] += [0] * (7 - len(self.matrix[i]))
learn to write unit tests
python provides built-in unit test support. it is really easy to use and of great use. what you invest in unit testing you get returned in better design, less errors, better maintainability and probably you even save time you use for debugging otherwise. again seriously, you will be a much better programmer when you have done your first little project with unit testing.
finally
my answer is intended to help you, not to intimidate you. try to understand and get better. if you have questions regarding my points, please feel free to ask.
Thank you for your answer. I made it a class to call it multiple times by another module, for example when I let random players play against each other multiple times. The matrix is the list of lists that stores the game data, the board is the thing that is drawn into the console. The code reuse is totally true, this is something I just dont see in my own code. In case of a full coloumn, the player is not turned over because the player number does not change. There is a continue statement for that case. i would like to go throught a different file if you are up for it. We could use the SO chat
â Tweakimp
Feb 16 at 19:49
I'm off in a minute. Feel free to start a chat, I definitely will answer, but unfortunately not now.
â stefan
Feb 16 at 20:04
I dont know id this works... chat.stackexchange.com/rooms/73272/tweakimp
â Tweakimp
Feb 16 at 20:33
add a comment |Â
up vote
2
down vote
accepted
up vote
2
down vote
accepted
As there is no answer yet - some observations in no specific order
you miss the top level main guard which allows the module to be imported
if __name__ == '__main__':
board = TFBoard()
board.gameLoop()
your class is not really a class
It is not intended to be copied, compared, it does not communicate with other program parts. It even has a blocking loop. It is just a namespace like container. however modules do already fulfill the namespace. you could safely remove all class
and self
stuff and run the application like
if __name__ == '__main__':
init()
gameLoop()
If you want to design a class you have to remove the gameLoop
from it. then you notice there is a little problem with the rotate
which forces you to expose internal implementation details to the main loop by requiring a call to applyGravity
as well. by the way putToken
does the job correctly, one input resulting in one call to the board.
ensure functions/methods do what the name promises
rotate
does not rotate the board, but a matrix only. it would make a perfect name for a method of a matrix class. a board method should do the complete job and apply the gravity as well.
reuse what you have
you could insert tokens on the top only and use applyGravity
to let it fall down. not only this resembles physics, but also there is less code to test. putToken
shows high complexity and repeated code.
handle errors
your putToken
does detect an error if a column is full. however it does not forward this to the loop, so the player is turned over.
separate I/O from core
do not do I/O from core functions, but from main loop only. this requires providing state and error information to the loop by return values or raising exceptions. or by providing getters for state. this will immediately lead to better design and testable functions.
there shall be no magic numbers in the code, define constants
when you decide to go for a 9x9 instead of a 7x7 board, there shall be only two edits. you make it worse by having those numbers as attributes, but you do use them only for drawing. when checking for wins, applying gravity or handling input you have raw numbers in the code. this is the worst possible combination, pretend to handle your constants properly in the __init__
but ignoring these later on. seriously, this is an absolute fail. also have a definition for 4
and write correct expressions for all the dependent values subtracting it from height
or width
.
some python sugar
self.matrix[i] += [0 for _ in range(7 - len(self.matrix[i]))]
may be written shorter (and more readable)
self.matrix[i] += [0] * (7 - len(self.matrix[i]))
learn to write unit tests
python provides built-in unit test support. it is really easy to use and of great use. what you invest in unit testing you get returned in better design, less errors, better maintainability and probably you even save time you use for debugging otherwise. again seriously, you will be a much better programmer when you have done your first little project with unit testing.
finally
my answer is intended to help you, not to intimidate you. try to understand and get better. if you have questions regarding my points, please feel free to ask.
As there is no answer yet - some observations in no specific order
you miss the top level main guard which allows the module to be imported
if __name__ == '__main__':
board = TFBoard()
board.gameLoop()
your class is not really a class
It is not intended to be copied, compared, it does not communicate with other program parts. It even has a blocking loop. It is just a namespace like container. however modules do already fulfill the namespace. you could safely remove all class
and self
stuff and run the application like
if __name__ == '__main__':
init()
gameLoop()
If you want to design a class you have to remove the gameLoop
from it. then you notice there is a little problem with the rotate
which forces you to expose internal implementation details to the main loop by requiring a call to applyGravity
as well. by the way putToken
does the job correctly, one input resulting in one call to the board.
ensure functions/methods do what the name promises
rotate
does not rotate the board, but a matrix only. it would make a perfect name for a method of a matrix class. a board method should do the complete job and apply the gravity as well.
reuse what you have
you could insert tokens on the top only and use applyGravity
to let it fall down. not only this resembles physics, but also there is less code to test. putToken
shows high complexity and repeated code.
handle errors
your putToken
does detect an error if a column is full. however it does not forward this to the loop, so the player is turned over.
separate I/O from core
do not do I/O from core functions, but from main loop only. this requires providing state and error information to the loop by return values or raising exceptions. or by providing getters for state. this will immediately lead to better design and testable functions.
there shall be no magic numbers in the code, define constants
when you decide to go for a 9x9 instead of a 7x7 board, there shall be only two edits. you make it worse by having those numbers as attributes, but you do use them only for drawing. when checking for wins, applying gravity or handling input you have raw numbers in the code. this is the worst possible combination, pretend to handle your constants properly in the __init__
but ignoring these later on. seriously, this is an absolute fail. also have a definition for 4
and write correct expressions for all the dependent values subtracting it from height
or width
.
some python sugar
self.matrix[i] += [0 for _ in range(7 - len(self.matrix[i]))]
may be written shorter (and more readable)
self.matrix[i] += [0] * (7 - len(self.matrix[i]))
learn to write unit tests
python provides built-in unit test support. it is really easy to use and of great use. what you invest in unit testing you get returned in better design, less errors, better maintainability and probably you even save time you use for debugging otherwise. again seriously, you will be a much better programmer when you have done your first little project with unit testing.
finally
my answer is intended to help you, not to intimidate you. try to understand and get better. if you have questions regarding my points, please feel free to ask.
answered Feb 16 at 19:13
stefan
1,201110
1,201110
Thank you for your answer. I made it a class to call it multiple times by another module, for example when I let random players play against each other multiple times. The matrix is the list of lists that stores the game data, the board is the thing that is drawn into the console. The code reuse is totally true, this is something I just dont see in my own code. In case of a full coloumn, the player is not turned over because the player number does not change. There is a continue statement for that case. i would like to go throught a different file if you are up for it. We could use the SO chat
â Tweakimp
Feb 16 at 19:49
I'm off in a minute. Feel free to start a chat, I definitely will answer, but unfortunately not now.
â stefan
Feb 16 at 20:04
I dont know id this works... chat.stackexchange.com/rooms/73272/tweakimp
â Tweakimp
Feb 16 at 20:33
add a comment |Â
Thank you for your answer. I made it a class to call it multiple times by another module, for example when I let random players play against each other multiple times. The matrix is the list of lists that stores the game data, the board is the thing that is drawn into the console. The code reuse is totally true, this is something I just dont see in my own code. In case of a full coloumn, the player is not turned over because the player number does not change. There is a continue statement for that case. i would like to go throught a different file if you are up for it. We could use the SO chat
â Tweakimp
Feb 16 at 19:49
I'm off in a minute. Feel free to start a chat, I definitely will answer, but unfortunately not now.
â stefan
Feb 16 at 20:04
I dont know id this works... chat.stackexchange.com/rooms/73272/tweakimp
â Tweakimp
Feb 16 at 20:33
Thank you for your answer. I made it a class to call it multiple times by another module, for example when I let random players play against each other multiple times. The matrix is the list of lists that stores the game data, the board is the thing that is drawn into the console. The code reuse is totally true, this is something I just dont see in my own code. In case of a full coloumn, the player is not turned over because the player number does not change. There is a continue statement for that case. i would like to go throught a different file if you are up for it. We could use the SO chat
â Tweakimp
Feb 16 at 19:49
Thank you for your answer. I made it a class to call it multiple times by another module, for example when I let random players play against each other multiple times. The matrix is the list of lists that stores the game data, the board is the thing that is drawn into the console. The code reuse is totally true, this is something I just dont see in my own code. In case of a full coloumn, the player is not turned over because the player number does not change. There is a continue statement for that case. i would like to go throught a different file if you are up for it. We could use the SO chat
â Tweakimp
Feb 16 at 19:49
I'm off in a minute. Feel free to start a chat, I definitely will answer, but unfortunately not now.
â stefan
Feb 16 at 20:04
I'm off in a minute. Feel free to start a chat, I definitely will answer, but unfortunately not now.
â stefan
Feb 16 at 20:04
I dont know id this works... chat.stackexchange.com/rooms/73272/tweakimp
â Tweakimp
Feb 16 at 20:33
I dont know id this works... chat.stackexchange.com/rooms/73272/tweakimp
â Tweakimp
Feb 16 at 20:33
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186932%2fconnect4-variant-in-python%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password