Is this an effective way to determine if a someone has won in connect 4?

135 Views Asked by At

I'm using the following function to determine if a winner has been crowned in connect four. Piece is whether they are green or red, last is the last played move (by piece), and name is the discord name of the person playing the game, as it is a file based connect four game. Board is a 2d array being made of all empty and filled squares. Due to the game being based in python, is this a effecient way to check?

Examples: Piece: :green_circle:

Board: [[':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:'], [':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:'], [':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:'], [':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:'], [':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:'], [':white_large_square:', ':green_circle:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:', ':white_large_square:']]

Last: 5,1

Discord View: enter image description here

def checks(piece, last, name):
  board = []
  open_file = open(name, "r")
  thing = open_file.readline()
  for x in range(6):
    value = open_file.readline()
    board.append(value.strip("\n").split(","))
  open_file.close()
  cords = last.split(',')
  i = int(cords[0])  # row/x
  j = int(cords[1])  # column/y

  # checks for 000_
  if j > 2:
    if board[i][j - 1] == piece and board[i][j - 2] == piece and board[i][
        j - 3] == piece:
      return piece + " won"
  # checks for _000
  if j < 4:
    if board[i][j + 1] == piece and board[i][j + 2] == piece and board[i][
        j + 3] == piece:
      return piece + " won"
  # checks for downs
  if i < 3:
    if board[i + 1][j] == piece and board[i + 2][j] == piece and board[
        i + 3][j] == piece:
      return piece + " won"
  #check if you place in a 00_0
  if not j in [0, 1, 6]:
    if board[i][j + 1] == piece and board[i][j - 1] == piece and board[i][
        j - 2] == piece:
      return piece + " won"
  #check for 0_00
  if not j in [0, 5, 6]:
    if board[i][j + 1] == piece and board[i][j + 2] == piece and board[i][
        j - 1] == piece:
      return piece + " won"
  # check for top piece of a down-right diagonal
  if i < 3 and j < 4:
    if board[i + 1][j + 1] == piece and board[i + 2][j + 2] == piece and board[
        i + 3][j + 3] == piece:
      return piece + " won"
  # check for bottom piece of a down-right diagonal
  if i > 2 and j > 2:
    if board[i - 1][j - 1] == piece and board[i - 2][j - 2] == piece and board[
        i - 3][j - 3] == piece:
      return piece + " won"

  # check for top piece of down-left diagonal
  if i < 3 and j > 2:
    if board[i + 1][j - 1] == piece and board[i + 2][j - 2] == piece and board[
        i + 3][j - 3] == piece:
      return piece + " won"
  # check for bottom piece of down-left diagonal
  if i > 2 and j < 4:
    if board[i - 1][j + 1] == piece and board[i - 2][j + 2] == piece and board[
        i - 3][j + 3] == piece:
      return piece + " won"
  # check for 2nd top piece of down-right diagonal
  if i in [1,2,3] and j in [1,2,3,4]:
    if board[i - 1][j - 1] == piece and board[i +1 ][j + 1] == piece and board[i +2][j +2] == piece:
      return piece + " won"
  # check for 3rd piece of down-right diagonal
  if i in [2,3,4] and j in [2,3,4,5]:
    if board[i - 1][j - 1] == piece and board[i -2 ][j -2] == piece and board[i +1][j +1] == piece:
      return piece + " won"
  # check for 2nd piece of down-left diagonal
  if i in [1,2,3] and j in [2,3,4,5]:
    if board[i - 1][j + 1] == piece and board[i +1 ][j -1] == piece and board[i +2][j -2] == piece:
      return piece + " won"
  # check for 3rd piece in down-left diagonal
  if i in [2,3,4] and j in [1,2,3,4]:
    if board[i - 1][j + 1] == piece and board[i +1 ][j -1] == piece and board[i -2][j +2] == piece:
      return piece + " won"
2

There are 2 best solutions below

0
The Myth On

Keeping in mind your conditions are apt, your code could be enhanced in the following manners:

  • Replacing conditions with all()
  • Avoiding nested if conditions
  • Using elif in places of if
  • Use min() to check inequality for smallest rather than checking both i and j
  • Combine conditions to make it faster

Here's just an enhanced version of your code:

def checks(piece, last, name):
    board = []
    open_file = open(name, "r")
    # thing = open_file.readline()
    for x in range(6):
        value = open_file.readline()
        board.append(value.strip("\n").split(","))
        open_file.close()
        cords = last.split(',')
        i = int(cords[0])  # row/x
        j = int(cords[1])  # column/y
        winMsg = f"{piece} win" # create variable for ease
        if j > 2:
            if all(piece == value for value in [board[i][j-1], board[i][j-2], board[i][j-2]]) or all(piece == value for value in [board[i+1][j-1], board[i+2][j-2], board[i+3][j-3]]): return winMsg
        elif all(piece == value for value in [board[i][j+1], board[i][j+2], board[i][j+3]]): return winMsg
        elif all(piece == value for value in [board[i+1][j], board[i+2][j], board[i+3][j]]): return winMsg
        elif j not in [0, 1, 6] and all(piece == value for value in [board[i][j+1], board[i][j-1], board[i][j-2]]): return winMsg
        elif j not in [0, 5, 6] and all(piece == value for value in [board[i][j-1], board[i][j+1], board[i][j+2]]): return winMsg
        elif all(piece == value for value in [board[i+1][j+1], board[i][j+2], board[i][j-1]]): return winMsg
        elif min(i, j) > 2 and all(piece == value for value in [board[i-1][j-1], board[i-2][j-2], board[i-3][j-3]]): return winMsg
        elif i > 2 and all(piece == value for value in [board[i-1][j+1], board[i-2][j+2], board[i-3][j+3]]): return winMsg
        elif i in [1,2,3]:
            if j in [1,2,3,4] and all(piece == value for value in [board[i-1][j-1], board[i+1][j+1], board[i+2][j+2]]): return winMsg
            elif j == 5 and all(piece == value for value in [board[i-1][j+1], board[i+1][j-1], board[i+2][j-2]]): return winMsg
        elif i == 4:
            if j in [1,2,3,4] and all(piece == value for value in [board[i-1][j+1], board[i+1][j-1], board[i-2][j+2]]): return winMsg
            elif j == 5 and all(piece == value for value in [board[i-1][j-1], board[i-2][j-2], board[i+1][j+1]]): return winMsg

If you could provide an exact input when there is a win, maybe a better approach could be made. Hope this helps :)

0
Bill On

Not sure if this is faster but I've done this before in Numpy. Here's how I did it:

import numpy as np


class Connect4Game():

    # Construct a set of binary masks to find connect 4s
    win_mask = np.zeros((4*4, 7, 7), 'bool')
    idx1 = np.array(range(4))
    idx2 = np.array([3]*4)
    for i in range(4):
        win_mask[([i]*4, idx1+i, idx2)] = True
        win_mask[([i+4]*4, idx2, idx1+i)] = True
        win_mask[([i+8]*4, idx1+i, idx1+i)] = True
        win_mask[([i+12]*4, 6-idx1-i, idx1+i)] = True

    def __init__(self, data=None):
        # Extend the board area by adding borders
        self.ext_board = np.zeros((12, 13), 'int8')
        # Make the board a view slice
        self.board = self.ext_board.view()[3:9, 3:10]
        if data is not None:
            self.load_game(data)

    def reset(self):
        self.board [:, :] = 0

    def load_game(self, data):
        data = np.array(data)
        assert(data.shape == (6, 7))
        self.reset()
        self.board[data == ':green_circle:'] = 1
        self.board[data == ':red_circle:'] = 2

    def check_for_win(self, last, piece):
        row, col = last
        selection = self.ext_board[row:row+7, col:col+7]
        wins = np.nonzero(np.all(
            ((selection == piece) & self.win_mask) 
             == self.win_mask, axis=(1, 2)
        ))[0]
        return wins.tolist()


# Demo
g = Connect4Game(example_board)
print(g.board)
last = (5, 1)
piece = 1
assert g.board[last] == piece
wins = g.check_for_win(last, piece)
print(wins)

Output:

array([[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, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0]], dtype=int8)

[]