HexapawnBoard class in Java - java

I have to complete 4 methods but I am having difficulty understanding them. I'm trying to complete whitemove(). I know that I have to iterate through the board, but I cannot totally understand how I'm supposed to do it!
import java.util.List;
import java.util.ArrayList;
public class HexapawnBoard {
private static final int WHITE_PIECE = 1;
private static final int BLACK_PIECE = 2;
private static final int EMPTY = 0;
private static final int BOARD_SIZE = 3;
private int[][] board;
/**
* Create a board position from a string of length BOARD_SIZE*BOARD_SIZE (9).
* The first BOARD_SIZE positions in the string correspond to the black player's home
* position. The last BOARD_SIZE positions in the string correspond ot the white player's
* home position. So, the string "BBB WWW" corresponds to a starting position.
*
* #param pos the string encoding the position of the board
*/
public HexapawnBoard(String pos)
{
if(pos.length() != BOARD_SIZE * BOARD_SIZE)
throw new RuntimeException("HexapawnBoard string must be of length BOARD_SIZExBOARD_SIZE");
board = new int[BOARD_SIZE][BOARD_SIZE];
for(int row=0;row<BOARD_SIZE;row++)
{
for(int col=0;col<BOARD_SIZE;col++)
{
switch(pos.charAt(row*BOARD_SIZE+col))
{
case 'B':
case 'b':
board[row][col] = BLACK_PIECE;
break;
case 'W':
case 'w':
board[row][col] = WHITE_PIECE;
break;
case ' ':
board[row][col] = EMPTY;
break;
default:
throw new RuntimeException("Invalid Hexapawn board pattern " + pos);
}
}
}
}
/**
* A copy constructor of HexapawnBoard
*
* #param other the other instance of HexapawnBoard to copy
*/
public HexapawnBoard(HexapawnBoard other)
{
board = new int[BOARD_SIZE][BOARD_SIZE];
for(int i=0;i<BOARD_SIZE;i++)
for(int j=0;j<BOARD_SIZE;j++)
this.board[i][j] = other.board[i][j];
}
/**
* Return a string version of the board. This uses the same format as the
* constructor (above)
*
* #return a string representation of the board.
*/
public String toString()
{
StringBuffer sb = new StringBuffer();
for(int row=0;row<BOARD_SIZE;row++)
{
for(int col=0;col<BOARD_SIZE;col++)
{
switch(board[row][col])
{
case BLACK_PIECE:
sb.append('B');
break;
case WHITE_PIECE:
sb.append('W');
break;
case EMPTY:
sb.append(' ');
break;
}
}
}
return sb.toString();
}
/**
* Determine if this board position corresponds to a winning state.
*
* #return true iff black has won.
*/
public boolean blackWins()
{
for(int col=0;col<BOARD_SIZE;col++)
if(board[BOARD_SIZE-1][col] == BLACK_PIECE)
return true;
return false;
}
/**
* Determine if this board position corresponds to a winning state
*
* #return true iff white has won
*/
public boolean whiteWins()
{
for(int col=0;col<BOARD_SIZE;col++)
if(board[0][col] == WHITE_PIECE)
return true;
return false;
}
/**
* Determine if black has a move
*
* # return truee iff black has a move
*/
public boolean blackCanMove()
{
return false;
}
/**
* Return a List of valid moves of white. Moves are represented as valid
* Hexapawn board positions. If white cannot move then the list is empty.
*
* #return A list of possible next moves for white
*/
public List<HexapawnBoard> whiteMoves()
{
return null
}
/**
* Determine if two board positions are equal
* #param other the other board position
* #return true iff they are equivalent board positions
*/
public boolean equals(HexapawnBoard other)
{
return true;
}
/**
* Determine if two board positions are reflections of each other
* #param other the other board position
* #return true iff they are equivalent board positions
*/
public boolean equalsReflection(HexapawnBoard other)
{
return true;
}
}

First of all, the code that you have so far seems to be correct.
Now, for the four methods that you have left, I can't really help you with the blackCanMove() or the whiteMoves() methods because I don't know what the rules for moving are in this hexapawn game. If you would explain the rules, I could help with that.
As far as the equals() and equalsReflection() methods go, you basically need to do what you did in the copy constructor, but instead of copying, do a conditional test. If two positions do not match up to what they are supposed to be, return false. Otherwise, once you have checked each square and they are all correct, return true.
As far as the whiteMoves() method, you need to iterate through the board and find all of the white pieces. For each white piece, there may be a few (at most 3) valid moves. Check each of those positions to see if they are possible. The piece may move diagonally if there is a black piece in that spot, or it may move forward if there is no piece at all in that spot. For each valid move, create a new HexapawnBoard representing what the board would look life following that move. At the end of the method, return a list of all of the HexapawnBoards that you created.

Related

How would I properly implement a JScrollPane GUI into my Java game?

I've been trying for a few days to implement this "basic" GUI into my game of Tic-Tac-Toe. The outline of this code requires me to set up a basic JFrame containing a JTextArea, inside a JScrollPane GUI. All of this is in class TicTacToeFrame, which extends class TicTacToe (holds all the methods/constructor for the board, including a method that prints the board to the console). TicTacToeFrame needs to override the print() method, so the board, which is a string, will be printed to the GUI instead of the console.
Note, I'm required to keep:
public class TicTacToeFrame extends TicTacToe
They don't allow us extend from JFrame like:
public class TicTacToeFrame extends JFrame
What I have is shown below:
TicTacToe class (Works)
import java.util.*;
/**
* A class modelling a tic-tac-toe (noughts and crosses, Xs and Os) game.
*
* #author Supasta
* #version November 20, 2019
*/
public class TicTacToe
{
public static final String PLAYER_X = "X"; // player using "X"
public static final String PLAYER_O = "O"; // player using "O"
public static final String EMPTY = " "; // empty cell
public static final String TIE = "T"; // game ended in a tie
private String player; // current player (PLAYER_X or PLAYER_O)
private String winner; // winner: PLAYER_X, PLAYER_O, TIE, EMPTY = in progress
private int numFreeSquares; // number of squares still free
private String board[][]; // 3x3 array representing the board
private TicTacToeFrame gui;
/**
* Constructs a new Tic-Tac-Toe board.
*/
public TicTacToe()
{
board = new String[3][3];
}
/**
* Sets everything up for a new game. Marks all squares in the Tic Tac Toe board as empty,
* and indicates no winner yet, 9 free squares and the current player is player X.
*/
private void clearBoard()
{
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
board[i][j] = EMPTY;
}
}
winner = EMPTY;
numFreeSquares = 9;
player = PLAYER_X; // Player X always has the first turn.
}
/**
* Plays one game of Tic Tac Toe.
*/
public void playGame()
{
int row, col;
Scanner sc;
clearBoard(); // clear the board
gui = new TicTacToeFrame();
// print starting board
gui.print();
// loop until the game ends
while (winner==EMPTY) { // game still in progress
// get input (row and column)
while (true) { // repeat until valid input
System.out.print("Enter row and column of chosen square (0, 1, 2 for each): ");
sc = new Scanner(System.in);
row = sc.nextInt();
col = sc.nextInt();
if (row>=0 && row<=2 && col>=0 && col<=2 && board[row][col]==EMPTY) break;
System.out.println("Invalid selection, try again.");
}
board[row][col] = player; // fill in the square with player
numFreeSquares--; // decrement number of free squares
// see if the game is over
if (haveWinner(row,col))
winner = player; // must be the player who just went
else if (numFreeSquares==0)
winner = TIE; // board is full so it's a tie
// print current board
print();
// change to other player (this won't do anything if game has ended)
if (player==PLAYER_X)
player=PLAYER_O;
else
player=PLAYER_X;
}
}
/**
* Returns true if filling the given square gives us a winner, and false
* otherwise.
*
* #param int row of square just set
* #param int col of square just set
*
* #return true if we have a winner, false otherwise
*/
private boolean haveWinner(int row, int col)
{
// unless at least 5 squares have been filled, we don't need to go any further
// (the earliest we can have a winner is after player X's 3rd move).
if (numFreeSquares>4) return false;
// Note: We don't need to check all rows, columns, and diagonals, only those
// that contain the latest filled square. We know that we have a winner
// if all 3 squares are the same, as they can't all be blank (as the latest
// filled square is one of them).
// check row "row"
if ( board[row][0].equals(board[row][1]) &&
board[row][0].equals(board[row][2]) ) return true;
// check column "col"
if ( board[0][col].equals(board[1][col]) &&
board[0][col].equals(board[2][col]) ) return true;
// if row=col check one diagonal
if (row==col)
if ( board[0][0].equals(board[1][1]) &&
board[0][0].equals(board[2][2]) ) return true;
// if row=2-col check other diagonal
if (row==2-col)
if ( board[0][2].equals(board[1][1]) &&
board[0][2].equals(board[2][0]) ) return true;
// no winner yet
return false;
}
/**
* Prints the board to standard out using toString().
*/
public void print()
{
System.out.println(toString());
}
/**
* Returns a string representing the current state of the game. This should look like
* a regular tic tac toe board, and be followed by a message if the game is over that says
* who won (or indicates a tie).
*
* #return String representing the tic tac toe game state
*/
public String toString()
{
String currentState = "";
String progress = "";
for(int i=0 ; i < 3; ++i){
currentState += board[i][0] + " | " + board[i][1] + " | " + board[i][2] + "\n";
if(i == 2){
break;
}
currentState += "------------\n";
}
/* Prints the winner, only if there is a winner */
if(winner != EMPTY){
if(this.winner == TIE){
progress = ("The games ends in a tie!");
}
else{
progress = ("Game over, " + player + " wins!");
}
}
else{
progress = "Game in progress";
}
return currentState + "\n" + progress + "\n";
}
}
TicTacToeFrame class (broken)
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* A class modelling a tic-tac-toe (noughts and crosses, Xs and Os) game in a very
* simple GUI window.
*
* #author Supasta
* #version November 20, 2019
*/
public class TicTacToeFrame extends TicTacToe
{
private JTextArea status; // text area to print game status
private JFrame frame;
private JScrollPane sta;
/**
* Constructs a new Tic-Tac-Toe board and sets up the basic
* JFrame containing a JTextArea in a JScrollPane GUI.
*/
public TicTacToeFrame()
{
super();
final JFrame frame = new JFrame("Tic Tac Toe");
frame.setSize(500,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
status = new JTextArea(super.toString());
JScrollPane sta = new JScrollPane();
sta.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
sta.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
frame.getContentPane().add(sta);
frame.setVisible(true);
}
/**
* Prints the board to the GUI using toString().
*/
public void print()
{
status.replaceSelection(toString());
}
}
What should I change in my TicTacToeFrame? Right now the GUI won't even appear.. And during testing, if I got it to appear, it wouldn't print any text.
I think the real issue here is that you are new to swing GUI. You are trying to just apply your frame to your existing code and hoping to see it - but everything has to be added explicitly.
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Test {
/**
* Do this for thread safety
* #param args
*/
public static void main (String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createGUI();
}
});
}
/**
* create the JFrame
*/
private static void createGUI() {
JFrame jf = new JFrame();
addComponents(jf.getContentPane());
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
}
/**
* add the components
* ALL YOUR NEW TIC TAC TOE RELATED JPANELS, JTEXTFIELDS, ETC. WILL GO HERE!
* #param pane
*/
private static void addComponents(Container pane) {
pane.setLayout(new FlowLayout());
JTextArea jta = new JTextArea("some text");
JScrollPane jsp = new JScrollPane(jta);
jsp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
pane.add(jsp);
}
}
To help you get started, here is a very basic program that will show you how I added a scrollable JTextArea. I would start from scratch, and add in your functionality from tic-tac-toe piece by piece. Adding items one at a time.
How do I get X to appear larger? Where I want it? When I want it?
These are all questions I expect you to have - and I recommend doing some research into the different layout managers of JPanels. You will undoubtedly find that getting swing to do what you want it to do is complicated as a beginner.

Blue J Word Collider Part 2

Earlier i asked a question specifically for the animate method in my word collider program. The purpose of this program is that when two words collide the words will explode and each individual character will be scattered randomly on the screen. Below is the code for my program;
/**
* Write a description of class WordCollider here.
*
* #author
* #version
*/
public class WordCollider
{
// instance variables - replace the example below with your own
private Text word1;
private Text word2;
// the characters contained in word1
private Text[] charWord1;
// the characters contained in word2
private Text[] charWord2;
/**
* Constructor for objects of class WordCollider
*/
public WordCollider(String w1, String w2)
{
// initialise instance variables
word1 = new Text(w1);
word1.randomizePosition();
word1.changeColor("green");
word1.changeSize(48);
word1.makeVisible();
word2 = new Text(w2);
word2.randomizePosition();
word2.changeColor("orange");
word2.changeSize(48);
word2.makeVisible();
charWord1 = new Text[w1.length()];
charWord2 = new Text[w2.length()];
fillChars(charWord1, w1);
fillChars(charWord2, w2);
}
private void fillChars(Text[] a, String w) {
char[] cs = w.toCharArray();
for (int i=0; i<a.length; i++) {
a[i] = new Text(""+cs[i]);
a[i].changeSize(48);
a[i].changeColor("red");
}
}
/**
* Randomize the position of the two words repeatedly and stop
* when the bounding box of the two words overlaps.
*/
public void animate()
{
while(checkOverlap() == false){
word1.randomizePosition();
word2.randomizePosition();
}
while(checkOverlap() == true){
word1.makeInvisible();
word2.makeInvisible();
}
for(int i = 0; i < charWord1.length; i++){
word1.makeVisible();
word1.randomizePosition();
}
for(int i = 0; i < charWord2.length; i++){
word2.makeVisible();
word2.randomizePosition();
}
}
/**
* erase the words and any other characters on the display
*/
public void clearDisplay() {
word1.makeInvisible();
word2.makeInvisible();
}
/**
* check if the bounding box of the two words overlaps.
* #return true when the words overlap and false otherwise.
*/
private Boolean checkOverlap() {
if (word2.getXPosition() < word1.getXPosition() + word1.getTextWidth()){
return true;
}
if (word1.getXPosition() > word2.getXPosition() + word2.getTextWidth()){
return true;
}
if (word2.getYPosition() < word1.getYPosition() - word1.getTextHeight()){
return true;
}
if (word1.getYPosition() > word2.getYPosition() - word2.getTextHeight()){
return true;
}
if (word2.getXPosition() < word1.getXPosition() + word1.getTextWidth() && word1.getXPosition() > word2.getXPosition() + word2.getTextWidth()){
return true;
}
if (word2.getYPosition() < word1.getYPosition() - word1.getTextHeight() && word1.getYPosition() > word2.getYPosition() - word2.getTextHeight()){
return true;
}
return false;
}
}
The purpose of the animate method is if checkOverlap is false the word will be randomized on the screen whereas if checkOverlap is true then i want to make the word invisible and scatter the letters on the screen
The purpose of the clear display method is just to make the words invisible on screen.
The purpose of the checkOverlap method is to check whether or not the two words overlap, if they do overlap then the return statement will be true and if they don't overlap then it will return false
How can i go round fixing this so my program works?

Java: connect 4, bad operand types for binary operator error

I'm getting some weird bad operand errors which I cannot seem to resolve.
public class Model {
// Keep track of whether a move animation is currently playing.
// When a move animation is busy then no new moves are allowed.
private boolean moveInProgress = false;
private int[][] pieces = new int[7][7];
private boolean gameOver = false;
public void checkGameOver() {
// TODO (step 3): implement this correctly.
gameOver = true;
}
/**
* Check if a new disk can be inserted in the current column.
* #param column
* #return true if and only if a move in this column is allowed.
*/
public boolean playableMove(int column) {
// No new moves are allowed when an animation is busy.
if (getMoveInProgress()) {
return false;
}
// TODO (step 3) No moves are allowed when the game is over.
if (gameOver) {
return false;
}
// TODO: Check if this move is playable.
if (pieces[column] > 6) {
return false;
}
return true;
}
/**
* Compute the final destination row of a candidate move.
* #param column
* #return the row.
*/
public int moveDestination(int column) {
// TODO: implement this method properly.
int positie = 6 - pieces[column];
return positie;
}
/**
* Commit the insertion of a new disk in a given column.
* #param column
*/
public void playMove(int column) {
// TODO: Verify the following preconditions:
// assert (isGameOver() == false);
// assert (playableMove(column) == true);
// TODO: Update the model to reflect the new move.
// TODO (step 3): Also check for termination conditions.
// TODO (step 3): Notify subscribers about important model changes.
if (!gameOver && playableMove(column)) {
pieces[column]++;
}
}
}
The errors are at
(76,28) bad operator for binary operator '>'
first type int[]
second type int
The same error is repeated 4 times.
Can anyone help me with this
You've defined private int[][] pieces = new int[7][7];. So when you access it with pieces[column], you're left with an array, so you can't compare it with an int.
You probably meant pieces[column].length ?
If you want to do any arithmetic operations with each element of an array, you'll have to do so explicitly.
For example,
if (!gameOver && playableMove(column)) {
for (int i=0; i < pieces[column].length; i++) {
pieces[column][i]++;
}
}
About the other checks (6 - pieces[column] which is meant to result in an integer and pieces[column] > 6 which leaves it unclear if this condition is meant to be applied to all elements or at least one) I cannot help you due to unknown preconditions.

Why is my object staying null?

For some reason, I can't seem to fix this giving me a NullPointerException. I print out poly.term.degree without any errors, and then set poly.next equal to poly and then get a nullPointerException when I try to print out poly.next.term.degree which should seemingly be the same. I'm aware this is incomplete code but I think this is my main issue.
public Polynomial add(Polynomial p)
{
Polynomial newPoly = new Polynomial();
newPoly.poly = new Node(0,0,null);
Polynomial myCurr = this;
Polynomial otherCurr = p;
while(myCurr.poly != null)
{
int myDeg = myCurr.poly.term.degree;
int otherDeg = p.poly.term.degree;
float myCo = myCurr.poly.term.coeff;
float otherCo = otherCurr.poly.term.coeff;
if(myDeg == otherDeg)
{
System.out.println("degrees "+myDeg + " and "+ otherDeg+ " are equal, creating new node...");
Node n = new Node(myCo+otherCo,p.poly.term.degree, newPoly.poly.next);
System.out.println(newPoly.poly.term.degree);
newPoly.poly.next = newPoly.poly;
newPoly.poly = n;
System.out.println(newPoly.poly.next.term.degree); // Gives me a NullPointerException
}
Also, the constructors and everything for these classes is below.
package poly;
import java.io.*;
import java.util.StringTokenizer;
/**
* This class implements a term of a polynomial.
*
* #author runb-cs112
*
*/
class Term {
/**
* Coefficient of term.
*/
public float coeff;
/**
* Degree of term.
*/
public int degree;
/**
* Initializes an instance with given coefficient and degree.
*
* #param coeff Coefficient
* #param degree Degree
*/
public Term(float coeff, int degree) {
this.coeff = coeff;
this.degree = degree;
}
/* (non-Javadoc)
* #see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object other) {
return other != null &&
other instanceof Term &&
coeff == ((Term)other).coeff &&
degree == ((Term)other).degree;
}
/* (non-Javadoc)
* #see java.lang.Object#toString()
*/
public String toString() {
if (degree == 0) {
return coeff + "";
} else if (degree == 1) {
return coeff + "x";
} else {
return coeff + "x^" + degree;
}
}
}
/**
* This class implements a linked list node that contains a Term instance.
*
* #author runb-cs112
*
*/
class Node {
/**
* Term instance.
*/
Term term;
/**
* Next node in linked list.
*/
Node next;
/**
* Initializes this node with a term with given coefficient and degree,
* pointing to the given next node.
*
* #param coeff Coefficient of term
* #param degree Degree of term
* #param next Next node
*/
public Node(float coeff, int degree, Node next) {
term = new Term(coeff, degree);
this.next = next;
}
}
/**
* This class implements a polynomial.
*
* #author runb-cs112
*
*/
public class Polynomial {
/**
* Pointer to the front of the linked list that stores the polynomial.
*/
Node poly;
/**
* Initializes this polynomial to empty, i.e. there are no terms.
*
*/
public Polynomial() {
poly = null;
}
/**
* Reads a polynomial from an input stream (file or keyboard). The storage format
* of the polynomial is:
* <pre>
* <coeff> <degree>
* <coeff> <degree>
* ...
* <coeff> <degree>
* </pre>
* with the guarantee that degrees will be in descending order. For example:
* <pre>
* 4 5
* -2 3
* 2 1
* 3 0
* </pre>
* which represents the polynomial:
* <pre>
* 4*x^5 - 2*x^3 + 2*x + 3
* </pre>
*
* #param br BufferedReader from which a polynomial is to be read
* #throws IOException If there is any input error in reading the polynomial
*/
public Polynomial(BufferedReader br) throws IOException {
String line;
StringTokenizer tokenizer;
float coeff;
int degree;
poly = null;
while ((line = br.readLine()) != null) {
tokenizer = new StringTokenizer(line);
coeff = Float.parseFloat(tokenizer.nextToken());
degree = Integer.parseInt(tokenizer.nextToken());
poly = new Node(coeff, degree, poly);
}
}
I think the problem lies here:
newPoly.poly.next = newPoly.poly;
newPoly.poly = n;
At first you say, that newPoly.poly.next = newPoly.poly; so you assign the current element to the next, which is recursive. And then you say newPoly.poly = n; . So you assign a new element to newPoly. I think that the garbage collector deletes the newPoly element, because it is overwritten, so you lose the reference to the newPoly element. Which means when you access it later you get a nullpointer exception. You could fix this like this:
newPoly.poly.next = n;
//and dont forget to set the next pointer of the new elemnt to 0
n.next = NULL;
Just assign the new element to the next element.
EDIT
#hendersawn
You could sort the list. See below:
sort(Node head_p){ //do not change the head, or you will lose the beginning.
Node tmp_p;
Node curr_p = head_p;
while(curr_p != NULL){
if(curr_p.poly.term.degree < curr_p.next.poly.term.degree) //either degree is smaller or greater
{//swap
tmp_p = curr_p; //save first element
curr_p = curr_p.next; //set first element to second
//now the 2 element is the actual third element so we need
//to put the first between the second and the third
tmp_p.next = curr_p.next; //set next of first to third
curr_p.next = tmp_p; //set second element to the first that we saved before
}
curr_p = curr_p.next; //move to next element...rinse repeat
}
}
newPoly might be null
newPoly.poly might be null
newPoly.poly.next might be null
newPoly.poly.next.term might be null
or
newPoly.poly.next.term.degree might be null.
To avoid NullPointerException, you need to make sure that any member used is initialized with a proper value.
Nothing is obviously null that I can tell, however with four 'dots' (something.something.something.something) of Object Oriented Indirection, you're going to encounter this problem a lot. Usually two - three 'dots' on a single line are all you should ever do, but since that's more about the design and not the error, I digress.
The way to find this problem would be to either:
Put a breakpoint right at that line and see what the variables are there, and which one is null or
Do a System.out.println for each of the components to see which one breaks it, and work backwards from there. ex:
System.out.println(newPoly.poly.next.term.degree); // Gives me a NullPointerException
System.out.println(newPoly);
System.out.println(newPoly.poly);
System.out.println(newPoly.poly.next);
System.out.println(newPoly.poly.next.term);
because the nullPointerException would only get thrown on one of those (it can't be degree, otherwise that statement would have just printed 'null'
if I had to bet, I'd say it's probably newPoly.poly.next which is null
the lines:
newPoly.poly.next = newPoly.poly;
newPoly.poly = n;
Superficially seem like they would be the culprit of your trouble, since you're assigning the 'next' of your newPoly.poly, but then you re-assign your newPoly.poly, and lose that old reference to .next, I think.
Good luck! hope that helps.

can't find source of inadvertent loop

I started refactoring this program I was working on and hit a major road block... I have one class that acts as a nucleus, with about 6 other smaller (but still important) classes working together to run the program... I took one method [called 'populate()'] out the nucleus class and made an entirely new class with it [called 'PopulationGenerator'], but when I try to create an object of the newly created class anywhere in the nucleus class I get stuck in a never ending loop of that new class
I've never had this issue when trying to create objects before... Here's the nucleus class before refactoring:
public class Simulator
{
// Constants representing configuration information for the simulation.
// The default width for the grid.
private static final int DEFAULT_WIDTH = 120;
// The default depth of the grid.
private static final int DEFAULT_DEPTH = 80;
// The probability that a fox will be created in any given grid position.
private static final double FOX_CREATION_PROBABILITY = 0.02;
// The probability that a rabbit will be created in any given grid position.
private static final double RABBIT_CREATION_PROBABILITY = 0.08;
// List of animals in the field.
private List<Animal> animals;
// The current state of the field.
private Field field;
// The current step of the simulation.
private int step;
// A graphical view of the simulation.
private SimulatorView view;
/**
* Construct a simulation field with default size.
*/
public Simulator()
{
this(DEFAULT_DEPTH, DEFAULT_WIDTH);
}
/**
* Create a simulation field with the given size.
* #param depth Depth of the field. Must be greater than zero.
* #param width Width of the field. Must be greater than zero.
*/
public Simulator(int depth, int width)
{
if(width <= 0 || depth <= 0) {
System.out.println("The dimensions must be greater than zero.");
System.out.println("Using default values.");
depth = DEFAULT_DEPTH;
width = DEFAULT_WIDTH;
}
animals = new ArrayList<Animal>();
field = new Field(depth, width);
// Create a view of the state of each location in the field.
view = new SimulatorView(depth, width);
view.setColor(Rabbit.class, Color.orange);
view.setColor(Fox.class, Color.blue);
// Setup a valid starting point.
reset();
}
/**
* Run the simulation from its current state for a reasonably long period,
* (4000 steps).
*/
public void runLongSimulation()
{
simulate(4000);
}
/**
* Run the simulation from its current state for the given number of steps.
* Stop before the given number of steps if it ceases to be viable.
* #param numSteps The number of steps to run for.
*/
public void simulate(int numSteps)
{
for(int step = 1; step <= numSteps && view.isViable(field); step++) {
simulateOneStep();
}
}
/**
* Run the simulation from its current state for a single step.
* Iterate over the whole field updating the state of each
* fox and rabbit.
*/
public void simulateOneStep()
{
step++;
// Provide space for newborn animals.
List<Animal> newAnimals = new ArrayList<Animal>();
// Let all rabbits act.
for(Iterator<Animal> it = animals.iterator(); it.hasNext(); ) {
Animal animal = it.next();
animal.act(newAnimals);
if(! animal.isAlive()) {
it.remove();
}
}
// Add the newly born foxes and rabbits to the main lists.
animals.addAll(newAnimals);
view.showStatus(step, field);
}
/**
* Reset the simulation to a starting position.
*/
public void reset()
{
step = 0;
animals.clear();
populate();
// Show the starting state in the view.
view.showStatus(step, field);
}
/**
* Randomly populate the field with foxes and rabbits.
*/
private void populate()
{
Random rand = Randomizer.getRandom();
field.clear();
for(int row = 0; row < field.getDepth(); row++) {
for(int col = 0; col < field.getWidth(); col++) {
if(rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Fox fox = new Fox(true, field, location);
animals.add(fox);
}
else if(rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Rabbit rabbit = new Rabbit(true, field, location);
animals.add(rabbit);
}
// else leave the location empty.
}
}
}
}
EDIT:
Here's this same class AFTER refactoring ...
import java.util.Random;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.awt.Color;
/**
* A simple predator-prey simulator, based on a rectangular field
* containing rabbits and foxes.
*
* Update 10.40:
* Now *almost* decoupled from the concrete animal classes.
*
* #TWiSTED_CRYSTALS
*/
public class Simulator
{
// Constants representing configuration information for the simulation.
// The default width for the grid.
private static final int DEFAULT_WIDTH = 120;
// The default depth of the grid.
private static final int DEFAULT_DEPTH = 80;
// The current state of the field.
private Field field;
// The current step of the simulation.
private int step;
// A graphical view of the simulation.
private SimulatorView view;
//Population Generator class... coupled to fox and rabbit classes
private PopulationGenerator popGenerator;
// Lists of animals in the field. Separate lists are kept for ease of iteration.
private List<Animal> animals;
/**
* Construct a simulation field with default size.
*/
public Simulator()
{
this(DEFAULT_DEPTH, DEFAULT_WIDTH);
}
/**
* Create a simulation field with the given size.
* #param depth Depth of the field. Must be greater than zero.
* #param width Width of the field. Must be greater than zero.
*/
public Simulator(int depth, int width)
{
if(width <= 0 || depth <= 0) {
System.out.println("The dimensions must be greater than zero.");
System.out.println("Using default values.");
depth = DEFAULT_DEPTH;
width = DEFAULT_WIDTH;
}
animals = new ArrayList<Animal>();
field = new Field(depth, width);
// Create a view of the state of each location in the field.
//
// view.setColor(Rabbit.class, Color.orange); // PG
// view.setColor(Fox.class, Color.blue); // PG
// Setup a valid starting point.
reset();
}
/**
* Run the simulation from its current state for a reasonably long period,
* (4000 steps).
*/
public void runLongSimulation()
{
simulate(4000);
}
/**
* Run the simulation from its current state for the given number of steps.
* Stop before the given number of steps if it ceases to be viable.
* #param numSteps The number of steps to run for.
*/
public void simulate(int numSteps)
{
for(int step = 1; step <= numSteps && view.isViable(field); step++) {
simulateOneStep();
}
}
/**
* Run the simulation from its current state for a single step.
* Iterate over the whole field updating the state of each
* fox and rabbit.
*/
public void simulateOneStep()
{
step++;
// Provide space for animals.
List<Animal> newAnimals = new ArrayList<Animal>();
// Let all animals act.
for(Iterator<Animal> it = animals.iterator(); it.hasNext(); ) {
Animal animal = it.next();
animal.act(newAnimals);
if(! animal.isAlive()) {
it.remove();
}
}
animals.addAll(newAnimals);
}
/**
* Reset the simulation to a starting position.
*/
public void reset()
{
PopulationGenerator popGenerator = new PopulationGenerator();
step = 0;
animals.clear();
popGenerator.populate();
// Show the starting state in the view.
view.showStatus(step, field);
}
public int getStep()
{
return step;
}
}
... and the new class
import java.util.ArrayList;
import java.util.Random;
import java.util.List;
import java.awt.Color;
public class PopulationGenerator
{
// The default width for the grid.
private static final int DEFAULT_WIDTH = 120;
// The default depth of the grid.
private static final int DEFAULT_DEPTH = 80;
// The probability that a fox will be created in any given grid position.
private static final double FOX_CREATION_PROBABILITY = 0.02;
// The probability that a rabbit will be created in any given grid position.
private static final double RABBIT_CREATION_PROBABILITY = 0.08;
// Lists of animals in the field. Separate lists are kept for ease of iteration.
private List<Animal> animals;
// The current state of the field.
private Field field;
// A graphical view of the simulation.
private SimulatorView view;
/**
* Constructor
*/
public PopulationGenerator()
{
animals = new ArrayList<Animal>();
field = new Field(DEFAULT_DEPTH, DEFAULT_WIDTH);
}
/**
* Randomly populate the field with foxes and rabbits.
*/
public void populate()
{
// Create a view of the state of each location in the field.
view = new SimulatorView(DEFAULT_DEPTH, DEFAULT_WIDTH);
view.setColor(Rabbit.class, Color.orange); // PG
view.setColor(Fox.class, Color.blue); // PG
Simulator simulator = new Simulator();
Random rand = Randomizer.getRandom();
field.clear();
for(int row = 0; row < field.getDepth(); row++) {
for(int col = 0; col < field.getWidth(); col++) {
if(rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Fox fox = new Fox(true, field, location);
animals.add(fox);
}
else if(rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Rabbit rabbit = new Rabbit(true, field, location);
animals.add(rabbit);
}
// else leave the location empty.
}
}
view.showStatus(simulator.getStep(), field);
}
}
here's the Field class that the PopulationGenerator calls... I havent changed this class in any way
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
/**
* Represent a rectangular grid of field positions.
* Each position is able to store a single animal.
*
* #TWiSTED_CRYSTALS
*/
public class Field
{
// A random number generator for providing random locations.
private static final Random rand = Randomizer.getRandom();
// The depth and width of the field.
private int depth, width;
// Storage for the animals.
private Object[][] field;
/**
* Represent a field of the given dimensions.
* #param depth The depth of the field.
* #param width The width of the field.
*/
public Field(int depth, int width)
{
this.depth = depth;
this.width = width;
field = new Object[depth][width];
}
/**
* Empty the field.
*/
public void clear()
{
for(int row = 0; row < depth; row++) {
for(int col = 0; col < width; col++) {
field[row][col] = null;
}
}
}
/**
* Clear the given location.
* #param location The location to clear.
*/
public void clear(Location location)
{
field[location.getRow()][location.getCol()] = null;
}
/**
* Place an animal at the given location.
* If there is already an animal at the location it will
* be lost.
* #param animal The animal to be placed.
* #param row Row coordinate of the location.
* #param col Column coordinate of the location.
*/
public void place(Object animal, int row, int col)
{
place(animal, new Location(row, col));
}
/**
* Place an animal at the given location.
* If there is already an animal at the location it will
* be lost.
* #param animal The animal to be placed.
* #param location Where to place the animal.
*/
public void place(Object animal, Location location)
{
field[location.getRow()][location.getCol()] = animal;
}
/**
* Return the animal at the given location, if any.
* #param location Where in the field.
* #return The animal at the given location, or null if there is none.
*/
public Object getObjectAt(Location location)
{
return getObjectAt(location.getRow(), location.getCol());
}
/**
* Return the animal at the given location, if any.
* #param row The desired row.
* #param col The desired column.
* #return The animal at the given location, or null if there is none.
*/
public Object getObjectAt(int row, int col)
{
return field[row][col];
}
/**
* Generate a random location that is adjacent to the
* given location, or is the same location.
* The returned location will be within the valid bounds
* of the field.
* #param location The location from which to generate an adjacency.
* #return A valid location within the grid area.
*/
public Location randomAdjacentLocation(Location location)
{
List<Location> adjacent = adjacentLocations(location);
return adjacent.get(0);
}
/**
* Get a shuffled list of the free adjacent locations.
* #param location Get locations adjacent to this.
* #return A list of free adjacent locations.
*/
public List<Location> getFreeAdjacentLocations(Location location)
{
List<Location> free = new LinkedList<Location>();
List<Location> adjacent = adjacentLocations(location);
for(Location next : adjacent) {
if(getObjectAt(next) == null) {
free.add(next);
}
}
return free;
}
/**
* Try to find a free location that is adjacent to the
* given location. If there is none, return null.
* The returned location will be within the valid bounds
* of the field.
* #param location The location from which to generate an adjacency.
* #return A valid location within the grid area.
*/
public Location freeAdjacentLocation(Location location)
{
// The available free ones.
List<Location> free = getFreeAdjacentLocations(location);
if(free.size() > 0) {
return free.get(0);
}
else {
return null;
}
}
/**
* Return a shuffled list of locations adjacent to the given one.
* The list will not include the location itself.
* All locations will lie within the grid.
* #param location The location from which to generate adjacencies.
* #return A list of locations adjacent to that given.
*/
public List<Location> adjacentLocations(Location location)
{
assert location != null : "Null location passed to adjacentLocations";
// The list of locations to be returned.
List<Location> locations = new LinkedList<Location>();
if(location != null) {
int row = location.getRow();
int col = location.getCol();
for(int roffset = -1; roffset <= 1; roffset++) {
int nextRow = row + roffset;
if(nextRow >= 0 && nextRow < depth) {
for(int coffset = -1; coffset <= 1; coffset++) {
int nextCol = col + coffset;
// Exclude invalid locations and the original location.
if(nextCol >= 0 && nextCol < width && (roffset != 0 || coffset != 0)) {
locations.add(new Location(nextRow, nextCol));
}
}
}
}
// Shuffle the list. Several other methods rely on the list
// being in a random order.
Collections.shuffle(locations, rand);
}
return locations;
}
/**
* Return the depth of the field.
* #return The depth of the field.
*/
public int getDepth()
{
return depth;
}
/**
* Return the width of the field.
* #return The width of the field.
*/
public int getWidth()
{
return width;
}
}
your problem is not in the Field class but below it. The Simulator constructor calls reset() which creates a new PopulationGenerator object, then calls populate() on that object. The populate() method calls Simulator simulator = new Simulator(); which creates a new Simulator object which continues the cycle. Solution: don't create a new Simulator object in PopulationGenerator, but instead pass the existing simulator to PopulationGenerator through its constructor or through a setSimulator(...) method.
e.g.,
class PopulationGenerator {
// ... etc...
private Simulator simulator; // null to begin with
// pass Simulator instance into constructor.
// Probably will need to do the same with SimulatorView
public PopulationGenerator(Simulator simulator, int depth, int width) {
this.simulator = simulator; // set the instance
// ... more code etc...
}
public void populate() {
// don't re-create Simulator here but rather use the instance passed in
}
}

Categories

Resources