javareversi

Java Reversi Game Flipping Tiles


I am creating a reversi (othello) game on Java. Included below are my functions for when tiles need to flip to the left, right, below, or above the selected piece. For some reason, whenever a vertical flip is initiated, it ALWAYS works correctly. But, when a horizontal flip is initiated, sometimes it does nothing (when it should do something), or flips over blank tiles that are not supposed to be flipped. The gameboard is an 8 by 8 matrix using an extended JButton called BlankPiece. I will post that in addition to the functions. Can anyone suggest a more efficient / better way to run this game without bugs? Any help is greatly appreciated! If you need clarification, just ask.

   public void checkFlipDown(BlankPiece temp)
    {
        for(int j = temp.getRow() - 1; j>=0; j--){
            // j equals the square right above the clicked one, and it keeps going up until it hits the top of the board
        if ((gameboard[j][temp.getCol()].pieceType() != 0) && (gameboard[j][temp.getCol()].pieceType() != temp.pieceType())){
            //if the spot right above the clicked one is not black AND it is the other player's color,
            continue;
            // keep going with the block statement
        }
        if ((gameboard[j][temp.getCol()].pieceType() != 0) && (gameboard[j][temp.getCol()].pieceType() == temp.pieceType())){
        // if the spot right above is not black AND it is the same player's color,
            for(int i = temp.getRow(); i >= j; i--)
                // i equals the row of the clicked spot, it goes until it finds J, where the other green / purple end piece is
                {
                // change the colors of all colors in the clicked color's column and rows in between the other border piece
                    gameboard[i][temp.getCol()].change(temp.pieceType());
                }
            }
        else
        {
            // if no other border pieces exist, go out of the loop
            break;
        }
        }
    }
    public void checkFlipUp(BlankPiece temp)
    {
        for(int j = temp.getRow() + 1; j<=7; j++){
            // j equals the square right below the clicked one, and it keeps going down until it hits the bottom of the board
        if ((gameboard[j][temp.getCol()].pieceType() != 0) && (gameboard[j][temp.getCol()].pieceType() != temp.pieceType())){
            //if the spot right above the clicked one is not black AND it is the other player's color,
            continue;
            // keep going with the block statement            2,4 jj = 3,4,5,6,7
        }
        if ((gameboard[j][temp.getCol()].pieceType() != 0) && (gameboard[j][temp.getCol()].pieceType() == temp.pieceType())){
        // if the spot right above is not black AND it is the same player's color,
            for(int i = temp.getRow(); i <= j; i++)
                // i equals the row of the clicked spot, it goes until it finds J, where the other green / purple end piece is
                {
                // change the colors of all colors in the clicked color's column and rows in between the other border piece
                    gameboard[i][temp.getCol()].change(temp.pieceType());
                }
            }
        else
        {
            break;
        }
        }
    }
    public void checkFlipLeft(BlankPiece temp)
    {
        for(int j = temp.getCol() + 1; j<=7; j++){
            // j equals the square right below the clicked one, and it keeps going down until it hits the bottom of the board
        if ((gameboard[j][temp.getRow()].pieceType() != 0) && (gameboard[j][temp.getRow()].pieceType() != temp.pieceType())){
            //if the spot right above the clicked one is not black AND it is the other player's color,
            continue;
            // keep going with the block statement            2,4 jj = 3,4,5,6,7
        }
        if ((gameboard[j][temp.getRow()].pieceType() != 0) && (gameboard[j][temp.getRow()].pieceType() == temp.pieceType())){
        // if the spot right above is not black AND it is the same player's color,
            for(int i = temp.getCol(); i <= j; i++)
                // i equals the row of the clicked spot, it goes until it finds J, where the other green / purple end piece is
                {
                // change the colors of all colors in the clicked color's column and rows in between the other border piece
                    gameboard[temp.getRow()][i].change(temp.pieceType());
                }
            }
        else
        {
            break;
        }
        }
    }
    public void checkFlipRight(BlankPiece temp)
    {
        for(int j = temp.getCol() - 1; j>=0; j--){
            // j equals the square to the right of the clicked one, and it keeps going down until it hits the bottom of the board
        if ((gameboard[j][temp.getRow()].pieceType() != 0) && (gameboard[j][temp.getRow()].pieceType() != temp.pieceType())){
            //if the spot right above the clicked one is not black AND it is the other player's color,
            continue;
            // keep going with the block statement            2,4 jj = 3,4,5,6,7
        }
        if ((gameboard[j][temp.getRow()].pieceType() != 0) && (gameboard[j][temp.getRow()].pieceType() == temp.pieceType())){
        // if the spot right above is not black AND it is the same player's color,
            for(int i = temp.getCol(); i >= j; i--)
                // i equals the row of the clicked spot, it goes until it finds J, where the other green / purple end piece is
                {
                // change the colors of all colors in the clicked color's column and rows in between the other border piece
                    gameboard[temp.getRow()][i].change(temp.pieceType());
                }
            }
        else if (gameboard[j][temp.getRow()].pieceType() == 0)
        {
            break;
        }
        }
    }

So those are my four functions (one for west, east, north, and south). BlankPiece is below:

import java.awt.Color;

import javax.swing.*;


public class BlankPiece extends JButton
{
    private final ImageIcon bbutton = new ImageIcon("bbutton.png");
    private final ImageIcon gbutton = new ImageIcon("gbutton.jpg");
    private final ImageIcon pbutton = new ImageIcon("pbutton.png");
    private int x;
    private int y;

    BlankPiece(int x, int y)
    {
      this.setBackground(Color.BLACK);
      this.setIcon(bbutton);
      this.x = x;
      this.y = y;
    }
    public void setGreen()
    {
          this.setBackground(Color.BLACK);
          this.setIcon(gbutton);
    }
    public void setPurple()
    {
          this.setBackground(Color.BLACK);
          this.setIcon(pbutton);
    }
    public int getRow() {
        // TODO Auto-generated method stub
        return x;
    }
    public int getCol() {
        return y;
    }
    public void resetImage(int count)
    {
        if (count % 2 == 0)
        {
              this.setIcon(gbutton);
        }
        else
        {
            this.setIcon(pbutton);
        }
    }

    public boolean isSet()
    {

        String image = "" + this.getIcon();

        if((image.equals("pbutton.png")) || (image.equals("gbutton.jpg")))
        {
        }
        return false;
    }
    public int pieceType()
    {
        if(getIcon().equals(pbutton)) // purple
        {
            return -1;
        }
        else if(getIcon().equals(bbutton)) // blank
        {
            return 0;
        }
        else // green
        {
            return 1;
        }
    }
    public void change(int i) {
        if (i == -1)
        {
            setIcon(pbutton);
        }
        else if(i == 1)
        {
            setIcon(gbutton);
        }
        else
        {
            setIcon(bbutton);
        }

    }

}

The problem is not that any checkflip isn't called (they are all called with the same correct parameters), but something in checkFlipRight or CheckFlipLeft functions...

EDIT: full working example by request.

Make a folder with TWO files

One is called Reversi.java and the other is BlankPiece.java

REVERSI.JAVA:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Reversi extends JPanel {
    BlankPiece [][] gameboard = new BlankPiece[8][8];
    JPanel toparea = new JPanel();
    JPanel gamearea = new JPanel();
    JPanel greenarea = new JPanel();
    JPanel purplearea = new JPanel();
    JPanel logo = new JPanel();
    JLabel logoarea = new JLabel();
    JLabel greenlogo = new JLabel();
    JLabel purplelogo = new JLabel();
    ImageIcon blackicon = new ImageIcon("bbutton.png");
    ImageIcon icon = new ImageIcon("reversilogo.png");
    ImageIcon dgreen = new ImageIcon("DarkGreen.png");
    ImageIcon lgreen = new ImageIcon("LightGreen.png");
    ImageIcon dpurple = new ImageIcon("DarkPurple.png");
    ImageIcon lpurple = new ImageIcon("LightPurple.png");
    int count = 0;
    int jplus = 0;
    public Reversi()
    {  
        gamearea.setLayout(new GridLayout(8,8));
        for (int row = 0; row < gameboard.length; row++)
        {
            for(int col = 0; col < gameboard.length; col++)
            {
                gameboard[row][col] = new BlankPiece(row, col);
                gamearea.add(gameboard[row][col]);
                gameboard[row][col].addActionListener(new ButtonListener());

            }
        }
        logoarea.setPreferredSize(new Dimension(304,73));
        toparea.setLayout(new BorderLayout());
        this.setLayout(new BorderLayout());
        toparea.setBackground(new Color(97,203,242));
        gamearea.setBackground(new Color(83,35,215));
        logo.setBackground(new Color(97,203,242));
        logoarea.setIcon(icon);
        greenlogo.setIcon(dgreen);
        purplelogo.setIcon(lpurple);
        toparea.add(greenlogo, BorderLayout.WEST);
        toparea.add(purplelogo, BorderLayout.EAST);
        toparea.add(logo, BorderLayout.CENTER);
        logo.add(logoarea);
        this.add(toparea, BorderLayout.NORTH);
        this.add(gamearea, BorderLayout.CENTER);
        gameboard[3][3].setGreen();
        gameboard[3][4].setPurple();
        gameboard[4][3].setPurple();
        gameboard[4][4].setGreen();
    }
    private class ButtonListener implements ActionListener
    {
        public void actionPerformed(ActionEvent e)
        {
            BlankPiece temp = (BlankPiece)e.getSource();  // #1
            int row = temp.getRow();  // #2
            System.out.println(row);
            int col = temp.getCol();
            System.out.println(col);

            temp.isSet();

            // col and row are the column and row of blankpiece that was clicked
            String image = "" + temp.getIcon();
            if((image.equals("pbutton.png")) || (image.equals("gbutton.jpg")))
            {
                JOptionPane.showMessageDialog(null, "This spot is already occupied by " +
                        "a piece. Please pick an empty tile.");
            }
            else 
            {
                temp.resetImage(count++);
                System.out.println("About to check");
                checkFlipDown(gameboard[row][col]);
                checkFlipUp(gameboard[row][col]);
                checkFlipLeft(gameboard[row][col]);
                checkFlipRight(gameboard[row][col]);
            }
            if (count % 2 == 0)
            {
                greenlogo.setIcon(dgreen);
                purplelogo.setIcon(lpurple);
            }
            else
            {
                greenlogo.setIcon(lgreen);
                purplelogo.setIcon(dpurple);
            }
        } 
    } 
    public void checkFlipDown(BlankPiece temp)
    {
        for(int j = temp.getRow() - 1; j>=0; j--){
            // j equals the square right above the clicked one, and it keeps going up until it hits the top of the board
        if ((gameboard[j][temp.getCol()].pieceType() != 0) && (gameboard[j][temp.getCol()].pieceType() != temp.pieceType())){
            //if the spot right above the clicked one is not black AND it is the other player's color,
            continue;
            // keep going with the block statement
        }
        if ((gameboard[j][temp.getCol()].pieceType() != 0) && (gameboard[j][temp.getCol()].pieceType() == temp.pieceType())){
        // if the spot right above is not black AND it is the same player's color,
            for(int i = temp.getRow(); i >= j; i--)
                // i equals the row of the clicked spot, it goes until it finds J, where the other green / purple end piece is
                {
                // change the colors of all colors in the clicked color's column and rows in between the other border piece
                    gameboard[i][temp.getCol()].change(temp.pieceType());
                }
            }
        else
        {
            // if no other border pieces exist, go out of the loop
            break;
        }
        }
    }
    public void checkFlipUp(BlankPiece temp)
    {
        for(int j = temp.getRow() + 1; j<=7; j++){
            // j equals the square right below the clicked one, and it keeps going down until it hits the bottom of the board
        if ((gameboard[j][temp.getCol()].pieceType() != 0) && (gameboard[j][temp.getCol()].pieceType() != temp.pieceType())){
            //if the spot right above the clicked one is not black AND it is the other player's color,
            continue;
            // keep going with the block statement            2,4 jj = 3,4,5,6,7
        }
        if ((gameboard[j][temp.getCol()].pieceType() != 0) && (gameboard[j][temp.getCol()].pieceType() == temp.pieceType())){
        // if the spot right above is not black AND it is the same player's color,
            for(int i = temp.getRow(); i <= j; i++)
                // i equals the row of the clicked spot, it goes until it finds J, where the other green / purple end piece is
                {
                // change the colors of all colors in the clicked color's column and rows in between the other border piece
                    gameboard[i][temp.getCol()].change(temp.pieceType());
                }
            }
        else
        {
            break;
        }
        }
    }
    public void checkFlipLeft(BlankPiece temp)
    {
        for(int j = temp.getCol() + 1; j<=7; j++){
            // j equals the square right below the clicked one, and it keeps going down until it hits the bottom of the board
        if ((gameboard[j][temp.getRow()].pieceType() != 0) && (gameboard[j][temp.getRow()].pieceType() != temp.pieceType())){
            //if the spot right above the clicked one is not black AND it is the other player's color,
            continue;
            // keep going with the block statement            2,4 jj = 3,4,5,6,7
        }
        if ((gameboard[j][temp.getRow()].pieceType() != 0) && (gameboard[j][temp.getRow()].pieceType() == temp.pieceType())){
        // if the spot right above is not black AND it is the same player's color,
            for(int i = temp.getCol(); i <= j; i++)
                // i equals the row of the clicked spot, it goes until it finds J, where the other green / purple end piece is
                {
                // change the colors of all colors in the clicked color's column and rows in between the other border piece
                    gameboard[temp.getRow()][i].change(temp.pieceType());
                }
            }
        else
        {
            break;
        }
        }
    }
    public void checkFlipRight(BlankPiece temp)
    {
        for(int j = temp.getCol() - 1; j>=0; j--){
            // j equals the square to the right of the clicked one, and it keeps going down until it hits the bottom of the board
        if ((gameboard[j][temp.getRow()].pieceType() != 0) && (gameboard[j][temp.getRow()].pieceType() != temp.pieceType())){
            //if the spot right above the clicked one is not black AND it is the other player's color,
            continue;
            // keep going with the block statement            2,4 jj = 3,4,5,6,7
        }
        if ((gameboard[j][temp.getRow()].pieceType() != 0) && (gameboard[j][temp.getRow()].pieceType() == temp.pieceType())){
        // if the spot right above is not black AND it is the same player's color,
            for(int i = temp.getCol(); i >= j; i--)
                // i equals the row of the clicked spot, it goes until it finds J, where the other green / purple end piece is
                {
                // change the colors of all colors in the clicked color's column and rows in between the other border piece
                    gameboard[temp.getRow()][i].change(temp.pieceType());
                }
            }
        else if (gameboard[j][temp.getRow()].pieceType() == 0)
        {
            break;
        }
        }
    }

}

BLANKPIECE.JAVA

import java.awt.Color;

import javax.swing.*;


public class BlankPiece extends JButton
{
    private final ImageIcon bbutton = new ImageIcon("bbutton.png");
    private final ImageIcon gbutton = new ImageIcon("gbutton.jpg");
    private final ImageIcon pbutton = new ImageIcon("pbutton.png");
    private int x;
    private int y;

    BlankPiece(int x, int y)
    {
      this.setBackground(Color.BLACK);
      this.setIcon(bbutton);
      this.x = x;
      this.y = y;
    }
    public void setGreen()
    {
          this.setBackground(Color.BLACK);
          this.setIcon(gbutton);
    }
    public void setPurple()
    {
          this.setBackground(Color.BLACK);
          this.setIcon(pbutton);
    }
    public int getRow() {
        // TODO Auto-generated method stub
        return x;
    }
    public int getCol() {
        return y;
    }
    public void resetImage(int count)
    {
        if (count % 2 == 0)
        {
              this.setIcon(gbutton);
        }
        else
        {
            this.setIcon(pbutton);
        }
    }

    public boolean isSet()
    {

        String image = "" + this.getIcon();

        if((image.equals("pbutton.png")) || (image.equals("gbutton.jpg")))
        {
        }
        return false;
    }
    public int pieceType()
    {
        if(getIcon().equals(pbutton)) // purple
        {
            return -1;
        }
        else if(getIcon().equals(bbutton)) // blank
        {
            return 0;
        }
        else // green
        {
            return 1;
        }
    }
    public void change(int i) {
        if (i == -1)
        {
            setIcon(pbutton);
        }
        else if(i == 1)
        {
            setIcon(gbutton);
        }
        else
        {
            setIcon(bbutton);
        }

    }

}

Solution

  • Unfortunately I wasn't able to run your code since it uses images and without them it is impossible to tell what part is working or not. However I have a suggestion for a different approach which I think it would help you to solve the problem (please pay attention to the ideas described and not necessarily to the code).

    Problem description: (as I understand it) When a player puts a piece of one color on the board (there are two colors in the game, one for each player), the system needs to traverse all blocks along all directions (there are 8 of them) until it encounters one of the following:

    The above description implies that the problem can be divided further to smaller problems that we need to solve first.

    Problems to solve:

    Your approach tries to solve all the above problems together. This makes it complicated. Furthermore the same work is repeated for different directions which does not need to be the case. As per the above description, no special action is required for a specific direction. If we solve the problem for a single direction then it should be solved for all.

    Suggested approach: Below you can find the algorithm to use (pseudo-code) to find all the pieces to capture. Note how the piece itself tries to solve the problem and propagates the work to the next piece until all captured pieces are found. This way you don't solve the problem yourself. You just describe it correctly to your objects. I have put comments inline to explain what each class is supposed to be. I hope it is clear and useful to you.

    // Convenient class to hold together a row and a column pair
    public class Index { ... }
    
    // Checks if an index falls out of board edges
    public static boolean isValid(Index index) {...}
    
    // Holds the array of pieces. Has method to get a piece by index
    public class Board { ... } 
    
    public enum ColorType { BLANK, WHITE, BLACK; } // the types of a Piece    
    
    // The possible directions to traverse
    public enum Direction {
        UP_LEFT, UP, UP_RIGHT, RIGHT, BOTTOM_RIGHT, BOTTOM, BOTTOM_LEFT, LEFT;
    
        // given an index it returns the next index along the same direction
        public Index next(Index index) {
            switch (this) {
            case UP_LEFT:
                return new Index(index.row() - 1, index.column() - 1);
            case BOTTOM:
                return new Index(index.row() + 1, index.column());
            ... // similar for the rest of cases
            }
        }
    }
    
    public class Piece {
    
        private Board board;
        private ColorType color = ColorType.BLANK;
        private Index index;
    
        ....
    
        // Should be called privately when the piece is put on the board and from BLANK becomes WHITE or BLACK
        private void checkCaptures() {
            Direction[] directions = Direction.values();
    
            for (Direction direction : directions) {
                // get next piece's index along the direction
                Index nextIndex = direction.next(this.index);
    
                if ( isValid(nextIndex) ) { // if the index is not valid (i.e. edge of the board) ignore it
    
                    // get next piece in the same direction
                    Piece piece = board.getPiece(nextIndex);
    
                    // find all pieces that should be captured in this direction 
                    List<Piece> candidatesToCapture = new ArrayList<Piece>();
                    piece.findCaptureCandidates(candidatesToCapture, this.color, direction);
    
                    for (Piece candidate : candidatesToCapture) {
                        // flip the color (WHITE to BLACK and vice-versa)
                        candidate.capture(); 
                    }
                } 
            }
        }
    
        private void findCaptureCandidates(List<Piece> captured, ColorType firstColor, Direction d) {
    
            Index next = d.next(this.index);
    
            if (this.color == firstColor) {
                // This piece has the same color with the first one.
                // No need to search further for this direction. All pieces collected in the list
                // between the first one and this one, have opposite color and should be captured.
    
            } else if (this.color == ColorType.BLANK) {
                // found a blank piece. Stop the search and clear any captured pieces found so far
                captured.clear();
            } else {
                // this piece has the opposite color of the first
                if ( isValid(next) ) {
                    // this is not the last piece in this direction. 
                    // Since it has a flipped color it is a candidate for capturing
                    captured.add(this);
    
                    // ask the next piece recursively to also check itself
                    Piece piece = board.getPiece(next);
                    piece.findCaptureCandidates(captured, firstColor, d);
                } else {
                    // next index is not valid i.e. we have reached board edge. 
                    // Stop the search and clear any captured pieces found so far
                    captured.clear();
                }
            } 
        }
    }