Home > Software engineering >  How can I regroup my two for loops into one with only a tiny difference between the two?
How can I regroup my two for loops into one with only a tiny difference between the two?

Time:09-17

I have the following code with the only difference being the j and the I position in my list. Is there any way to make it better by writing a function or something like that because I can't quite figure it out?

for i in range(dimension):
    for j in range(dimension - 4):
        k = 1
        while k < 5 and gameboard[i][j k] == gameboard[i][j]:
            k  = 1
        if k == 5:
            winner = gameboard[i][j]

for i in range(dimension):
    for j in range(dimension - 4):
        k = 1
        while k < 5 and gameboard[j k][i] == gameboard[j][i]:
            k  = 1
        if k == 5:
            winner = gameboard[j][i]

CodePudding user response:

You could merge the two loops by inserting an additional nested loop that handles the permutations of i and j and the corresponding dimension deltas:

for i in range(dimension):
    for j in range(dimension - 4):
        for i,j,di,dj in [ (i,j,0,1), (j,i,1,0) ]:
            k = 1
            while k < 5 and gameboard[i k*di][j k*dj] == gameboard[i][j]:
                k  = 1
            if k == 5:
                winner = gameboard[i][j]

Generalized for all directions

Alternatively you could create a function that tells you if there is a win in a given direction and use that in a loop.

def directionWinner(board,i,j,di,dj):
    player,pi,pj = board[i][j],i,j
    # if player == empty: return
    for _ in range(4):
        pi,pj = pi di, pj dj
        if pi not in range(len(board)): return
        if pj not in range(len(board)): return
        if board[pi][pj] != player:     return  
    return player

Then use it to check all directions:

for pos in range(dimensions*dimensions):
    i,j = divmod(pos,dimensions)
    for direction in [ (0,1),(1,0),(1,1),(1,-1) ]:
        winner = directionWinner(gameboard,i,j,*direction)
        if winner is not None: break
    else: continue; break

The directions are represented by the increase/decrease in vertical and horizontal coordinates (deltas) for each step of one. So [ (0,1),(1,0),(1,1),(1,-1) ] gives you "down", "across", "diagonal 1", diagonal 2" respectively.

Checking only from last move

The same idea can be used to check for a winner from a specific position (e.g. checking if last move is a win):

# count how may consecutive in a given direction (and its inverse)
def countDir(board,i,j,di,dj,inverted=False):
    player,pi,pj = board[i][j],i,j
    count  = 0
    while pi in range(len(board) and pj in range(len(board)):
          if board[pi][pj] == player: count  = 1
          else: break
          pi, pj = pi di, pj dj 
    if not inverted: 
       count  = countDir(board,i,j,-di,-dj,True)-1
    return count

 def winnerAt(board,i,j):
     for direction in [ (0,1),(1,0),(1,1),(1,-1) ]:
         if countDir(board,i,j,*direction)>=5:
             return board[i,j]

Then, after playing at position i,j, you can immediately know if the move won the game:

if winnerAt(gameboard,i,j) is not None:
    print(gameboard[i][j],"wins !!!")
   

CodePudding user response:

It's maybe a trivial change, but why don't you just put the second check in the first loop just by swapping indices and using another variable for the while part? I mean something like this:

def check_winner(gameboard, dimension):
    for i in range(dimension):
        for j in range(dimension - 4):
            # perform the first check
            k = 1
            while k < 5 and gameboard[i][j k] == gameboard[i][j]:
                k  = 1
            if k == 5:
                return gameboard[i][j]
            # and the second check
            l = 1
            while l < 5 and gameboard[j   k][i] == gameboard[j][i]:
                l  = 1
            if l == 5:
                return gameboard[j][i]

Anyways, it's cleaner to just return if you found the winner, and avoid redundancy.

  • Related