Reputation: 1
I am new to Python (learning it for a little over 1 month) and I tried creating Tic Tac Toe. However once I finished it, I decide to expand the board (from 3x3 to 9x9 depending from the customer input) and allow a win by connecting 4 in a row, column or diagonal anywhere in the board.
Therefore I needed a function that search - depending from the customer input - in every directions on the board, without going overboard, for 3 connected cells with the same mark.
Then I realize that actually I need to check simultaneously in both opposite direction from the mark, as there is a possibility that the currently placed mark can connect other groups of the same mark and winning by doing this.
Below you can find my solution of the above winning condition, which is working, however it does not look pretty as there is a lot of repetitive almost identical for loops. But I have zero clue how to combine them.
There are 8 loops (as there are 8 directions), in 4 groups of opposite directions, and each loop check if the next cell is with the same value (mark) as the one that is currently placed by the customer (either 'O' or 'X').If it is the count variable is increased by 1. If not - the loop breaks. Then I am checking if the two opposite direction loops managed to made count == 4. If yes - we have a winner. If not - we go to the next pair.
I am sure there should be easier to maintain code, but I am not able to think of any.
Thank you for your help!
P.S. if there is a need I will post more from my code
def do_we_have_a_winner(position, current_mark):
global game_board
connect = 4
# unpacking the coordinates of the current position
x, y = coordinates[position]
length = len(game_board)
count = 0
for i in range(0, connect):
if (x-i in range(0, length)) and (y+i in range(0, length)):
if game_board[x-i][y+i] != current_mark:
break
else:
count += 1
for i in range(1, connect):
if (x+i in range(0, length)) and (y-i in range(0, length)):
if game_board[x+i][y-i] != current_mark:
break
else:
count += 1
if count == connect:
print("We have a winner!")
return True
count = 0
for i in range(0, connect):
if (x+i in range(0, length)) and (y+i in range(0, length)):
if game_board[x+i][y+i] != current_mark:
break
else:
count += 1
for i in range(1, connect):
if (x-i in range(0, length)) and (y-i in range(0, length)):
if game_board[x-i][y-i] != current_mark:
break
else:
count += 1
if count == connect:
print("We have a winner!")
return True
count = 0
for i in range(0, connect):
if (y+i in range(0, length)):
if game_board[x][y+i] != current_mark:
break
else:
count += 1
for i in range(1, connect):
if (y-i in range(0, length)):
if game_board[x][y-i] != current_mark:
break
else:
count += 1
if count == connect:
print("We have a winner!")
return True
count = 0
for i in range(0, connect):
if (x+i in range(0, length)):
if game_board[x+i][y] != current_mark:
break
else:
count += 1
for i in range(1, connect):
if (x-i in range(0, length)):
if game_board[x-i][y] != current_mark:
break
else:
count += 1
if count == connect:
print("We have a winner!")
return True
return False
Upvotes: 0
Views: 587
Reputation: 71477
Here's an approach that involves less copying and pasting -- hopefully it gives you some idea of how to break things down into the smallest number of the most reusable pieces possible. :)
The general idea here is to come up with a way of expressing the concept of scanning a line across the board in different directions in such a way that you can just express the direction and then let the same block of code handle the scan regardless of what the direction is.
from typing import List, Optional, Tuple
def do_we_have_a_winner(board: List[List[Optional[str]]], length: int) -> Optional[str]:
"""Returns the 'mark' of the player with a row of the given length."""
width = range(len(board))
height = range(len(board[0]))
# Do four scans across the board -- right, down, and diagonals.
for dx, dy in [(0, 1), (1, 0), (1, 1), (1, -1)]:
edges: List[Tuple[int, int]] = []
if dx > 0:
# scanning right, start from left edge
edges += [(0, y) for y in height]
if dy > 0:
# scanning down, start from top edge
edges += [(x, 0) for x in width]
if dy < 0:
# scanning up, start from bottom edge
edges += [(x, height[-1]) for x in width]
for ex, ey in edges:
mark: Optional[str] = None
row = 0
x, y = ex, ey
while x in width and y in height:
if board[x][y] == mark:
row += 1
else:
mark = board[x][y]
row = 1
if mark is not None and row >= length:
return mark
x, y = x + dx, y + dy
return None
print(do_we_have_a_winner([
['X', 'O', 'O'],
['O', 'X', 'O'],
['O', 'O', 'X'],
], 3)) # X
Note that this function assumes one at most winner, so if there are multiple winners, it will only return one of them -- I'll leave it as an exercise for the reader to figure out how to change it to handle that situation better. :)
Upvotes: 2