preloader
image

Othello

What is Othello/Reversi?

Reversi is a strategy board game for two players, played on an 8×8 uncheckered board. There are sixty-four identical game pieces called disks (often spelled “discs”), which are light on one side and dark on the other. Players take turns placing disks on the board with their assigned color facing up. During a play, any disks of the opponent’s color that are in a straight line and bounded by the disk just placed and another disk of the current player’s color are turned over to the current player’s color.

The object of the game is to have the majority of disks turned to display your color when the last playable empty square is filled.


Implementation

I this project out by creating all the necessary tools, setting up for the rest of the code. The board is represent as a 2D list, which can be access by [row][column]. I then created a copy of the board for the computer to go through each possibble move and find the best possible move. The set_starting_position is quite self explanitory, and the print board prints the 2D list to the terminal nicely.

def make_board(board):
  for i in range(8):
      for c in range(8):
          board[i][c] = " "

def copy_board(board,copy):
    for i in range(8):
        for c in range(8):
            copy[i][c] = board[i][c]
    # makes a copy of the board to find the best possible move for the AI

def set_starting_position(board):
    board[3][3] = "X"
    board[4][4] = "X"
    board[3][4] = "O"
    board[4][3] = "O"
    # sets the starting positions

def print_board(board):
    print(" |A|B|C|D|E|F|G|H|")
    print(" |---------------|")
    for i in range(8):
        rowstring = str(i + 1)
        for c in range(8):
            rowstring = rowstring + "|" + board[c][i]
        print(rowstring + "|")
        print(" |---------------|")

This next method gets a bit complicated. The ultimate goal of the method is to take the move from the player, check if it is legal, and play it on the board. Most of it is simple, if the players move is not one of the rows or columns then it can’t be a move. It is passes that if, then it asks the user for a move, then calls the method check_if_valid_move()

def get_move(board, player):
    row = 0
    column = 0
    inputplay = "Z0"
    # if the users inputs is either not ABCDEFGH 123456789 then it will ask the user for another input
    while inputplay[1] not in ("1", "2", "3", "4", "5", "6", "7", "8") or inputplay[0] not in ("A", "B", "C", "D", "E", "F", "G", "H"):
        if player == "X":
            inputplay = input("Mr.X, What is your play A1-H8? ")
        else:
            inputplay = input("Mr.O, What is your play A1-H8? ")

        if inputplay[1] in ("1", "2", "3", "4", "5", "6", "7", "8") and inputplay[0] in ("A", "B", "C", "D", "E", "F", "G", "H"):
            row = int(inputplay[1]) - 1
            column = ord(inputplay[0]) - 65
            # turn the letters into an ascii value and subtracts by the appropriate amount
            move_good = 0
            move_good = check_if_valid_move(board, column, row, player)
            #makes sure the move the user inputed is valid
            if move_good == 0:
                inputplay = "Z0"
            # if it isn't ask the user for another input
            else:
                board[column][row]= player
                letter_change(board, row, column, player)
            # if it is put it in and change the letters in between

This is where some of the methods get quite complicated, so instead of trying to explain it as a broad method, I added comments to the code so that it should be easier to track.

def check_if_valid_move(board, column, row, player):

    movement_direction_steps = [[0,-1],[0,1],[-1,-1],[1,1],[-1,0],[1,0],[-1,1],[1,-1]]
    # this is what happens to the cordinates if you move 1 way in each direction
    if player=="X":
        opponent="O"
    else:
        opponent="X"

    valid_move = 0
    if board[column][row] == (" "):
        for direction in range(8):

            if (valid_move == 0):
                # only lopping if valid move is 0 because we are checking to see if it is a valid move
                square_to_examine_row = row
                square_to_examine_column =column
                this_direction_column_chg=movement_direction_steps[direction][0]
                this_direction_row_chg=movement_direction_steps[direction][1]
                # variables that we want to look at and move towards
                might_be_valid_move=0
                end_walk = 0
                while ((square_to_examine_row >= 0) and (square_to_examine_row <= 7) and
                       (square_to_examine_column >= 0) and (square_to_examine_column <= 7) and # keeping it inside the board
                       (end_walk == 0)):

                    square_to_examine_row = square_to_examine_row + this_direction_row_chg
                    square_to_examine_column = square_to_examine_column + this_direction_column_chg

                    if ((square_to_examine_row >= 0) and (square_to_examine_row <= 7) and
                           (square_to_examine_column >= 0) and (square_to_examine_column <= 7)):

                           if ((might_be_valid_move==1) and (board[square_to_examine_column][square_to_examine_row] == player)):
                               valid_move = 1
                               end_walk = 1
                               # walking out and seeing if the piece after an oppenents piece is your piece becuase if it is than the move is valid
                               # we do this 8 times so that if you walk out and keep seeing an oppenents piece for 7 tiles but on the 8th tiles you find your piece, it will still work
                           if ((might_be_valid_move==1) and (board[square_to_examine_column][square_to_examine_row] == " ")):
                               valid_move = 0
                               end_walk = 1
                               # if it is walking out finds an opponents piece but then finds a space the move is invalid


                           if ((might_be_valid_move==0) and (board[square_to_examine_column][square_to_examine_row] in (" ", player))):
                               end_walk = 1
                               # if it is at the start of walking out and the first thing it hits is a space or a player then the move is invalid

                           if ((might_be_valid_move==0) and (board[square_to_examine_column][square_to_examine_row] == opponent)):
                               might_be_valid_move = 1
                               # if it is at the start of walking out and the first thing it hits is an oppents player then the move might be valid
    return valid_move

Conclusion

This was the hardest project in my first year of programming and I thought it showed in my programming. It was a bit sloppy and not a great implmenetation of AI. I can definetly improve on this and make something better in the future.

You can find my Github link for the code here