// AP(r) Computer Science Marine Biology Simulation:
// The SquareEnvironment class is copyright(c) 2002 College Entrance
// Examination Board (www.collegeboard.com).
//
// This class is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation.
//
// This class is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

import java.util.ArrayList;
import java.util.Random;

/**
 *  AP® Computer Science Marine Biology Simulation:
* SquareEnvironment is an abstract class that implements * only the navigational methods in the Environment interface. * It considers the cells in the environment to be square, with sides * to the north, south, east, and west, and navigates accordingly. * *

* The SquareEnvironment class is * copyright© 2002 College Entrance Examination Board * (www.collegeboard.com). * * @author Alyce Brady * @author Chris Nevison * @author Julie Zelenski * @version 1 July 2002 * @see Direction * @see Location **/ public abstract class SquareEnvironment implements Environment { private static final int NUM_SIDES_IN_SQUARE = 4; // Instance Variables: Encapsulated data for EACH square environment // If includeDiagonals is true, cells have 8 neighbors (neighbors on // 4 sides plus 4 diagonals); if false, cells have only 4 neighbors. private boolean includeDiagonals; // constructors /** Constructs a SquareEnvironment object in which cells * have four adjacent neighbors -- those with which they share sides. * These neighbors are in the four cardinal directions. **/ public SquareEnvironment() { includeDiagonals = false; } /** Constructs a SquareEnvironment object in which cells * have four or eight adjacent neighbors, depending on the value * of the includeDiagonalNeighbors parameter. If * includeDiagonalNeighbors is true, cells * have eight adjacent neighbors -- the immediately adjacent neighbors * on all four sides and the four neighbors on the diagonals. * If includeDiagonalNeighbors is false, * cells have only the four neighbors they would have in an environment * created with the default SquareEnvironment constructor. * @param includedDiagonalNeighbors whether to include the four * diagonal locations as neighbors **/ public SquareEnvironment(boolean includeDiagonalNeighbors) { includeDiagonals = includeDiagonalNeighbors; } // accessor methods for navigating around this environment /** Returns the number of sides around each cell. * @return the number of cell sides in this environment **/ public int numCellSides() { return NUM_SIDES_IN_SQUARE; } /** Returns the number of adjacent neighbors around each cell. * @return the number of adjacent neighbors **/ public int numAdjacentNeighbors() { return (includeDiagonals ? NUM_SIDES_IN_SQUARE*2 : NUM_SIDES_IN_SQUARE); } /** Generates a random direction. The direction returned by * randomDirection reflects the direction from * a cell in the environment to one of its adjacent neighbors. * @return a direction **/ public Direction randomDirection() { Random randNumGen = new Random(); int randNum = randNumGen.nextInt(numAdjacentNeighbors()); return new Direction(randNum * Direction.FULL_CIRCLE/numAdjacentNeighbors()); } /** Returns the direction from one location to another. If * fromLoc and toLoc are the same, * getDirection arbitrarily returns Direction.NORTH. * @param fromLoc starting location for search * @param toLoc destination location * @return direction from fromLoc to toLoc **/ public Direction getDirection(Location fromLoc, Location toLoc) { if (fromLoc.equals(toLoc)) return Direction.NORTH; int rowDifference = fromLoc.row() - toLoc.row(); // our coord system is upside down int colDifference = toLoc.col() - fromLoc.col(); double inRads = Math.atan2(rowDifference, colDifference); double angle = 90 - Math.toDegrees(inRads); // convert to our sweep, North is 0 Direction d = new Direction((int)angle); return d.roundedDir(numAdjacentNeighbors(), Direction.NORTH); } /** Returns the adjacent neighbor (whether valid or invalid) of a location * in the specified direction. * @param fromLoc starting location for search * @param compassDir direction in which to look for adjacent neighbor * @return neighbor of fromLoc in given direction * (whether valid or not) **/ public Location getNeighbor(Location fromLoc, Direction compassDir) { Direction roundedDir = compassDir.roundedDir(numAdjacentNeighbors(), Direction.NORTH); // Calculate neighboring location using sines and cosines. // First have to adjust because our 0 is North, not East. // The row change is the opposite of what is expected because // our row numbers increase as they go down, not up. int adjustedDegrees = 90 - roundedDir.inDegrees(); double inRads = Math.toRadians(adjustedDegrees); int colDelta = (int)(Math.cos(inRads) * Math.sqrt(2)); int rowDelta = -(int)(Math.sin(inRads) * Math.sqrt(2)); return new Location(fromLoc.row() + rowDelta, fromLoc.col() + colDelta); } /** Returns the adjacent neighbors of a specified location. * Only neighbors that are valid locations in the environment will be * included. * @param ofLoc location whose neighbors to get * @return a list of locations that are neighbors of ofLoc **/ public ArrayList neighborsOf(Location ofLoc) { ArrayList nbrs = new ArrayList(); Direction d = Direction.NORTH; for (int i = 0; i < numAdjacentNeighbors(); i++) { Location neighbor = getNeighbor(ofLoc, d); if ( isValid(neighbor) ) nbrs.add(neighbor); d = d.toRight(Direction.FULL_CIRCLE/numAdjacentNeighbors()); } return nbrs; } }