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;
}
}
You can get the 8 adjacent cells using two different arrays. These arrays are used to get row and column numbers of 8 neighbors of a given cell
int rowNbr[] = new int[] {-1, -1, -1, 0, 0, 1, 1, 1};
int colNbr[] = new int[] {-1, 0, 1, -1, 1, -1, 0, 1};
And iterate the given matrix by adding the current row/col to above arrays like :
for (int k = 0; k < 8; ++k) {
sop(matrix[row + rowNbr[k], col + colNbr[k]])
}
Also,you might want to check https://www.geeksforgeeks.org/find-number-of-islands/
Hope this will get you the further thought process. Thnx
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 { }
}
Related
I am stuck on my Flood It game. I am trying to solve the problem of getting a starting matrix and finding the same color connected to the point (0,0). For instance if my starting matrix is generated:
1 4 5
1 1 1
5 3 2
It should capture the 1's so that on the next turn lets say I choose 4 then the matrix should be:
4 4 5
4 4 4
5 3 2
However when I choose 4 now the matrix is:
4 4 5
1 1 1
5 3 2
I know this is part of any normal Flood It game but I'm stuck on implementing it.
'
import java.awt.event.*;
import javax.swing.*;
public class GameController implements ActionListener {
private GameModel model;
private GameView view;
private MyStack<DotInfo> dots;
private int size;
private DotInfo dot;
/**
* Constructor used for initializing the controller. It creates the game's view
* and the game's model instances
*
* #param size
* the size of the board on which the game will be played
*/
public GameController(int size) {
this.size = size;
model = new GameModel(size);
view = new GameView(model, this);
dots = new MyStack<DotInfo>(size*size);
/*view.reset.addActionListener(this);
view.quit.addActionListener(this);
view.grey.addActionListener(this);
view.yellow.addActionListener(this);
view.blue.addActionListener(this);
view.green.addActionListener(this);
view.violet.addActionListener(this);
view.red.addActionListener(this);*/
}
/**
* resets the game
*/
public void reset(){
model.reset();
System.out.println(model);
}
/**
* Callback used when the user clicks a button (reset or quit)
*
* #param e
* the ActionEvent
*/
public void actionPerformed(ActionEvent e) {
if (e.getSource() instanceof JButton) {
JButton x = (JButton) e.getSource();
if (x.getText()=="Quit") { //Quit button
System.exit(0);
}
else if (x.getText()=="Reset") { //Reset button
reset();
view.update();
}
}
}
/**
* <b>selectColor</b> is the method called when the user selects a new color.
* If that color is not the currently selected one, then it applies the logic
* of the game to capture possible locations. It then checks if the game
* is finished, and if so, congratulates the player, showing the number of
* moves, and gives two options: start a new game, or exit
* #param color
* the newly selected color
*/
public void selectColor(int color){
model.setCurrentSelectedColor(color);
sendCapturedToStack();
equalityCheck(color);
System.out.println(model);
}
/**
* On every turn <b>sendCapturedToStack</b> will push every captured dot to the stack.
* It will also allow the colours to change every time a user picks a color.
*/
private void sendCapturedToStack() {
model.capture
for (int j=0;j<size;j++) {
for (int i=0;i<size;i++) {
if (model.isCaptured(i, j)) {
model.capture(i, j);
dots.push(model.get(i,j));
}
}
}
}
/**
* <b>equalityCheck</b> checks to see if there is a dot to the left, right, above, or below
* another dot. If there is then the dot will be captured and pushed to the top of the stack.
* #param newColor
* the newly selected color
*/
private void equalityCheck(int newColor) {
while (!dots.isEmpty()) {
dot = dots.pop();
int x = dot.getX();
int y = dot.getY();
if (y<size-1 && model.getColor(x,y+1)==newColor && !model.isCaptured(x,y+1)) {
model.capture(x, y+1);
dots.push(model.get(x,y+1));
} if (x<size-1 && model.getColor(x+1,y)==newColor && !model.isCaptured(x+1,y)) {
model.capture(x+1, y);
dots.push(model.get(x+1,y));
} if (y>0 && model.getColor(x,y-1)==newColor && !model.isCaptured(x,y-1)) {
model.capture(x, y-1);
dots.push(model.get(x,y-1));
} if (x>0 && model.getColor(x-1,y)==newColor && !model.isCaptured(x-1,y)) {
model.capture(x-1, y);
dots.push(model.get(x-1,y));
}
}
}
}
I'm getting wrong output. I'll put the 3 codes here, they are pretty basic as I've just started to study. (The third code is the tester)
The problem: in the tester I'm getting that input (0,0,0), but I need to get (50,40,30)
I think the main problem is here:
public void setColor (RGBColor color)
{
}
I tried playing with it, and I don't have a better solution than:
_color = new RGBColor(color);
_color.setRed(color.getRed());
_color.setGreen(color.getGreen());
_color.setBlue(color.getBlue());
RGBColor class.
/**
* This program is used to represent 3 Colors: Red, Green, Blue. (RGB)
* These colors hold values between 0 and 255.
*
*
* #Author doesn't matter ;)
*/
public class RGBColor
{
/**
* attributes: red, green and blue component of a color.
*/
private int _red,_green,_blue;
/**
* final variables.
*/
private final int MAX_VALUE = 255,MIN_VALUE = 0;
private final double THIRTY_PERCENT = 0.3,FIFTY_NINE_PERCENT = 0.59,ELEVEN_PERCENT = 0.11;
/**
* Consctructor which gets 3 colors (RGB), we check here if their range is valid (0 - 255), if not we assign black to it.
*
* #param red - The red color component value.
* #param green - The green color component value.
* #param blue - The blue color component value
*/
public RGBColor(int red, int green, int blue)
{
if(isValid(red,green,blue))
{
_red = red;
_green = green;
_blue = blue;
}
else
doBlack();
}
/**
* Construct a black RGBColor. i.e. red = green = blue = 0
*/
public RGBColor()
{
doBlack();
}
/**
* Construct a new RGBColor which is a copy of the given color.
*
* #param other - The RGBColor to copy.
*/
public RGBColor(RGBColor other)
{
_red = other._red;
_green = other._green;
_blue = other._blue;
}
/**
* Returns the Red component of RGBColor.
*
* #return The red color component value of this RGBColor.
*/
public int getRed()
{
return _red;
}
/**
* Returns the Green component of RGBColor.
*
* #return The green color component value of this RGBColor.
*/
public int getGreen()
{
return _green;
}
/**
* Returns the blue component of RGBColor.
*
* #return The blue color component value of this RGBColor.
*/
public int getBlue()
{
return _blue;
}
/**
* Sets the red color component value of this RGBColor, only if the color range is valid (0-255).
*
* #param num - The red color component value to set.
*/
public void setRed(int num)
{
if(isValid(num))
_red = num;
}
/**
* Sets the green color component value of this RGBColor, only if the color range is valid (0-255).
*
* #param num - The green color component value to set.
*/
public void setGreen(int num)
{
if(isValid(num))
_green = num;
}
/**
* Sets the blue color component value of this RGBColor, only if the color range is valid (0-255).
*
* #param num - The blue color component value to set.
*/
public void setBlue(int num)
{
if(isValid(num))
_blue = num;
}
/**
* Compares the 3 RGB colors, returns true if all are equal.
*
* #return true if the RGBColors are equal; false otherwise.
*/
public boolean equals(RGBColor other)
{
return ((_red == other._red) &&
(_green == other._green) &&
(_blue == other._blue));
}
/**
* Changes this color to be a mix of this and other RGBColors, It simply takes this color and other RGBColor and makes and average of them.
* For example (255,0,0) and (0,0,255) becomes: (127,0,127). Note that it returns integer numbers and not fraction numbers.
*
* #param other is the other color.
*/
public void mix(RGBColor other)
{
_red = (_red + other._red) / 2;
_green = (_green + other._green) / 2;
_blue = (_blue + other._blue) / 2;
}
/**
* Returns the grayscale value of this RGBColor.
* Grayscale is defined by taking Red multipled by 30% plus green multiplied by 59% plus blue multipled by 11%.
*
* #return The grayscale value of this RGBColor, a double number.
*/
public double convertToGrayscale()
{
return ((THIRTY_PERCENT * _red) + (FIFTY_NINE_PERCENT * _green) + (ELEVEN_PERCENT * _blue));
}
/**
* Inverts the color of RGBColor, every spot is reduced relative to 255. For example: (10,20,30) becomes (245,235,225).
*/
public void invert()
{
_red = (MAX_VALUE - _red);
_green = (MAX_VALUE - _green);
_blue = (MAX_VALUE - _blue);
}
/**
* Here we check if the color number was entered correctly.
* It has to be an integer (whole number) between 0-255.
*
* #param nums - a component value, should be the number between 1-4
* #param return - return true if the number is between 1-4, false otherwise.
*/
private boolean isValid(int nums)
{
return ((nums >= MIN_VALUE) && (nums <= MAX_VALUE));
}
/**
* Here we check if the color number was entered correctly.
* It has to be an integer (whole number) between 0-255.
*
* #param red - the red component
* #param green - the green component
* #param blue - the red component
* #param return true if values are correct, false otherwise.
*/
private boolean isValid(int red, int green, int blue)
{
return ((red <= MAX_VALUE && red >= MIN_VALUE &&
green <= MAX_VALUE && green >= MIN_VALUE &&
blue <= MAX_VALUE && blue >= MIN_VALUE));
}
/**
* Returns RGB color string triplet with numbers between 0-255, i.e. (0,127,127)
*/
public String toString()
{
return ("(" + _red + "," + _green + "," + _blue + ")");
}
/**
* RGBColor will become the color Black. (0,0,0)
*/
private void doBlack()
{
_red = _green = _blue = 0;
}
}
LightBulb Class.
/**
* In this program we use _color to represent the color of the bulb
* And we use _switchedOn to represent whether the bulb is turned on or off.
*
* #author
* #date 20/11/2014.
*/
public class LightBulb
{
/**
instance private variables of the lightbulb's color and the switch state.
*/
private static RGBColor _color;
private boolean _switchedOn;
private int ZERO = 0;
private int MAX = 255;
/**
* Construct a new LightBulb with the given color component values.
* Check if atleast one of the color isn't in the given value range (0-255)
* if not in the range, the default color is black (0,0,0).
*/
public LightBulb(int red, int green, int blue)
{
if(isTrue(red,green,blue))
_color = new RGBColor(red,green,blue);
else
_color = new RGBColor(ZERO,ZERO,ZERO);
_switchedOn = false;
}
/**
* Construct a new LightBulb which is a copy of the given bulb, and turn switch off.
*/
public LightBulb(RGBColor color)
{
_color = new RGBColor(color);
_switchedOn = false;
}
/**
* Construct a new LightBulb which is a copy of the given LightBulb
*/
public LightBulb (LightBulb other)
{
_color = new RGBColor(other._color);
_switchedOn = other._switchedOn;
}
//Return the current color of the bulb.
public RGBColor getColor()
{
return new RGBColor(_color);
}
//Sets the color of the bulb.
public void setColor (RGBColor color)
{
_color = new RGBColor(color);
}
//Checks if the bulb is on, if it is - returns true. else, returns false.
public boolean isSwitchedOn()
{
if(_switchedOn)
return true;
else
return false;
}
//If the lightbulb is turned on, it turns it off. else,if it is off - it turns it on.
public void switchLight()
{
if(_switchedOn)
_switchedOn = false;
else
_switchedOn = true;
}
//Returns a string which prints a triplet of the color in numbers of range (0-255), and the switch state of the bulb.
// e.g. (255,255,0) On , e.g. (255,127,0) Off
public String toString()
{
_color = new RGBColor();
return ("(" +_color.getRed()+ "," +_color.getGreen()+ "," + _color.getBlue() + ")" + " " + switchState());
}
//If the switch is on - returns a string "On". else, if it is off returns a string "Off".
public String switchState()
{
if(_switchedOn)
return ("On");
else
return ("Off");
}
public boolean isTrue(int red, int green, int blue)
{
if( red >= ZERO && red <= MAX &&
green >= ZERO && green <= MAX &&
blue >= ZERO && blue <= MAX )
return true;
else
return false;
}
}
LightBulb tester class.
/**
* Write a description of class Tester here.
*
* #author
* #version (a version number or a date)
*/
public class LightBulbTester
{
/**
*
*/
public static void main(String[] args)
{
// Create two light bulb objects
LightBulb l1 = new LightBulb(127,0,127);
LightBulb l2 = new LightBulb(new RGBColor(127,0,127));
LightBulb l3 = new LightBulb(l2);
// Print (test the get method)
System.out.println("Welcome to Light Bulb tester");
System.out.println("1) color of light object is " + l1.getColor());
// Test the set method
l1.setColor(new RGBColor(50,40,30));
System.out.println("2) The new color of light is :" + l1);
// Test isSwitchedOn
System.out.println("3) Light object is switched on? " + l1.isSwitchedOn());
// Now switch on
l1.switchLight();
System.out.println("4) Light after switchLight():" + l1);
System.out.println("Good luck!");
}
}
In this code -
public String toString()
{
_color = new RGBColor();
return ("(" +_color.getRed()+ "," +_color.getGreen()+ "," + _color.getBlue() + ")" + " " + switchState());
}
you're always outputting the value of a new RGBColor object, which will always give you black. You probably don't want to set _color's value here; just remove that line (or replace it with a null check).
Also, remove the static qualifier from the _color property. You don't want to share the value between all bulbs!
I had been through this link1 link2 for an answer, but was not helpful.
Because
frame.setVisible(false);
is giving below exception,
Exception in thread "main" java.lang.NullPointerException
at Simulation.drawOcean(Simulation.java:72)
at Simulation.main(Simulation.java:191)
Application has below 2 java files.
Simulation class launches GUI.
After running Simulation.java with default command line parameters, Frame does not get close, despite finite while loop.
I could not close the Frame by clicking close button(top right - red), after the simulation is done.
Do i need to set some property for Frame?
Please help me!!!
/* Simulation.java */
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.util.Random;
/* DO NOT CHANGE THIS FILE (except as noted). */
/* (You may wish to make temporary changes or insert println() statements) */
/* while testing your code. When you're finished testing and debugging, */
/* though, make sure your code works with the original version of this file */
/**
* The Simulation class is a program that runs and animates a simulation of
* Sharks and Fish.
*
* The Simulation program takes up to four parameters. The first two specify
* the width and height of the ocean. The third parameter specifies the value
* of starveTIme. For example, if you run
*
* java Simulation 25 25 1
*
* then Simulation will animate a 25x25 ocean with a starveTime of 1. If you
* run "java Simulation" with no parameters, by default Simulation will animate
* a 50x25 ocean with a starveTime of 3. With some choices of parameters,
* the ocean quickly dies out; with others;, it teems forever.
*
* #author mohet01
*
*/
public class Simulation {
/**
* The constant cellSize determines the size of each cell on the screen
* during animation. (You may change this if you wish).
*/
private static final int cellSize = 4;
/**
* Default parameters. (You may change this of you wish).
*/
private static int i = 50; //Default ocean width
private static int j = 25; //Default ocean height
private static int starveTime = 3; //Default shark starvation time
/**
* drawOcean() adds cell contents as part of graphics
*/
private static void drawOcean(Graphics graphics, Ocean ocean){
if(ocean != null){
int width = ocean.width();
int height = ocean.height();
for(int row = 0; row < height; row++){
for(int col = 0; col < width; col++){
int contents = ocean.cellContents(row, col);
if(contents == Ocean.SHARK){
//Draw a red Shark
graphics.setColor(Color.red);
graphics.fillRect(row*cellSize, col*cellSize, cellSize, cellSize);
}else if(contents == Ocean.FISH){
// Draw a green fish
graphics.setColor(Color.green);
graphics.fillRect(row * cellSize, col * cellSize, cellSize, cellSize);
}else{
//Clear the rectangle
graphics.clearRect(row, col, cellSize, cellSize);
}
}
}
}
}
/**
* main() reads the parameters and performs the simulation and animation.
* #param args
* #throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Ocean sea;
/**
* Read the input parameters
*/
if(args.length >0){
try{
i = Integer.parseInt(args[0]);
}catch(NumberFormatException e){
System.out.println("First argument to Simulation is not a number.");
}
}
if(args.length > 1){
try{
j = Integer.parseInt(args[1]);
}catch(NumberFormatException e){
System.out.println("Second argument to Simulation is not a number");
}
}
if(args.length > 2){
try{
starveTime = Integer.parseInt(args[2]);
}catch(NumberFormatException e){
System.out.println("Third argument to Simulation is not a number");
}
}
/**
* Create a window on your screen
*/
Frame frame = new Frame("Sharks and Fish");
frame.setSize(i*cellSize + 10, j*cellSize + 30);
frame.setVisible(true);
/**
* Create a "Canvas" we can draw upon; attach it to the window
*/
Canvas canvas = new Canvas();
canvas.setBackground(Color.white);
canvas.setSize(i*cellSize, j*cellSize);
frame.add(canvas);
Graphics graphics = canvas.getGraphics();
/**
* Create the initial ocean.
*/
sea = new Ocean(i, j, starveTime);
/**
* Visit each cell (in a roundabout order); randomnly place a fish, shark,
* or nothing in each.
*/
Random random = new Random(0);
int x = 0;
int y = 0;
for(int row = 0;row < j; row++){
//This will visit every x-coordinate once.
x = (x + 78887) %j;
if((x & 8) == 0){
for(int col = 0; col < i; col++){
//This will visit every y coordinate once.
y = (y+78887)%i;
if((y & 8) == 0){
int r = random.nextInt();
if(r < 0){
//50% of cells start with fish
//x - width, y - height
sea.addFish(x, y);
}else if(r > 1500000000){
//~15% of cells start with sharks
sea.addShark(x, y);
}
}
}
}
}
/**
* Perform timesteps forever
*/
int timeLeft = 20;
while (timeLeft > 0) {
// Wait one second (1000 milliseconds)
Thread.sleep(1000);
// Draw the current ocean
drawOcean(graphics, sea);
// For fun, you might wish to change the delay in the next line.
// If you make it too short, though, the graphics won't work properly.
// Simulate a timestep
sea = sea.timeStep();
timeLeft--;
}
}
}
/* Ocean.java */
/**
* The Ocean class defines an object that models an ocean full of sharks and
* fish. Descriptions of the methods you must implements appear below. They
* include a constructor of the form
*
* public Ocean(int i, int j, int starveTime);
*
* that creates an empty ocean having width i and height j, in which sharks
* starve after starveTime timesteps.
*
* See the README file accompanying this project for additional details.
*
* #author mohet01
*
*/
public class Ocean {
/**
* Do not rename these constants. WARNING: if you change the numbers, you
* will need to recompile Test4.java. Failure to do so will give you a very
* hard-to-find bug.
*/
public final static int EMPTY = 1;
public final static int SHARK = 2;
public final static int FISH = 3;
/**
* Define any variables associated with an Ocean object here. These
* variables MUST be private.
*
*/
private final static int UNKNOWN = -1; // for unknown return type
private int width;
private int height;
private int[][] oceanMatrix;
//TODO space optimization on below matrix
private int[][] sharkHungerLevelMatrix;
private int starveTime;
/**
* The following methods are required for Part I.
*
*/
/**
* Constructor that creates an empty ocean having width i and
* height j, in which sharks starve until after starveTime timesteps.
*
* #param width(i)
* is the width of the ocean.
* #param height(j)
* is the height of the ocean.
* #param starveTime
* is the number of timeSteps sharks survive without food.
*/
public Ocean(int i, int j, int starveTime) {
this.width = i;
this.height = j;
this.oceanMatrix = new int[j][i];
this.sharkHungerLevelMatrix = new int[j][i];
this.starveTime = starveTime;
for (int row = 0; row < j; row++) {
for (int col = 0; col < i; col++) {
oceanMatrix[row][col] = EMPTY;
}
}
for (int row = 0; row < j; row++) {
for (int col = 0; col < i; col++) {
sharkHungerLevelMatrix[row][col] = EMPTY;
}
}
}
/**
* width() returns the width of an ocean Object.
*
* #return the width of the ocean.
*
*/
public int width() {
return this.width;
}
/**
* height() returns the height of an Ocean object.
*
* #return the height of the Ocean.
*/
public int height() {
return this.height;
}
/**
* starveTime() returns the number of timesteps sharks survive without food.
*
* #return the number of timesteps sharks survive without food.
*/
public int starveTime() {
return starveTime;
}
/**
* addFish() places a fish in cell (x,y) if the cell is empty. If the cell
* is already occupied, leave the cell as it is.
*
* #param x
* is the x-coordinate of the cell to place a fish in.
* #param y
* is the y-coordinate of the cell to place a fish in.
*/
public void addFish(int x, int y) {
if (oceanMatrix[x][y] == EMPTY) {
oceanMatrix[x][y] = FISH;
}
}
/**
* addShark() (with two parameters) places a newborn shark in cell (x, y) if
* the cell is empty. A "newborn" shark is equivalent to a shark that has
* just eaten. If the cell is already occupied, leave the cell as it is.
*
* #param x
* is the x-coordinate of the cell to place a shark in.
* #param y
* is the y-coordinate of the cell to place a shark in.
*/
public void addShark(int x, int y) {
if (oceanMatrix[x][y] == EMPTY) {
oceanMatrix[x][y] = SHARK;
}
}
/**
* cellContents() returns EMPTY is cell (x,y) is empty, FISH if it contains
* a fish, and SHARK if it contains a shark.
*
* #param x
* is the x-coordinate of the cell whose contents are queried.
* #param y
* is the y-coordinate of the cell whose contents are queried.
*/
public int cellContents(int x, int y) {
return oceanMatrix[x][y];
}
/**
* isFish() checks for the existence of fish in that cell.
* #param x
* is the x-coordinate of the cell whose contents are queried.
* #param y
* is the y-coordinate of the cell whose contents are queried.
* #return the boolean value
*/
private boolean isFish(int x, int y){
return (this.oceanMatrix[x][y] == Ocean.FISH);
}
/**
* isShark() checks for the existence of shark in that cell.
* #param x
* is the x-coordinate of the cell whose contents are queried.
* #param y
* is the y-coordinate of the cell whose contents are queried.
* #return the boolean value
*/
private boolean isShark(int x, int y){
return (this.oceanMatrix[x][y] == Ocean.SHARK);
}
/**
* isSharkStarving() checks the hunger level of shark, if reached to starveTime level
* #param x
* is the x-coordinate of the cell whose contents are queried.
* #param y
* is the y-coordinate of the cell whose contents are queried.
* #return the boolean value
*/
private boolean isSharkStarving(int x, int y){
return (this.sharkHungerLevelMatrix[x][y] == (this.starveTime+1));
}
/**
* checkFish() checks the existence of atleast one fish
* surrounding shark cell
* #param x
* is the x-coordinate of the cell whose contents are queried.
* #param y
* is the y-coordinate of the cell whose contents are queried.
* #return returns true on atleast one fish exist otherwise false
*
*/
private boolean checkFish(int x, int y){
for(int i = x-1;i <= x+1; i++){
for(int j = y-1; j <= y+1; j++){
if(this.isFish(mod(i,this.height), mod(j,this.width))){
return true;
}
}
}
return false;
}
/**
* countShark() counts the number of sharks surrounding queried cell
* #param x
* is the x-coordinate of the cell whose contents are queried.
* #param y
* is the y-coordinate of the cell whose contents are queried.
* #return returns number of sharks surrounding fish cell
*/
private int countShark(int x, int y){
int neighbourSharkCount = 0;
for(int i = x-1;i <= x+1; i++){
for(int j = y-1; j <= y+1; j++){
if(this.isShark(mod(i,this.height), mod(j,this.width))){
neighbourSharkCount++;
}
} // end inner for loop
}//end outer for loop
return neighbourSharkCount;
}
/**
* countFish() counts the number of fish surrounding queried cell
* #param x
* is the x-coordinate of the cell whose contents are queried.
* #param y
* is the y-coordinate of the cell whose contents are queried.
* #return returns number of sharks surrounding queried cell
*/
private int countFish(int x, int y){
int neighbourFishCount = 0;
for(int i = x-1;i <= x+1; i++){
for(int j = y-1; j <= y+1; j++){
if(this.isFish(mod(i,this.height), mod(j,this.width))){
neighbourFishCount++;
}
} // end inner for loop
}//end outer for loop
return neighbourFishCount;
}
/**
* mod() performs the modulo operation using euclidean divison
*
* #param n
* is the numerator
* #param d
* is the denominator
* #return the remainder
*/
private int mod(int n, int d) {
if (n >= 0)
return n % d;
else
return d + ~(~n % d);
}
/**
* timeStep() performs a simulation timestep as described in README.
*
* #return an ocean representing the elapse of one timestep.
*/
public Ocean timeStep() {
Ocean sea = new Ocean(width, height, starveTime);
for (int row = 0; row < this.height; row++) {
for (int col = 0; col < this.width; col++) {
switch(this.oceanMatrix[row][col]){
case Ocean.SHARK:
boolean gotTheFish = false;
//Check all the 8 neighbors of a Shark Cell for fish
if(this.checkFish(row,col)){
gotTheFish = true;
}
//Updating Shark Cell
if(gotTheFish){
/*
* 1) If a cell contains a shark, and any of its neighbors is a fish, then the
* shark eats during the time step, and it remains in the cell at the end of the
* time step. (We may have multiple sharks sharing the same fish. This is fine;
* they all get enough to eat.)
*/
sea.oceanMatrix[row][col] = Ocean.SHARK; // for next time step
}else{
/*
* 2) If a cell contains a shark, and none of its neighbors is a fish, it gets
* hungrier during the time step. If this time step is the (starveTime + 1)th
* time step the shark has gone through without eating, then the shark dies
* (disappears). Otherwise, it remains in the cell.
*/
this.sharkHungerLevelMatrix[row][col]++;
if(this.isSharkStarving(row,col)){
this.oceanMatrix[row][col] = Ocean.EMPTY; // for this time step
this.sharkHungerLevelMatrix[row][col] = Ocean.EMPTY; // for this time step
}
sea.sharkHungerLevelMatrix[row][col] = this.sharkHungerLevelMatrix[row][col]; // for next time step
sea.oceanMatrix[row][col] = this.oceanMatrix[row][col]; // for next time step
}
break;
case Ocean.FISH:
int neighbourSharkCount=0;
//Check all the 8 neighbors of a Fish cell to count for sharks
neighbourSharkCount=countShark(row,col);
//Updating fish cell for current & next time step
if(neighbourSharkCount ==1){
/*
* 4) If a cell contains a fish, and one of its neighbors is a shark, then the
* fish is eaten by a shark, and therefore disappears.
*/
this.oceanMatrix[row][col] = Ocean.EMPTY; //fish disappears this time step
}
else if(neighbourSharkCount > 1){
/*
* 5) If a cell contains a fish, and two or more of its neighbors are sharks, then
* a new shark is born in that cell. Sharks are well-fed at birth; _after_ they
* are born, they can survive an additional starveTime time steps without eating.
*/
sea.oceanMatrix[row][col] = Ocean.SHARK; // new shark for next time step
}
else if(neighbourSharkCount < 1){
/*
* 3) If a cell contains a fish, and all of its neighbors are either empty or are
* other fish, then the fish stays where it is.
*/
sea.oceanMatrix[row][col] = FISH; //for next time step
}
break;
case Ocean.EMPTY:
int fishCount=0;
int sharkCount=0;
//Check all the 8 neighbors of an Empty cell to count sharks and Fish
fishCount = this.countFish(row,col);
sharkCount = this.countShark(row, col);
//Update Empty Cell for current & next time step.
/* (no need to handle this case)
* 6) If a cell is empty, and fewer than two of its neighbors are fish, then the
* cell remains empty.
*/
if((fishCount >= 2) && (sharkCount <=1)){
/*
* 7) If a cell is empty, at least two of its neighbors are fish, and at most one
* of its neighbors is a shark, then a new fish is born in that cell.
*/
this.oceanMatrix[row][col] = FISH;// for current time step
sea.oceanMatrix[row][col] = FISH; //for next time step
}else if((fishCount >= 2) && (sharkCount >= 2)){
/*
* 8) If a cell is empty, at least two of its neighbors are fish, and at least two
* of its neighbors are sharks, then a new shark is born in that cell. (The new
* shark is well-fed at birth, even though it hasn’t eaten a fish yet.)
*/
sea.oceanMatrix[row][col] = Ocean.SHARK; // for next time step
}
break;
}
}//end inner for loop
}//end outer for loop
return sea;
}
/**
* The following method is required for Part II.
*
*
*/
/**
* addShark() (with three parameters) places a shark in cell (x, y) if the
* cell is empty. The shark's hunger is represented by the third parameter.
* If the cell is already occupied, leave the cell as it is, You will need
* this method to help convert run-length encodings to Oceans.
*
* #param x
* is the x-coordinate of the cell to place a shark in.
* #param y
* is the y-coordinate of the cell to place a shark in.
* #param feeding
* is an integer that indicates the shark's hunger. You may
* encode it any way you want; for instance, "feeding" may be the
* last timestep the shark was fed, or the amount of time that
* has passed since the shark was last fed, or the amount of time
* left before the shark will starve. It's upto you, but be
* consistent.
*/
public void addShark(int x, int y, int feeding) {
this.oceanMatrix[x][y] = Ocean.SHARK;
this.sharkHungerLevelMatrix[x][y] = feeding;
}
/**
* The following method is required for Part III.
*/
/**
* sharkFeeding() returns an integer that indicates the hunger of the shark
* in cell (x, y), using the same "feeding" representation as the parameter
* to addShark() described above. If cell (x, y) does not contain a shark,
* then its return value is undefined--that is, anything you want. Normally,
* this method should not be called if cell (x, y) does not contain a shark.
* You will need this method to help convert Oceans to run-length encodings.
*
* #param x
* is the x-coordinate of the cell whose contents are queried.
* #param y
* is the y-coordinate of the cell whose contents are queried.
*
*/
public int sharkFeeding(int x, int y) {
if(this.isShark(x, y)){
return this.sharkHungerLevelMatrix[x][y];
}
return Ocean.UNKNOWN;
}
}
While this is not a Swing program, you can substitute JFrame for Frame to leverage EXIT_ON_CLOSE.
JFrame frame = new JFrame("Sharks and Fish");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
The AWT approach is to add a WindowListener.
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Have you tried?
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
I'm working to test this chunk of code - it's a class called MazeBuilder. My problem is that most of the methods are protected, so I can't access them in the tests...
So my thought was that the test should just focus on Run(), so it accesses a lot of the other methods. But I'm concerned that it will be impossible to get any sort of cohesive testing done operating from just one method.
Additionally, what is the proper way to test the 2 constructors ( MazeBuilder() and MazeBuilder(boolean deterministic) )? As it stands, I'm just testing that the object formed is not null - i.e. that they're being built at all. Is there a more expansive way of testing a constructor that I'm unaware of?
package falstad;
public class MazeBuilder implements Runnable {
// Given input information:
protected int width, height ; // width and height of maze,
Maze maze; // reference to the maze that is constructed, results are returned by calling maze.newMaze(..)
private int rooms; // requested number of rooms in maze, a room is an area with no walls larger than a single cell
int expectedPartiters; // user given limit for partiters
// Produced output information to create the new maze
// root, cells, dists, startx, starty
protected int startx, starty ; // starting position inside maze for entity to search for exit
// conventional encoding of maze as a 2 dimensional integer array encapsulated in the Cells class
// a single integer entry can hold information on walls, borders/bounds
protected Cells cells; // the internal representation of a maze as a matrix of cells
protected Distance dists ; // distance matrix that stores how far each position is away from the exit positino
// class internal local variables
protected SingleRandom random ; // random number stream, used to make randomized decisions, e.g for direction to go
Thread buildThread; // computations are performed in own separated thread with this.run()
//int colchange; // randomly selected in run method of this thread, used as parameter to Segment class constructor
/**
* Constructor for a randomized maze generation
*/
public MazeBuilder(){
random = SingleRandom.getRandom();
}
/**
* Constructor with option to make maze generation deterministic or random
*/
public MazeBuilder(boolean deterministic){
if (true == deterministic)
{
System.out.println("Project 2: functionality to make maze generation deterministic not implemented yet! Fix this!");
// Control random number generation
// TODO: implement code that makes sure that if MazeBuilder.build is called for same skill level twice, same results
// HINT: check http://download.oracle.com/javase/6/docs/api/java/util/Random.html and file SingleRandom.java
}
random = SingleRandom.getRandom();
}
/**
* Provides the sign of a given integer number
* #param num
* #return -1 if num < 0, 0 if num == 0, 1 if num > 0
*/
static int getSign(int num) {
return (num < 0) ? -1 : (num > 0) ? 1 : 0;
}
/**
* This method generates a maze.
* It computes distances, determines a start and exit position that are as far apart as possible.
*/
protected void generate() {
// generate paths in cells such that there is one strongly connected component
// i.e. between any two cells in the maze there is a path to get from one to the other
// the search algorithm starts at some random point
generatePathways();
final int[] remote = dists.computeDistances(cells) ;
// identify cell with the greatest distance
final int[] pos = dists.getStartPosition();
startx = pos[0] ;
starty = pos[1] ;
// make exit position at true exit in the cells data structure
setExitPosition(remote[0], remote[1]);
}
/**
* This method generates pathways into the maze.
*
*/
protected void generatePathways() {
int[][] origdirs = new int[width][height] ;
int x = random.nextIntWithinInterval(0, width-1) ;
int y = 0;
final int firstx = x ;
final int firsty = y ;
int dir = 0;
int origdir = dir;
cells.setVisitedFlagToZero(x, y);
while (true) {
int dx = Constants.DIRS_X[dir];
int dy = Constants.DIRS_Y[dir];
if (!cells.canGo(x, y, dx, dy)) {
dir = (dir+1) & 3;
if (origdir == dir) {
if (x == firstx && y == firsty)
break;
int odr = origdirs[x][y];
dx = Constants.DIRS_X[odr];
dy = Constants.DIRS_Y[odr];
x -= dx;
y -= dy;
origdir = dir = random.nextIntWithinInterval(0, 3);
}
} else {
cells.deleteWall(x, y, dx, dy);
x += dx;
y += dy;
cells.setVisitedFlagToZero(x, y);
origdirs[x][y] = dir;
origdir = dir = random.nextIntWithinInterval(0, 3);
}
}
}
/**
* Establish valid exit position by breaking down wall to outside area.
* #param remotex
* #param remotey
*/
protected void setExitPosition(int remotex, int remotey) {
int bit = 0;
if (remotex == 0)
bit = Constants.CW_LEFT;
else if (remotex == width-1)
bit = Constants.CW_RIGHT;
else if (remotey == 0)
bit = Constants.CW_TOP;
else if (remotey == height-1)
bit = Constants.CW_BOT;
else
dbg("Generate 1");
cells.setBitToZero(remotex, remotey, bit);
//System.out.println("exit position set to zero: " + remotex + " " + remotey + " " + bit + ":" + cells.hasMaskedBitsFalse(remotex, remotey, bit)
// + ", Corner case: " + ((0 == remotex && 0 == remotey) || (0 == remotex && height-1 == remotey) || (width-1 == remotex && 0 == remotey) || (width-1 == remotex && height-1 == remotey)));
}
static final int MIN_ROOM_DIMENSION = 3 ;
static final int MAX_ROOM_DIMENSION = 8 ;
/**
* Allocates space for a room of random dimensions in the maze.
* The position of the room is chosen randomly. The method is not sophisticated
* such that the attempt may fail even if the maze has ample space to accommodate
* a room of the chosen size.
* #return true if room is successfully placed, false otherwise
*/
private boolean placeRoom() {
// get width and height of random size that are not too large
// if too large return as a failed attempt
final int rw = random.nextIntWithinInterval(MIN_ROOM_DIMENSION, MAX_ROOM_DIMENSION);
if (rw >= width-4)
return false;
final int rh = random.nextIntWithinInterval(MIN_ROOM_DIMENSION, MAX_ROOM_DIMENSION);
if (rh >= height-4)
return false;
// proceed for a given width and height
// obtain a random position (rx,ry) such that room is located on as a rectangle with (rx,ry) and (rxl,ryl) as corner points
// upper bound is chosen such that width and height of room fits maze area.
final int rx = random.nextIntWithinInterval(1, width-rw-1);
final int ry = random.nextIntWithinInterval(1, height-rh-1);
final int rxl = rx+rw-1;
final int ryl = ry+rh-1;
// check all cells in this area if they already belong to a room
// if this is the case, return false for a failed attempt
if (cells.areaOverlapsWithRoom(rx, ry, rxl, ryl))
return false ;
// since the area is available, mark it for this room and remove all walls
// from this on it is clear that we can place the room on the maze
cells.markAreaAsRoom(rw, rh, rx, ry, rxl, ryl);
return true;
}
static void dbg(String str) {
System.out.println("MazeBuilder: "+str);
}
/**
* Fill the given maze object with a newly computed maze according to parameter settings
* #param mz maze to be filled
* #param w width of requested maze
* #param h height of requested maze
* #param roomct number of rooms
* #param pc number of expected partiters
*/
public void build(Maze mz, int w, int h, int roomct, int pc) {
init(mz, w, h, roomct, pc);
buildThread = new Thread(this);
buildThread.start();
}
/**
* Initialize internal attributes, method is called by build() when input parameters are provided
* #param mz maze to be filled
* #param w width of requested maze
* #param h height of requested maze
* #param roomct number of rooms
* #param pc number of expected partiters
*/
private void init(Maze mz, int w, int h, int roomct, int pc) {
// store parameters
maze = mz;
width = w;
height = h;
rooms = roomct;
expectedPartiters = pc;
// initialize data structures
cells = new Cells(w,h) ;
dists = new Distance(w,h) ;
//colchange = random.nextIntWithinInterval(0, 255); // used in the constructor for Segments class Seg
}
static final long SLEEP_INTERVAL = 100 ; //unit is millisecond
/**
* Main method to run construction of a new maze with a MazeBuilder in a thread of its own.
* This method is called internally by the build method when it sets up and starts a new thread for this object.
*/
public void run() {
// try-catch block to recognize if thread is interrupted
try {
// create an initial invalid maze where all walls and borders are up
cells.initialize();
// place rooms in maze
generateRooms();
Thread.sleep(SLEEP_INTERVAL) ; // test if thread has been interrupted, i.e. notified to stop
// put pathways into the maze, determine its starting and end position and calculate distances
generate();
Thread.sleep(SLEEP_INTERVAL) ; // test if thread has been interrupted, i.e. notified to stop
final int colchange = random.nextIntWithinInterval(0, 255); // used in the constructor for Segments class Seg
final BSPBuilder b = new BSPBuilder(maze, dists, cells, width, height, colchange, expectedPartiters) ;
BSPNode root = b.generateBSPNodes();
Thread.sleep(SLEEP_INTERVAL) ; // test if thread has been interrupted, i.e. notified to stop
// dbg("partiters = "+partiters);
// communicate results back to maze object
maze.newMaze(root, cells, dists, startx, starty);
}
catch (InterruptedException ex) {
// necessary to catch exception to avoid escalation
// exception mechanism basically used to exit method in a controlled way
// no need to clean up internal data structures
// dbg("Catching signal to stop") ;
}
}
static final int MAX_TRIES = 250 ;
/**
* Generate all rooms in a given maze where initially all walls are up. Rooms are placed randomly and of random sizes
* such that the maze can turn out to be too small to accommodate the requested number of rooms (class attribute rooms).
* In that case less rooms are produced.
* #return generated number of rooms
*/
private int generateRooms() {
// Rooms are randomly positioned such that it may be impossible to place the all rooms if the maze is too small
// to prevent an infinite loop we limit the number of failed to MAX_TRIES == 250
int tries = 0 ;
int result = 0 ;
while (tries < MAX_TRIES && result <= rooms) {
if (placeRoom())
result++ ;
else
tries++ ;
}
return result ;
}
/**
* Notify the maze builder thread to stop the creation of a maze and to terminate
*/
public void interrupt() {
buildThread.interrupt() ;
}
}
To unit test your protected methods, simply put your test class in the same package as the class you are looking to test (in this case falsted). Just because they are in the same package, it doesn't mean they have to be in the same directory (just a parallel test directory hierarchy).
For example, if you are using maven, your source would be in src/main/java/falsted and your tests would be in src/test/java/falsted. From a maven perspective, these are separate directories and therefore can easily be managed separately, while from a Java perspective, they are the same package (so protected methods are visible).
Test your constructors by probing the state of the object to ensure that all values got their default or initial value.
You can use protected in test method. Recommended way to structure your project is.
In src/main/java
package falstad;
public class MazeBuilder {}
In src/test/java
package falstad;
public class MazeBuilderTest {}
I am writing a small game where 20 balloons are created on screen and mouse released on them expands them. They are supposed to 'pop' when one balloon touches another, but at present when I click a balloon it pops a random one and throws an 'Array index out of bounds' exception. I've racked my brain to figure out why my code isn't working but just can't get it. Here's some of the code causing the problem:
import comp102.*;
import java.util.*;
import java.awt.Color;
public class BalloonGame implements UIButtonListener, UIMouseListener{
// Fields
private final int numBalloons = 20;
private int currentScore; // the score for the current game
private int highScore = 0; // highest score in all games so far.
private int totalPopped = 0;
Balloon balloons[] = new Balloon[numBalloons];
// Constructor
/** Set up the GUI, start a new game.
*/
public BalloonGame(){
UI.setMouseListener(this);
UI.addButton("New Game", this);
UI.addButton("Lock Score", this);
this.newGame();
}
// GUI Methods to respond to buttons and mouse
/** Respond to button presses, to start a new game and to end the current game */
public void buttonPerformed(String cmd){
if (cmd.equals("New Game")) { this.newGame(); }
else if (cmd.equals("Lock Score")) { this.endGame(); }
}
/** Respond to mouse released with the main action of the game*/
public void mousePerformed(String action, double x, double y) {
if (action.equals("released")) {
this.doAction(x, y);
}
}
/** Start the game:
Clear the graphics pane
Initialise the score information
Make a new set of Balloons at random positions
*/
public void newGame(){
UI.clearGraphics();
this.currentScore = 0;
this.totalPopped = 0;
for (int i = 0; i < this.balloons.length; i++) {
this.balloons[i] = new Balloon(50 + Math.random()*400, 50 + Math.random()*400);
this.balloons[i].draw();
}
UI.printMessage("New game: click on a balloon. High score = "+this.highScore);
}
/** Main game action.
Find the balloon at (x,y) if any,
Expand it
Check whether it is touching another balloon,
If so, update totalPopped, pop both balloons, and remove them from the list
Recalculate the score.
If there are no balloons left, end the game.
*/
public void doAction(double x, double y) {
for (int i = 0; i < this.balloons.length; i++) {
if (this.balloons[i].on(x, y) && !this.balloons[i].isPopped()) {
this.balloons[i].expand();
}
for (int j = 1; j <this.balloons.length; j++) {
if (this.balloons[i].isTouching(this.balloons[j]) && this.balloons[j] != null)
{
this.totalPopped +=2;
this.balloons[i].pop();
this.balloons[j].pop();
this.balloons[i] = null;
this.balloons[j] = null;
}
}
}
this.calculateScore();
if (totalPopped == numBalloons) {
this.endGame();
}
}
/** Find a balloon that the point (x, y) is on.
* Returns null if point is not on any balloon*/
public Balloon findBalloon(double x, double y){
return null;
}
/** Find and return another balloon that is touching this balloon
* Returns null if no such Balloon. */
public Balloon findTouching(Balloon balloon){
return null;
}
/** Calculate the score: sum of the sizes of current ballons, minus
the total of the popped balloons (totalPopped).
Report the score as a message */
public void calculateScore(){
for (Balloon b: balloons) {
this.currentScore += b.size();
}
if (currentScore >= highScore) {
this.highScore = this.currentScore;
}
UI.printMessage("Score = "+this.currentScore+" High score = "+this.highScore);
}
/** Returns true if all the balloons have been popped,
* Returns false if any of the balloons is not popped */
public boolean allPopped(){
for (Balloon b : this.balloons){
if (!b.isPopped()){
return false;
}
}
return true;
}
/** End the current game.
Record the the score as the new high score if it is better
Print a message
Clear the list of balloons (so the player can't keep playing)
*/
public void endGame(){
this.highScore = this.currentScore;
UI.println("High score = " + this.highScore);
Arrays.fill(balloons, null);
}
// Main
public static void main(String[] arguments){
BalloonGame ob = new BalloonGame();
}
}
uses the balloon class also:
import comp102.*;
import java.util.*;
import java.awt.Color;
import java.io.*;
/** Represents a balloon that can grow until it pops.
A Balloon can say whether a particular point is on it, and
whether it is touching another balloon.
It can also return its size.
Once it has popped, no point is on it, and it can't touch another balloon.
Also, its size is reported as a negative value.
*/
public class Balloon{
// Fields
private double radius = 10;
private double centerX, centerY;
private Color color;
private boolean popped = false;
// Constructors
/** Construct a new Balloon object.
Parameters are the coordinates of the center of the balloon
Does NOT draw the balloon yet.
*/
public Balloon(double x, double y){
this.centerX = x;
this.centerY = y;
this.color = Color.getHSBColor((float)Math.random(), 1.0f, 1.0f);
}
public void draw(){
UI.setColor(color);
UI.fillOval(centerX-radius, centerY-radius, radius*2, radius*2);
if (!this.popped){
UI.setColor(Color.black);
UI.drawOval(centerX-radius, centerY-radius, radius*2, radius*2);
}
}
/** Make the balloon larger by a random amount between 4 and 10*/
public void expand(){
if (! this.popped){
this.radius = this.radius + (Math.random()*6 + 4);
this.draw();
}
}
/** pop the balloon (changes colour to gray, draws, and pauses briefly)*/
public void pop(){
this.color = Color.lightGray;
this.popped = true;
this.draw();
UI.sleep(20);
}
/** Returns true if the balloon has been popped */
public boolean isPopped(){
return this.popped;
}
/** Returns true if the point (x,y) is on the balloon, and false otherwise */
public boolean on(double x, double y){
if (popped) return false;
double dx = this.centerX - x;
double dy = this.centerY - y;
return ((dx*dx + dy*dy) < (this.radius * this.radius));
}
/** Returns true if this Balloon is touching the other balloon, and false otherwise
* Returns false if either balloon is popped. */
public boolean isTouching(Balloon other){
if (this.popped || other.popped) return false;
double dx = other.centerX - this.centerX;
double dy = other.centerY - this.centerY;
double dist = other.radius + this.radius;
return (Math.hypot(dx,dy) < dist);
}
/** Calculates and returns the area of the balloon
* Returns it in "centi-pixels" (ie, number of pixels/100)
* to keep them in a reasonable range.
* Returns a negative size if it is popped.*/
public int size(){
int s = (int) ((this.radius * this.radius * Math.PI)/100);
if (popped) { s = 0 - s; }
return s;
}
}
is this right?
for (int j = 1; j <this.balloons.length; j++) {
doesn't it allow i and j to be equal, so you end up asking whether a balloon is touching itself? Do you mean j = i + 1 ?
If you are now getting null pointer exceptions, get into a debugger and step through until you see where. My guess is that you are visiting array items that have been popped, and hence are null.
if (this.balloons[i].isTouching(this.balloons[j]) && this.balloons[j] != null)
You are testing this.balloons[j] for null after you are using it. I'd put some null checks before trying to work with each item.