'Check moves in othello/reversi [Python 3]

I'm currently trying to create the game of othello (aka, reversi) in python 3.

I have huge problems with the section of the program where it shall evaluate if a move is valid or not.

What I want to create:

  1. Check if a position on the board is empty or not
  2. Check if there are any neighbours of the opposite color
  3. If there are such neighbours, continue in that direction and see if we can reach one of our own pieces without crossing an empty position.

I have tried many different functions but i cant get it right...

In the link down below is my latest attempt,

Link to git on github



Solution 1:[1]

Check if a position on the board is empty or not

The first thing that comes to mind is to represent the board as a string, where every character in the string represents the state of that square, starting from 0 (top left) to 63 (bottom right).

For example, say you represented a square being occupied by a white by "o", for black, "x", and an empty square would be ".", for the starting state, it would be:

board = "...........................ox......xo..........................."

Then if you wanted to check whether a square is empty or not, you would check whether that character is empty:

if board[0] == ".":
    #Square 0 (top left corner) is empty!

Check if there are any neighbours of the opposite color

For this, you could pre-store tuples of directions that correspond to all of a square's neighbours (this will also be helpful for calculating possible moves):

DIRECTIONS = [
    (0, -1),  #Up
    (1, -1),  #Up-Right
    (1, 0),   #Right
    (1, 1),   #Down-Right
    (0, 1),   #Down
    (-1, 1),  #Down-Left
    (-1, 0),  #Left
    (-1, -1), #Up-Left
]

Where the first item of the tuple is the amount to move horizontally, and the second item being the amount to move vertically. So if you wanted to find neighbors of the opposite color, you would get the index of the current square:

row, col = index // 8, index % 8 # To convert from an index from string to (x, y) values

Then loop through the directions, and check what color the neighbor is using the logic from above.

current = board[index]

for x, y in DIRECTIONS:
    r, c = index // (8), index % (8)
    r += y
    c += x
    tile = board[r * 8 + c]

    if tile != current and tile != ".": #Not same color, and not empty, so must be opposite color
         #Do stuff

Now since that is pretty basic code, it doesn't cover possibilities where the current tile is on an edge on the corner, where the index would go to an incorrect position, looping around. One possibility is to just hardcode those cases in, and ignore those directions, however another (possibly more elegant way) can be used as well. Instead of representing the board as a 64-character string, you can represent it as a 100-character string to include a border like so:

? ? ? ? ? ? ? ? ? ?
? . . . . . . . . ?
? . . . . . . . . ?
? . . . . . . . . ?
? . . . . . . . . ?
? . . . . . . . . ?
? . . . . . . . . ?
? . . . . . . . . ?
? . . . . . . . . ?
? ? ? ? ? ? ? ? ? ?

Then you can simply check if the "neighbor" is part of the border (in this case represented by a question mark, but can be anything else besides the ones you use to represent empty spaces and discs).

However, you will need to account for the fact that you have to convert indices from a 8x8 board to 10x10 board (and convert back) appropriately.

If there are such neighbors, continue in that direction and see if we can reach one of our own pieces without crossing an empty position.

This is pretty simple once you implement the above logic, you will just need a while loop to check tiles in a certain direction, and whether they can be taken if we reach the original's color.

SIZE = 8
DIRECTIONS = [....]
BORDERCHAR = "?"

def add_border(board):
    edge = BORDERCHAR * (SIZE + 2)
    new_board = edge
    for i in range(0, SIZE ** 2, SIZE):
        new_board += BORDERCHAR + board[i:i + SIZE] + BORDERCHAR   
    new_board += edge
    return new_board

def possible_moves(board, token):
    board = add_border(board) #Converting to 10x10 
    moves = []
    if token == "x":
        other = "o"
    else:
        other = "x"

    for space in [i for i, j in enumerate(board) if j == "."]:

        for x, y in DIRECTIONS:
            r, c = space // (SIZE + 2), space % (SIZE + 2)
            r += y
            c += x
            tile = board[r * (SIZE + 2) + c]

            #Checking to see if we reach one of the original discs
            entered = False
            while tile == other:
                entered = True
                r += y
                c += x
                tile = board[r * (SIZE + 2) + c]

            if entered is True and tile == token:
                moves.append((space // (SIZE + 2), space % (SIZE + 2)))
                break

    moves = [(r - 1) * (SIZE) + (c - 1) for r, c in moves] #Converting back to 8x8

    return moves

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1