/**
* The map for battleship.
*
* User: Pascal
* Version: 1.3.1
* Date: 13.05.13
*
* Changes:
* 0.1 (07.03.13)
* - First implementation.
* - Implement initialization of local variables.
* 1.0 (15.04.13)
* - Implement ship setting
* - Implement random ship setting
* 1.1 (16.04.13)
* - Implement BombField() function.
* 1.2 (17.04.13)
* - Refactor methods.
* - Add helper functions for console output and the ai.
* 1.3 (18.04.13)
* - Implement removing of ships. 'UnSetShip()'
* 1.3.1 (13.05.2013)
* - BugFix map size. If I initialize the size at the beginning of a class at the declaration
* I became problems with the setting, because every time I want to get the size I get only
* the default size (10x10).
*/
package entities.Map;
import entities.EntityBase;
import entities.ship.Ship;
import entities.ship.ShipState;
import entities.ship.ShipType;
import game.Options;
import other.Const;
import other.Randomizer;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
/**
* The map for battleship.
*
* @author Pascal
* @version 1.3.1
*/
public class FieldMap extends EntityBase
{
public List<List<Field>> map;
public List<Ship> ships;
public Point MapSize;
String bombState = "";
boolean hasHit = false;
Integer moves;
Point hitPoint;
Ship lastSunkenShip;
/**
* Initializes the map data.
*/
@Override
public void Initialize()
{
Options.ReadOptionsToProperties();
MapSize = Options.MapSize;
MapSize.setLocation(MapSize.x + 2, MapSize.y + 2);
moves = 0;
// Initialize the map
map = new ArrayList<List<Field>>();
for (int i = 0; i < MapSize.x; i++)
{
map.add(new ArrayList<Field>());
for (int j = 0; j < MapSize.y; j++)
{
map.get(i).add(new Field());
}
}
/**
* I use 12 fields each row, because the borders have FieldState.Coast
* and on this fields you can place nothing, so in the game it's like
* you're playing on a 10x10 map.
*/
for (int i = 0; i < map.size(); i++)
{
for (int j = 0; j < map.size(); j++)
{
if (i == 0 || i == map.size()-1)
{ map.get(i).get(j).State = FieldState.Coast; }
else if (j == 0 || j == map.size()-1)
{ map.get(i).get(j).State = FieldState.Coast; }
else
{
// Initialize set the state automatically to FieldState.Nothing.
map.get(i).get(j).Initialize();
}
}
}
ships = new ArrayList<Ship>();
// Initialize ships without position.
ships.add(new Ship(ShipType.Battleship, 5));
ships.add(new Ship(ShipType.Cruiser, 4));
ships.add(new Ship(ShipType.Cruiser, 4));
ships.add(new Ship(ShipType.Destroyer, 3));
ships.add(new Ship(ShipType.Destroyer, 3));
ships.add(new Ship(ShipType.Destroyer, 3));
ships.add(new Ship(ShipType.Submarine, 2));
ships.add(new Ship(ShipType.Submarine, 2));
ships.add(new Ship(ShipType.Submarine, 2));
if (Randomizer.nextInt(10) == 8)
ships.add(new Ship(ShipType.YellowSubmarine, 2));
else
ships.add(new Ship(ShipType.Submarine, 2));
if (Const.DEBUG)
{
for (Ship ship : ships)
System.out.printf(ship.Type.toString() + " - " + ship.Length + " - " + ship.Position + "\n");
}
}
/**
* Update the entity object.
*/
@Override
public void Update()
{
}
/**
* Translate the map into a list of characters which can be printed
* out on the console
*
* @return Returns a list of characters with the map data.
*/
public List<List<Character>> MapAsCharList(boolean hide)
{
List<List<Character>> ret = new ArrayList<List<Character>>();
// Get the basic chars from the map
for (int i = 0; i < map.size(); i++)
{
ret.add(new ArrayList<Character>());
for (int t = 0; t < map.get(i).size(); t++)
{
ret.get(i).add(map.get(i).get(t).FieldAsChar(hide));
}
}
// Overlay the map chars with the ships.
for (Ship ship : ships)
{
for (int i = 0; i < ship.Length; i++)
{
if (ship.Vertical)
ret.get(ship.Position.x+i).set(ship.Position.y, ship.Parts.get(i).StateAsChar(hide));
else
ret.get(ship.Position.x).set(ship.Position.y+i, ship.Parts.get(i).StateAsChar(hide));
}
}
return ret;
}
/**
* Set the ships randomly over the map.
*/
public void SetShipsRandomly()
{
Integer row, column, vertical;
int state = 0;
int stateCount = 0;
for (Ship ship : ships)
{
boolean set = false;
while (!set)
{
row = Randomizer.nextInt(map.size()-1);
column = Randomizer.nextInt(map.size()-1);
vertical = Randomizer.nextInt(1);
boolean isVertical;
isVertical = vertical != 0;
set = SetShip(ship, row, column, isVertical);
// Output if the calculating of positions need more time.
state++;
if (state > 5000)
{
state = 0;
stateCount++;
System.out.println("Calculate ship positions.");
}
// If the program cannot set a ship, reset all and begin from new.
if (stateCount > 2)
{
System.out.printf("I'm sorry but I can't calculate all the ship positions. :(\n");
System.out.printf("But I try it again. :)");
for (Ship removingShip : ships)
UnsetShip(removingShip);
SetShipsRandomly();
}
}
}
}
/**
* Set a ship at the given position.
* @param ship The ship that had to be set.
* @param row The row place.
* @param column The column place.
* @param vertical Set ship vertical or horizontal.
* @return It's true if the ship can set, otherwise it's false.
*/
boolean SetShip(Ship ship, int row, int column, boolean vertical)
{
boolean canSet = true;
int tmpRow = row;
int tmpColumn = column;
if ((row + ship.Length >= map.size()) ||
(column + ship.Length >= map.size()))
{
return false;
}
for (int i = 0; i < ship.Length; i++)
{
if (vertical)
tmpRow = row + i;
else
tmpColumn = column + i;
// Check the places around a ship part, so it do not connect to another ship
if (checkState(map.get(tmpRow).get(tmpColumn).State) ||
(tmpRow == 0 || checkState(map.get(tmpRow-1).get(tmpColumn).State)) ||
checkState(map.get(tmpRow+1).get(tmpColumn).State) ||
(tmpColumn == 0 || checkState(map.get(tmpRow).get(tmpColumn-1).State)) ||
checkState(map.get(tmpRow).get(tmpColumn+1).State))
{
canSet = false;
}
}
if (canSet)
{
ship.Position = new Point(row, column);
ship.Vertical = vertical;
tmpRow = row;
tmpColumn = column;
for (int i = 0; i < ship.Length; i++)
{
if (vertical)
tmpRow = row + i;
else
tmpColumn = column + i;
map.get(tmpRow).get(tmpColumn).State = FieldState.Occupied;
}
}
return canSet;
}
/**
* Unset a ship
* @param ship The ship which hat to be unset.
*/
private void UnsetShip(Ship ship)
{
int tmpRow = ship.Position.x;
int tmpColumn = ship.Position.y;
for (int i = 0; i < ship.Length; i++)
{
if (ship.Vertical)
tmpRow = ship.Position.x + i;
else
tmpColumn = ship.Position.y + i;
if (tmpRow > -1 && tmpColumn > -1)
map.get(tmpRow).get(tmpColumn).State = FieldState.Occupied;
}
ship.Position = new Point(-1, -1);
ship.Vertical = false;
}
private boolean checkState(FieldState state)
{
return !(state == FieldState.Nothing || state == FieldState.Coast);
}
/**
* Bombs a specific Field.
* @param point The coordinates of the missile.
* @return Returns a string which describes your success or failure.
*/
public boolean BombField(Point point)
{
return BombField(point.x, point.y);
}
/**
* Bombs a specific Field.
* @param x The x coordinate of the missile.
* @param y The y coordinate of the missile.
* @return Returns a string which describes your success or failure.
*/
public boolean BombField(int x, int y)
{
lastSunkenShip = null;
hitPoint = new Point(x, y);
hasHit = false;
if (Const.DEBUG)
{
System.out.printf(x + ";" + y + " - ");
System.out.printf(map.get(x).get(y).State.toString() + "\n");
}
switch (map.get(x).get(y).State)
{
case Coast:
bombState = "You cannot fire at the coast. I don't allow this.";
return false;
case Bombed:
bombState = "This field is already bombed. You cannot do it twice";
return false;
case Occupied:
{
map.get(x).get(y).State = FieldState.Bombed;
String shipType = "";
boolean isSunk = false;
searchShip:
for (Ship ship : ships)
{
int tmpX = ship.Position.x, tmpY = ship.Position.y;
for (int i = 0; i < ship.Length; i++)
{
if (tmpX == x && tmpY == y)
{
ship.Parts.get(i).State = ShipState.Damaged;
shipType = ship.Type.name();
isSunk = ship.IsSunk();
if (isSunk)
{
ship.Sink();
lastSunkenShip = ship;
}
else
ship.State = ShipState.Damaged;
break searchShip;
}
if (Const.DEBUG && ship.State != ShipState.Ok)
System.out.println(ship.State + ";" + ship.Type + ";" + isSunk);
if (ship.Vertical)
tmpX++;
else
tmpY++;
}
}
if (isSunk)
{
// Little easter egg. :)
if (shipType.equalsIgnoreCase("yellowsubmarine"))
bombState = SinkYellowSubmarine();
else
bombState = "You hit a " + shipType + ". It sunks.";
hasHit = true;
moves++;
return true;
}
else
{
bombState = "You hit something.";
hasHit = true;
moves++;
return true;
}
}
default:
{
map.get(x).get(y).State = FieldState.Bombed;
bombState = "There is nothing, search a better target.";
moves++;
return true;
}
}
}
/**
* Get the state of the <code>BombField()</code> function.
* @return Returns a string with the state of your missile (if there is nothing or else)
*/
public String getBombState()
{
return bombState;
}
/**
* Returns a boolean value whether a ship was hit or not.
* @return true = hit a ship; false = miss target
*/
public boolean getHasHit()
{
return hasHit;
}
/**
* Get the las point who hitting a ship
* @return Return a Point with the target coordinates.
*/
public Point getHitPoint()
{
return hitPoint;
}
/**
* Returns all moves in this map
* @return Number of moves as integer
*/
public Integer getMoves()
{
return moves;
}
/**
* Returns the last sunken ship or null if in the actually move no ship sunk.
* @return A ship object.
*/
public Ship getLastSunkenShip()
{
return lastSunkenShip;
}
/**
* If you destroy the yellow submarine you became this.
* @return Returns a special string for yellow submarine.
*/
public String SinkYellowSubmarine()
{
String out = "You destroy the yellow submarine. Here a little song for you ;D.\n\n";
out += "In the town where I was born\n";
out += "Lived a man who sailed to sea\n";
out += "And he told us of his life\n";
out += "In the land of submarines\n";
out += "So we sailed on to the sun\n";
out += "Till we found a sea of green\n";
out += "And we lived beneath the waves\n";
out += "In our yellow submarine\n\n";
out += "We all live in a yellow submarine\n";
out += "Yellow submarine, yellow submarine\n";
out += "We all live in a yellow submarine\n";
out += "Yellow submarine, yellow submarine\n - The Beatles \n\n";
return out;
}
/**
* Determines whether all ships are sunk.
* @return true = all sunk, false = not
*/
public boolean Defeated()
{
boolean ret = true;
for (Ship ship : ships)
{
if (ship.State != ShipState.Sunk)
ret = false;
}
return ret;
}
/**
* Dispose the class and release objects.
*/
@Override
public void Dispose()
{
map.clear();
ships.clear();
}
}