Ship.java
package org.microcol.model;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
public class Ship {
private Model model;
private final Player owner;
private final ShipType type;
private Location location;
private int availableMoves;
private boolean canAttack;
Ship(final Player owner, final ShipType type, final Location location) {
this.owner = Preconditions.checkNotNull(owner);
this.type = Preconditions.checkNotNull(type);
this.location = Preconditions.checkNotNull(location);
}
void setModel(final Model model) {
this.model = Preconditions.checkNotNull(model);
final Terrain terrain = this.model.getMap().getTerrainAt(location);
if (terrain != Terrain.OCEAN) {
throw new IllegalStateException(String.format("Ship must start on ocen (%s -> %s).", location, terrain));
}
}
public Player getOwner() {
return owner;
}
public ShipType getType() {
return type;
}
public Location getLocation() {
return location;
}
public int getAvailableMoves() {
return availableMoves;
}
void startTurn() {
availableMoves = type.getSpeed();
canAttack = true;
}
// Netestuje nedosažitelné lokace, pouze jestli je teoreticky možné na danou lokaci vstoupit
public boolean isMoveable(final Location location) {
Preconditions.checkNotNull(location);
if (!model.getMap().isValid(location)) {
return false;
}
if (model.getMap().getTerrainAt(location) != Terrain.OCEAN) {
return false;
}
return owner.getEnemyShipsAt(location).isEmpty();
}
public List<Location> getAvailableLocations() {
model.checkGameActive();
model.checkCurrentPlayer(owner);
if (availableMoves == 0) {
return ImmutableList.of();
}
Set<Location> openSet = new HashSet<>();
openSet.add(location);
Set<Location> closedSet = new HashSet<>();
for (int i = 0; i < availableMoves + 1; i++) {
Set<Location> currentSet = new HashSet<>();
for (Location location : openSet) {
for (Location neighbor : location.getNeighbors()) {
if (isMoveable(neighbor)) {
currentSet.add(neighbor);
}
}
closedSet.add(location);
}
openSet.clear();
openSet.addAll(currentSet);
}
closedSet.remove(location);
return ImmutableList.copyOf(closedSet);
}
public List<Location> getPath(final Location destination) {
return getPath(destination, false);
}
public List<Location> getPath(final Location destination, final boolean excludeDestination) {
PathFinder finder = new PathFinder(this, location, destination, excludeDestination);
return finder.search();
}
public void moveTo(final Path path) {
model.checkGameActive();
model.checkCurrentPlayer(owner);
Preconditions.checkNotNull(path);
Preconditions.checkArgument(path.getStart().isAdjacent(location), "Path (%s) must be adjacent to current location (%s).", path.getStart(), location);
Preconditions.checkArgument(model.getMap().isValid(path), "Path (%s) must be valid.", path);
Preconditions.checkArgument(!path.containsAny(owner.getEnemyShipsAt().keySet()), "There is enemy ship on path (%s).", path);
final Location start = location;
final List<Location> locations = new ArrayList<>();
// TODO JKA Use streams
for (Location newLocation : path.getLocations()) {
if (availableMoves <= 0) {
// TODO JKA neumožnit zadat delší cestu, než je povolený počet
break;
}
if (model.getMap().getTerrainAt(newLocation) != Terrain.OCEAN) {
throw new IllegalArgumentException(String.format("Path (%s) must contain only ocean (%s).", newLocation, model.getMap().getTerrainAt(newLocation)));
}
locations.add(newLocation);
location = newLocation;
availableMoves--;
}
if (!locations.isEmpty()) {
model.fireShipMoved(this, start, Path.of(locations));
}
}
// TODO JKA canAttack
public void attack(final Ship ship) {
model.checkGameActive();
model.checkCurrentPlayer(owner);
Preconditions.checkNotNull(ship);
Preconditions.checkArgument(!owner.equals(ship.owner), "Cannot attack own ship (%s - %s).", this, ship);
Preconditions.checkArgument(location.isAdjacent(ship.location), "Ship is not adjacent (%s - %s)", this, ship);
Preconditions.checkArgument(canAttack, "This ship (%s) already fight this turn.", this);
availableMoves = 0;
canAttack = false;
final Ship destroyed = (Math.random() <= 0.6) ? ship : this;
model.destroyShip(destroyed);
model.fireShipAttacked(this, ship, destroyed);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("owner", owner)
.add("type", type)
.add("location", location)
.add("availableMoves", availableMoves)
.add("canAttack", canAttack)
.toString();
}
}