javamultidimensional-arrayothello

How to check adjacent indices of a 2D array - Othello


Just to be clear, I have looked at problems somewhat similar to this on Stack Overflow and other websites before asking for help. I also included all of the code below just in case it could help anyone understand the problem.

In the game Othello, also known as Reversi, there are two players who use tiles of opposite colors. The player needs to place a tile so it is adjacent to a tile of the opposite color and the opposite color must be surrounded by a tile on either side in the same direction. For example, if there is a white tile to the left of a black tile, then another black tile needs to be placed on the left of the white tile so it is surrounded. If a tile is surrounded it flips.

(Black - White - Black) --> (Black - Black - Black)

Surrounding can happen either horizontally, diagonally or vertically.

The problem I'm having is I do not know how to check all of the indices and see if they work at once. I have tried checking for the values adjacent to a tile of the opposite color, by checking all possible values next to it. This doesn't work properly as it can't make sure that a tile is surrounded on both sides or apply to a line of more than one tile in a row.

/**
 *  Checks to see if a valid move can be made at the indicated OthelloCell,
 *  for the given player.
 *  @param  xt      The horizontal coordinate value in the board.
 *  @param  yt      The vertical coordinate value in the board.
 *  @param  bTurn   Indicates the current player, true for black, false for white
 *  @return         Returns true if a valid move can be made for this player at
 *                  this position, false otherwise.
 */
public boolean isValidMove(int xt, int yt, boolean bTurn)
{
    int length = board[0].length;

    // checks if the tile has already been picked, meaning it can no longer be selected
    if(board[xt][yt].hasBeenPlayed())
    {
        return false;
    }
    else
    {
        /* For BLACK (Working) */
        if(bTurn)
        {
            // checks tiles one row above
            if(xt + 1 < length && board[xt + 1][yt].getBlackStatus() == false)
            {
                System.out.println("the one 1 row up and in the same column doesn't work");
                return true;
            }
            if(xt + 1 < length && board[xt + 1][yt + 1].getBlackStatus() == false)
            {
                System.out.println("the one 1 row up and in the right column doesn't work");
                return true;
            }
            if(xt + 1 < length && board[xt + 1][yt - 1].getBlackStatus() == false)
            {
                System.out.println("the one 1 row up and in the left column doesn't work");
                return true;
            }
            // checks tiles left and right
            if(yt + 1 < length && board[xt][yt + 1].getBlackStatus() == false)
            {
                System.out.println("the one in the same row and in the right column doesn't work");
                return true;
            }
            if(yt > 1 && board[xt][yt - 1].getBlackStatus() == false)
            {
                System.out.println("the one in the same row and in the left column doesn't work");
                return true;
            }
            // checks tiles one row below
            if(xt > 1 && board[xt - 1][yt].getBlackStatus() == false)
            {
                System.out.println("The one 1 row down and in the same column doesn't work");
                return true;
            }
            if(xt > 1 && board[xt - 1][yt + 1].getBlackStatus() == false)
            {
                System.out.println("The one 1 row down and in the right column doesn't work");
                return true;
            }
            if(xt > 1 && board[xt - 1][yt - 1].getBlackStatus() == false)
            {
                System.out.println("The one 1 row down and in the left column doesn't work");
                return true;
            }
        }

    }
    return false;
}

/**
 *  Checks to see if a valid move can be made at the indicated OthelloCell, in a 
 *  particular direction (there are 8 possible directions).  These are indicated by:
 *  (1,1) is up and right
 *  (1,0) is right
 *  (1,-1) is down and right
 *  (0,-1) is down
 *  (-1,-1) is down and left
 *  (-1,0) is left
 *  (-1,1) is left and up
 *  (0,1) is up
 *  @param  xt      The horizontal coordinate value in the board.
 *  @param  yt      The vertical coordinate value in the board.
 *  @param  i       -1 is left, 0 is neutral, 1 is right,
 *  @param  j       -1 is down, - is neutral, 1 is up.
 *  @param  bTurn   Indicates the current player, true for black, false for white.
 *  @return         Returns true if this direction has pieces to be flipped, false otherwise.
 */
public boolean directionValid(int xt, int yt, int i, int j, boolean bTurn)
{       
    return true;
}

Above are the two methods I'm having trouble with.

public class Othello
{
/**    The board object.  This board will be 8 x 8, and filled with OthelloCells.
 *     The cell may be empty, hold a white game piece, or a black game piece.       */
private OthelloCell [][] board;

/**    The coordinates of the active piece on the board.                            */
private int x, y;

/**    Booleans indicating that the mouse is ready to be pressed, that it is     
 *     black's turn to move (false if white's turn), and that the game is over.     */
private boolean mousePressReady, blackTurn, gameOver;

/**
 *  Creates an Othello object, with a sized graphics canvas, and a 2D (8 x 8) array
 *  of OthelloCell, setting up initial values.
 */
/* COMPLETE */
public Othello ( )
{
    StdDraw.setCanvasSize(500,650);
    StdDraw.setXscale(0,1);
    StdDraw.setYscale(0,1.3);
    StdDraw.enableDoubleBuffering();
    Font font = new Font("Arial", Font.BOLD, 30);
    StdDraw.setFont(font);

    startBoard();
}

/**
 *  Called by the constructor, or when the player hits the "RESET" button,
 *  initializing the game board (an 8 x 8 array of OthelloCell).
 */
/* COMPLETE */
public void startBoard ( )
{
    mousePressReady = blackTurn = true;
    gameOver = false;
    board = new OthelloCell[8][8];
    for ( int i = 0; i < board.length; i++ )
    {
        for ( int j = 0; j < board[i].length; j++ )
        {
            board[i][j] = new OthelloCell(i,j);
        }
    }
    board[3][3].playIt();
    board[3][3].setBlack(true);
    board[4][4].playIt();
    board[4][4].setBlack(true);
    board[4][3].playIt();
    board[4][3].setBlack(false);
    board[3][4].playIt();
    board[3][4].setBlack(false);
}

/**
 *  Sets up and runs the game of Othello.
 */
/* COMPLETE */
public static void main(String [] args)
{
    Othello game = new Othello();
    game.run();
}

/**
 *  Runs an endless loop to play the game.  Even if the game is over, the
 *  loop is still ready for the user to press "RESET" to play again.
 */
/* COMPLETE */
public void run ( )
{
    do
    {
        drawBoard();
        countScoreAnddrawScoreBoard();
        StdDraw.show();
        StdDraw.pause(30);
        makeChoice();
        gameOver = checkTurnAndGameOver();
    }
    while(true);
}

/**
 *  Draws the board, in its current state, to the GUI.
 */
/* COMPLETE */
public void drawBoard ( )
{
    StdDraw.setPenColor(new Color(150,150,150));
    StdDraw.filledRectangle(0.5,0.75,0.5,0.75);
    StdDraw.setPenColor(new Color(0,110,0));
    StdDraw.filledSquare(0.5,0.5,0.45);
    StdDraw.setPenColor(new Color(0,0,0));
    StdDraw.filledSquare(0.5,0.5,0.42);
    for ( int i = 0; i < board.length; i++ )
    {
        for ( int j = 0; j < board[i].length; j++ )
        {
            board[i][j].drawCell();
        }
    }
}

/**
 *  Waits for the user to make a choice.  The user can make a move
 *  placing a black piece or the white piece (depending on whose turn
 *  it is), or click on the "RESET" button to reset the game.
 */
/* COMPLETE */
public void makeChoice ( )
{
    boolean moveChosen = false;
    while(!moveChosen)
    {
        if(mousePressReady && StdDraw.isMousePressed())
        {           
            mousePressReady = false;
            double xval = StdDraw.mouseX();
            double yval = StdDraw.mouseY();
            if(xval > 0.655 && xval < 0.865 && yval > 1.15 && yval < 1.23)    //  This if checks for a reset.
            {
                startBoard();
                return;
            }
            if(xval < 0.1 || xval > 0.9 || yval < 0.1 || yval > 0.9)          //  This if checks for a press off the board.
            {
                return;
            }
            int tempx = (int)(10 * (xval - 0.1));
            int tempy = (int)(10 * (yval - 0.1));
            if(isValidMove(tempx,tempy,blackTurn))                            //  This if checks to see if the move is valid.
            {
                x = tempx;
                y = tempy;
                playAndFlipTiles();
                blackTurn = !blackTurn;
                System.out.println(x + "  " + y);
            }
        }
        if(!StdDraw.isMousePressed() && !mousePressReady)                      //  This if gives back control when the mouse is released.
        {
            mousePressReady = true;
            return;
        }
        StdDraw.pause(20);
    }

}

/**
 *  Checks to see if a valid move can be made at the indicated OthelloCell,
 *  for the given player.
 *  @param  xt      The horizontal coordinate value in the board.
 *  @param  yt      The vertical coordinate value in the board.
 *  @param  bTurn   Indicates the current player, true for black, false for white
 *  @return         Returns true if a valid move can be made for this player at
 *                  this position, false otherwise.
 */
public boolean isValidMove(int xt, int yt, boolean bTurn)
{
    int length = board[0].length;

    // checks if the tile has already been picked, meaning it can no longer be selected
    if(board[xt][yt].hasBeenPlayed())
    {
        return false;
    }
    else
    {

    }
    return false;
}

/**
 *  Checks to see if a valid move can be made at the indicated OthelloCell, in a 
 *  particular direction (there are 8 possible directions).  These are indicated by:
 *  (1,1) is up and right
 *  (1,0) is right
 *  (1,-1) is down and right
 *  (0,-1) is down
 *  (-1,-1) is down and left
 *  (-1,0) is left
 *  (-1,1) is left and up
 *  (0,1) is up
 *  @param  xt      The horizontal coordinate value in the board.
 *  @param  yt      The vertical coordinate value in the board.
 *  @param  i       -1 is left, 0 is neutral, 1 is right,
 *  @param  j       -1 is down, - is neutral, 1 is up.
 *  @param  bTurn   Indicates the current player, true for black, false for white.
 *  @return         Returns true if this direction has pieces to be flipped, false otherwise.
 */
public boolean directionValid(int xt, int yt, int i, int j, boolean bTurn)
{       
    return true;
}

/**
 *  Places a game piece on the current cell for the current player.  Also flips the
 *  appropriate neighboring game pieces, checking the 8 possible directions from the
 *  current cell.
 */
public void playAndFlipTiles ( )
{
    board[x][y].setBlack(blackTurn);
    board[x][y].playIt();       

    //  To be completed by you.
}

/**
 *  A helper method for playAndFlipTiles.  Flips pieces in a given direction.  The
 *  directions are as follows:
 *  (1,1) is up and right
 *  (1,0) is right
 *  (1,-1) is down and right
 *  (0,-1) is down
 *  (-1,-1) is down and left
 *  (-1,0) is left
 *  (-1,1) is left and up
 *  (0,1) is up
 *  @param  xt      The horizontal coordinate value in the board.
 *  @param  yt      The vertical coordinate value in the board.
 *  @param  i       -1 is left, 0 is neutral, 1 is right,
 *  @param  j       -1 is down, - is neutral, 1 is up.
 */
public void flipAllInThatDirection(int xt, int yt, int i, int j)
{

}

/**
 *  Counts the white pieces on the board, and the black pieces on the board.
 *  Displays these numbers toward the top of the board, for the current state
 *  of the board.  Also prints whether it is "BLACK'S TURN" or "WHITE'S TURN"
 *  or "GAME OVER".
 */
 /* COMPLETE */
public void countScoreAnddrawScoreBoard ( )
{
    int whiteCount = 0, blackCount = 0;

    for(int i = 0; i < board.length; i++)
    {
        for(int j = 0; j < board[i].length; j++)
        {
            if(board[i][j].hasBeenPlayed())
            {
                if(board[i][j].getBlackStatus())
                {
                    blackCount++;
                }
                else
                {
                    whiteCount++;
                }
            }
        }
    }

    drawScoresAndMessages(whiteCount,blackCount);
}

/**
 *  A helper method for countScoreAnddrawScoreBoard.  Draws the scores
 *  and messages.
 *  @param  whiteCount      The current count of the white pieces on the board.
 *  @param  blackCount      The current count of the black pieces on the board.
 */
 /* COMPLETE */
public void drawScoresAndMessages(int whiteCount, int blackCount)
{
    StdDraw.setPenColor(new Color(0,0,0));
    StdDraw.filledRectangle(0.41,1.05,0.055,0.045);
    StdDraw.filledRectangle(0.80,1.05,0.055,0.045);
    StdDraw.filledRectangle(0.76,1.19,0.11,0.045);
    StdDraw.setPenColor(new Color(255,255,255));
    StdDraw.filledRectangle(0.41,1.05,0.05,0.04);
    StdDraw.filledRectangle(0.80,1.05,0.05,0.04);
    StdDraw.filledRectangle(0.76,1.19,0.105,0.04);
    StdDraw.setPenColor(new Color(0,0,0));
    StdDraw.text(0.24,1.04,"BLACK");
    StdDraw.text(0.41,1.04,"" + blackCount);
    StdDraw.text(0.63,1.04,"WHITE");
    StdDraw.text(0.80,1.04,"" + whiteCount);
    StdDraw.text(0.76,1.18,"RESET");
    if(gameOver)
    {
        StdDraw.text(0.34,1.18,"GAME OVER");
    }
    else if(blackTurn)
    {
        StdDraw.text(0.34,1.18,"BLACK'S TURN");
    }
    else
    {
        StdDraw.text(0.34,1.18,"WHITE'S TURN");
    }
}

/**
 *  Checks to see if black can play.  Checks to see if white can play.
 *  If neither can play, the game is over.  If black can't go, then set
 *  blackTurn to false.  If white can't go, set blackTurn to true.
 *  @return         Returns true if the game is over, false otherwise.
 */
 /* COMPLETE */
public boolean checkTurnAndGameOver ( )
{
    boolean whiteCanGo = false, blackCanGo = false;

    //  To be completed by you.

    return false;
}
}

/**
 * Represents a single cell in the game of Othello.  By default, a cell is black, and
 * has not been played.  When a game piece is "placed" on the board, the boolean played
 * is set to true.  If the game piece is black, then the boolean black is true, and if
 * the game piece is white, then the boolean black is false.  The ints x and y 
 * represent the coordinate values of the cell within the game board, with the lower
 * left at (0,0) and the upper right at (7,7).
 */
class OthelloCell         
{
/**    The coordinates of the active piece on the board.                              */
private int x, y;

/**    Booleans indicating if a piece has been played (or is empty), and indicating
 *     if the piece is black (or white)                                               */
private boolean played, black;

/**
 *  Creates an OthelloCell object, at the given coordinate pair.
 *  @param  i      The horizontal coordinate value for the cell on the board.
 *  @param  j      The vertical coordinate value for the cell on the board.
 */
 /* COMPLETE */
public OthelloCell(int i, int j)
{
    played = false;
    x = i;
    y = j;
    black = true;
}

/**
 *  Draws the cell on the board, in its current state.
 */
 /* COMPLETE */
public void drawCell ( )   
{
    StdDraw.setPenColor(new Color(0,0,0));
    StdDraw.filledSquare(0.15 + 0.1 * x, 0.15 + 0.1 * y, 0.05);
    StdDraw.setPenColor(new Color(0,110,0));
    StdDraw.filledSquare(0.15 + 0.1 * x, 0.15 + 0.1 * y, 0.048);

    if(played)
    {
        for(int i = 0; i <= 20; i++)
        {
            if(black)
            {
                StdDraw.setPenColor(new Color(5+8*i,5+8*i,5+8*i));
            }
            else
            {
                StdDraw.setPenColor(new Color(255-8*i,255-8*i,255-8*i));
            }
            StdDraw.filledCircle(0.15 + 0.1 * x - i*0.001, 0.15 + 0.1 * y + i*0.001, 0.043-i*0.002);
        }
    }
}

/**
 *  Sets the piece to black (black true) or white (black false).
 *  @param  bool      The value to be assigned to the game piece.
 */
 /* COMPLETE */
public void setBlack(boolean bool)
{
    if(bool)
    {
        black = true;
    }
    else
    {
        black = false;
    }
}

/**
 *  Return the status of black; true for a black piece, false for a white piece.
 *  @return            Returns true for a black piece, false for a white piece.
 */
 /* COMPLETE */
public boolean getBlackStatus ( )
{
    return black;
}

/**
 *  Sets the value of played to true, to indicate that a piece has been placed on this cell.
 */
 /* COMPLETE */
public void playIt ( )   
{
    played = true;
}

/**
 *  Return the status of played, indicating whether or not there is a game piece on this cell.
 *  @return            Returns true if a game piece is on this cell, false otherwise.
 */
 /* COMPLETE */
public boolean hasBeenPlayed ( )
{
    return played;
}
}

Solution

  • This is my approach, although its in C# I am sure its of some use. Also uses part of the solution from Ashutosh.

    My Board is the two dimensional array "pieces", lowercase "length" is just the size of the board and num is an indicator for a color: Empty is 0, num is whats currently being played, 1 for White and 2 for Black.

    All its doing is looking for the closest matching color in either direction without empty spaces inbetween and then placing the current playing color in all the array spaces inbetween.

    This can probably be done way smarter, I dont even like my approach of always checking for the full size of the field in every direction and just ignoring index out of range exceptions, but it was the simplest

                int[] rowNbr = { -1, -1, -1, 0, 0, 1, 1, 1 };
                int[] colNbr = { -1, 0, 1, -1, 1, -1, 0, 1 };
                for (int x = 0; x < 8; x++)
                {
                    int facX = rowNbr[x];
                    int facY = colNbr[x];
                    try
                    {
                        for (int i = 1; i < length; i++)
                        {
                            if (pieces[click.X + i * facX, click.Y + i * facY] == 0) break;
                            if (pieces[click.X + i * facX, click.Y + i * facY] == (byte)num)
                            {
                                for (int j = i - 1; j > 0; j--)
                                {
                                    pieces[click.X + j * facX, click.Y + j * facY] = (byte)num;
                                }
                                break;
                            }
                        }
                    }
                    catch { } 
                }