Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
291 views
in Technique[技术] by (71.8m points)

python - Finding neighbor cells in a grid with the same value. Ideas how to improve this function?

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
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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. :)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...