package comp1110.ass1;
public class WalkTheDog {
/** The location of the tree, initialised to off board */
private Location tree = new Location();
/** The location of the first cat, initialised to off board */
private Location cat1 = new Location();
/** The location of the second cat, initialised to off board */
private Location cat2 = new Location();
/**
* An array storing the current game state of the board.
* A position on the board can either be a Tree, Cat, Dog, Owner or Empty.
* Consult the enum class State.Java for more information.
* Note that the index of the array a state is stored in corresponds to the
* position on the board (and therefore Location via the toPosition() method.
*
* The required dog and owner positions are not stored in this array,
* rather than can be accessed via the requiredDogLocs and requiredOwnerLocs
* arrays detailed below.
*/
private final State[] board = new State[16];
/**
* An array of all required dog Locations on the board.
* Note that this array may be empty if there are no required dog positions.
*/
private Location[] requiredDogLocs;
/**
* An array of all required owner Locations on the board.
* Note that this array may be empty if there are no required owner
* positions.
*/
private Location[] requiredOwnerLocs;
/**
* An array of all 4 dog pieces required to complete the challenge.
* Check out the Piece.java class for more details.
*/
private final Piece[] pieces = new Piece[]{new Piece(3), new Piece(2), new Piece(1), new Piece(0)};
/**
* The objective of the game.
* Check out the Objective.java class for more details.
*/
private final Objective objective;
/**
* Construct a new WalkTheDog game given an objective.
*/
public WalkTheDog(Objective objective){
this.objective = objective;
this.initialiseBoardState();
}
/**
* Construct a game for a given level of difficulty.
* This chooses a new objective and creates a new instance of
* the game at the given level of difficulty.
*
* @param difficulty The difficulty of the game.
*/
public WalkTheDog(int difficulty){
this(Objective.newObjective(difficulty));
}
/**
* Initialise the board, required dog and owner locations and locations of
* each of the pieces on the board.
*/
public void initialiseBoardState() {
String initState = this.objective.getInitialState();
// initialise board
for (int i = 0; i < 16; i++) {
this.board[i] = State.EMPTY;
}
// Make tree
String relevantSubstring = getRelevantSubstring(initState, 'T');
if (!relevantSubstring.equals("")) {
tree = new Location(relevantSubstring);
this.setState(tree, State.TREE);
}
// Make cats
relevantSubstring = getRelevantSubstring(initState, 'C');
if (!relevantSubstring.equals("")) {
cat1 = new Location(relevantSubstring.substring(0, 2));
this.setState(cat1, State.CAT);
if (relevantSubstring.length()==4) {
cat2 = new Location(relevantSubstring.substring(2));
this.setState(cat2, State.CAT);
}
}
// Make required dogs
relevantSubstring = getRelevantSubstring(initState, 'D');
if (!relevantSubstring.equals("")) {
int no_of_dogs = relevantSubstring.length()/2;
requiredDogLocs = new Location[no_of_dogs];
for (int i = 0; i < no_of_dogs; i++) {
requiredDogLocs[i] = new Location(relevantSubstring.substring(2*i, 2*(i+1)));
}
} else
requiredDogLocs = new Location[0];
// Make required owners
relevantSubstring = getRelevantSubstring(initState, 'O');
if (!relevantSubstring.equals("")) {
int no_of_owners = relevantSubstring.length()/2;
requiredOwnerLocs = new Location[no_of_owners];
for (int i = 0; i < no_of_owners; i++) {
requiredOwnerLocs[i] = new Location(relevantSubstring.substring(2*i, 2*(i+1)));
}
} else
requiredOwnerLocs = new Location[0];
}
/**
* A helper method for initialiseBoardState returning the relevant substring
* of the objective given a character.
* @param objective The objective string
* @param letter Either 'T', 'C', 'D' or 'O'
* @return The relevant substring or "" if there is none.
*/
public static String getRelevantSubstring(String objective, char letter) {
String order = "TCDO";
int index = objective.indexOf(letter);
if (index == -1)
return "";
int start = index + 1;
int end = -1;
for (int i = order.indexOf(letter) + 1; i < 4; i++) {
end = objective.indexOf(order.charAt(i));
if (end != -1)
break;
}
if (end == -1)
end = objective.length();
return objective.substring(start, end);
}
public Objective getObjective() {
return this.objective;
}
public Piece[] getPieces() {return this.pieces;}
public Location getTree(){
return this.tree;
}
public Location getCat1() {
return this.cat1;
}
public Location getCat2() {
return this.cat2;
}
public Location[] getRequiredDogLocs() {
return this.requiredDogLocs;
}
public Location[] getRequiredOwnerLocs() {
return this.requiredOwnerLocs;
}
/**
* Returns the dog placement string of the current board state
* In the same format as a regular solution string but excluding
* all pieces not yet placed.
* @return The piece placement string of the current board state
*/
public String getPlacements() {
String ret = "";
for (Piece piece : this.pieces) {
if (piece.onBoard())
ret += piece.getPlacement();
}
return ret;
}
/**
* Print out the board state. May be useful for debugging.
*/
public void printBoardState() {
for (int i = 0; i < 16; i++) {
System.out.print(this.board[i] + " ");
if (i % 4 == 3)
System.out.println("");
}
}
/**
* Returns state of board at given location, assuming the location is valid.
*
* @param loc: the location in question
* @return the state at the given location
*/
public State getState(Location loc) {
return this.board[loc.toPosition()];
}
/**
* Set the state of a given location on the board to a given state.
* Assumes the given location is a valid location.
*
* @param loc The board location to update
* @param state The new state of loc
*/
public void setState(Location loc, State state) {
this.board[loc.toPosition()] = state;
}
/**
* Return dog-owner piece of given leash length.
* A leash length of 0 indicates the single green dog.
* @param leashLength The leash length
* @return the piece with a leash of length leashLength
*/
public Piece getPiece(int leashLength) {
assert leashLength <= 3 && leashLength >=0;
return this.pieces[3-leashLength];
}
/**
* Checks whether a given placement string of a single dog piece is
* well formed based on the following conditions:
* 1 – it is either 2 or 4 characters in length
* 2 – all characters are ‘0’, ‘1’, ‘2’ or ‘3’
*
* @param placement string to check
* @return True if placement string is well formed, False otherwise
*/
public static boolean isPlacementWellFormed(String placement) {
return false; // FIXME Task 5 (P)
}
/**
* Returns whether or not a given placement string allows for a valid leash
* based on the following conditions:
* 1 – the placement string is well formed (see README.md)
* 2 – the leash must be ‘taut’, namely the Manhattan distance must be
* equal to the leash length
* 3 – the leash may not be diagonal
* 4 – the leash may bend at 90 degrees around a tree but otherwise must be
* straight
* Note that the single dog should only be valid if the placement string is
* of length 2
*
* Note that this method may require you to know the location of the tree
* on the current game board. To access this you can write:
* Location treeLoc = this.getTree();
*
* @param placement The placement string
* @param leashLength The leash length of the piece
* @return True if the placement string abides by the conditions and False
* otherwise
*/
public boolean checkLeash(String placement, int leashLength) {
return false; // FIXME Task 6 (P)
}
/**
* Returns whether or not a given location conflicts with (ie occupies the
* same location as) one of the leashes already placed on the board.
* Have a look at the Piece class to find out how the leash for each piece
* is stored.
*
* Note that you may need to use some of the getter methods of this class
* and the Piece and Location classes. Have a look at how toPosition() is
* used in the getState and setState methods above to see how to use
* instance methods.
*
* @param loc: the location to be checked
* @return True if the given location conflicts with a leash on the board,
* False otherwise
*/
public boolean conflictsWithLeash(Location loc) {
return false; // FIXME Task 7 (CR)
}
/**
* Returns whether or not the position of the owner in a given
* placement string is a valid based on the following conditions:
* 1 – The location is on the board
* 2 – The location is not on top of an already placed tree, owner, cat,
* dog or required dog position
* 3 – The location does not conflict with a leash
*
* @param owner The owner placement string
* @return True if placement string is a valid owner location, False
* otherwise.
*/
public boolean isOwnerLocationValid(String owner) {
return false; // FIXME Task 8 (CR)
}
/**
* Returns whether or not the position of the dog in a given placement
* string is valid based on the following conditions:
* 1 – The location is on the board
* 2 – The location is not adjacent to cat, dog or required dog position
* 3 – The location is not on top of an already placed tree, owner, cat, dog
* or required owner position
* 4 – The location does not conflict with a leash
*
* @param dog The dog placement string
* @return True if placement string is a valid dog location, False otherwise.
*/
public boolean isDogLocationValid(String dog) {
return false; // FIXME Task 9 (CR)
}
/**
* Assuming a valid dog/owner placement string, return an array of locations
* containing the locations of the leash.
* A leash can be found according to the following conditions:
* 1 – unless bent around a tree, leashes must always be straight and either
* horizontal or vertical (ie not diagonal)
* 2 – leashes may bend around a tree at a 90 degree angle
*
* The returned array should be of length leashLength + 1 with elements
* being the locations of the leash listed from the position of the dog to
* the position of the owner.
*
* For example, for placement “0003” and treeLoc (-1, -1), the function should return
* [(0, 0), (0, 1), (0, 2), (0, 3)]
* while the placement “1122” should only be given if the treeLoc is either
* (1, 2) or (2, 1), in which case it should return
* [(1, 1), (1, 2), (2, 2)] or
* [(1, 1), (2, 1), (2, 2)] respectively.
* In the case of a two character string (indicating the placement of the
* single green dog) a one-element array should be returned. For example the
* input of placement: “00” should return [(0, 0)].
*
* Note that (x, y) has been used to denote a Location with coordinates x
* and y.
* Note also that this method should work for any coordinates, do not
* restrict it to a 4×4 board only.
*
* @param placement: the placement string of the piece
* @param treeLoc: the location of the tree or (-1, -1) if the tree is not on the board
* @return an array containing the locations of the leash from the dog
* location to the owner location
*/
public static Location[] findLeash(String placement, Location treeLoc) {
return new Location[]{}; // FIXME Task 10 (D)
}
/**
* Returns whether or not a given placement allows for a valid leash
* (excluding the locations of the owner and dog on the leash)
* based on the following conditions:
*
* 1 – the placement string is well formed (see README.md)
* 2 – the leash must be ‘taut’, namely the Manhattan distance must be
* equal to the leash length
* 3 – the leash may not be diagonal
* 4 – the leash may bend at 90 degrees around a tree but otherwise must be
* straight
* 5 – the leash may not pass over cats, dogs, owners or required dogs or owners
* 6 – the leash must not cross another leash
*
* Note that there is no requirement to confirm that the dog and owner
* positions are valid.
*
* @param placement The placement string
* @param leashLength The leash length of the piece
* @return True if the placement string yields a valid leash, False
* otherwise
*/
public boolean isLeashValid(String placement, int leashLength) {
return false; // FIXME Task 11 (D)
}
/**
* Return whether or not a given placement string is a valid placement
* for the given piece on the current board. That is, we require:
* 1 – a well formed placement
* 2 – the piece to not currently be placed on the board
* 3 – the dog location of the piece to be valid
* 4 – if the piece has an owner the owner location and leash must also be
* valid
*
* @param piece The piece to be placed
* @param placement The placement string
* @return True if the placement string is valid, False otherwise
*/
public boolean isPlacementValid(Piece piece, String placement) {
if (!isPlacementWellFormed(placement))
return false;
if (piece.onBoard())
return false; // piece is already placed, it should be removed before being moved to a new position
if (placement.length() == 2)
return this.isDogLocationValid(placement);
if (this.isDogLocationValid(placement.substring(0, 2)) && this.isOwnerLocationValid(placement.substring(2, 4)))
return this.isLeashValid(placement, piece.getLeashLength());
return false;
}
/**
* Returns whether or not a given string is a solution to the current
* objective based on the following conditions:
* 1 – All 4 pieces are placed on the board in valid positions
* 2 – All required owner locations and dog locations are satisfied
*
* @param placements solution string to check
* @return True if the placement string is a solution to the objective
* of the board state, False otherwise
*/
public boolean isSolution(String placements) {
return false; // FIXME Task 12 (HD)
}
/**
* Returns the unique solution string to the boards objective.
* The format of this string is described in the README.
*
* @return The solution string of the game
*/
public String solve() {
return “”; // FIXME Task 13 (HD)
}
}