/**
* Copyright (C) 2002-2012 The FreeCol Team
*
* This file is part of FreeCol.
*
* FreeCol 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, either version 2 of the License, or
* (at your option) any later version.
*
* FreeCol 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.
*
* You should have received a copy of the GNU General Public License
* along with FreeCol. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.freecol.common.model;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
import net.sf.freecol.common.FreeColException;
import net.sf.freecol.common.model.FreeColObject;
import net.sf.freecol.common.model.Map.Direction;
import net.sf.freecol.common.model.Map.Position;
import net.sf.freecol.common.model.Player.Stance;
import net.sf.freecol.common.model.pathfinding.CostDecider;
import net.sf.freecol.common.model.pathfinding.CostDeciders;
import net.sf.freecol.common.model.pathfinding.GoalDecider;
import net.sf.freecol.server.model.ServerUnit;
import net.sf.freecol.util.test.FreeColTestCase;
import net.sf.freecol.util.test.FreeColTestUtils;
public class MapTest extends FreeColTestCase {
private final TileType highSeasType
= spec().getTileType("model.tile.highSeas");
private final TileType lakeType
= spec().getTileType("model.tile.lake");
private final TileType oceanType
= spec().getTileType("model.tile.ocean");
private final TileType plainsType
= spec().getTileType("model.tile.plains");
private final UnitType artilleryType
= spec().getUnitType("model.unit.artillery");
private final UnitType colonistType
= spec().getUnitType("model.unit.freeColonist");
private final UnitType galleonType
= spec().getUnitType("model.unit.galleon");
private final UnitType pioneerType
= spec().getUnitType("model.unit.hardyPioneer");
private Map getSingleLandPathMap(Game game){
MapBuilder builder = new MapBuilder(game);
builder.setBaseTileType(oceanType);
// Land Stripe
builder.setTile(1,11,plainsType);
builder.setTile(2,10,plainsType);
builder.setTile(2,9,plainsType);
builder.setTile(3,8,plainsType);
builder.setTile(3,7,plainsType);
return builder.build();
}
// (1,5)*
// *
// * * * F(3,7)
// * C(3,8)
// * *
//
// * *
//
// *S(1,11)
//
private Map getShortLongPathMap(Game game){
MapBuilder builder = new MapBuilder(game);
builder.setBaseTileType(oceanType);
//Start
builder.setTile(1,11,plainsType);
//Short path
builder.setTile(2,10,plainsType);
builder.setTile(2,9,plainsType);
//Longer path
builder.setTile(1,9,plainsType);
builder.setTile(1,7,plainsType);
builder.setTile(1,5,plainsType);
builder.setTile(2,6,plainsType);
builder.setTile(2,7,plainsType);
// Common
builder.setTile(3,8,plainsType);
// Finish
builder.setTile(3,7,plainsType);
return builder.build();
}
public void testMapGameInt() throws FreeColException {
int expectedWidth = 20;
int expectedHeigth = 15;
Game game = getStandardGame();
MapBuilder builder = new MapBuilder(game);
Map map = builder.setDimensions(expectedWidth, expectedHeigth).build();
assertEquals(expectedWidth, map.getWidth());
assertEquals(expectedHeigth, map.getHeight());
}
public void testGetSurroundingTiles() {
Game game = getStandardGame();
MapBuilder builder = new MapBuilder(game);
Map map = builder.setDimensions(10, 15).build();
game.setMap(map);
// Check in the middle
List<Tile> surroundingTiles = new ArrayList<Tile>();
for (Tile t: map.getTile(4,8).getSurroundingTiles(1))
surroundingTiles.add(t);
assertEquals(8, surroundingTiles.size());
assertTrue(surroundingTiles.contains(map.getTile(4, 6)));
assertTrue(surroundingTiles.contains(map.getTile(4, 10)));
assertTrue(surroundingTiles.contains(map.getTile(3, 8)));
assertTrue(surroundingTiles.contains(map.getTile(5, 8)));
assertTrue(surroundingTiles.contains(map.getTile(3, 7)));
assertTrue(surroundingTiles.contains(map.getTile(4, 7)));
assertTrue(surroundingTiles.contains(map.getTile(3, 9)));
assertTrue(surroundingTiles.contains(map.getTile(4, 9)));
// Check on sides
surroundingTiles = new ArrayList<Tile>();
for (Tile t: map.getTile(0, 0).getSurroundingTiles(1))
surroundingTiles.add(t);
assertEquals(3, surroundingTiles.size());
assertTrue(surroundingTiles.contains(map.getTile(0, 2)));
assertTrue(surroundingTiles.contains(map.getTile(1, 0)));
assertTrue(surroundingTiles.contains(map.getTile(0, 1)));
// Check larger range
surroundingTiles = new ArrayList<Tile>();
for (Tile t: map.getTile(4, 8).getSurroundingTiles(2))
surroundingTiles.add(t);
assertEquals(25 - 1, surroundingTiles.size());
// Check that all tiles are returned
surroundingTiles = new ArrayList<Tile>();
for (Tile t: map.getTile(4, 8).getSurroundingTiles(10))
surroundingTiles.add(t);
assertEquals(150 - 1, surroundingTiles.size());
}
public void testGetReverseDirection() {
assertEquals(Direction.S, Direction.N.getReverseDirection());
assertEquals(Direction.N, Direction.S.getReverseDirection());
assertEquals(Direction.E, Direction.W.getReverseDirection());
assertEquals(Direction.W, Direction.E.getReverseDirection());
assertEquals(Direction.NE, Direction.SW.getReverseDirection());
assertEquals(Direction.NW, Direction.SE.getReverseDirection());
assertEquals(Direction.SW, Direction.NE.getReverseDirection());
assertEquals(Direction.SE, Direction.NW.getReverseDirection());
}
public void testGetWholeMapIterator() {
Game game = getStandardGame();
Tile[][] tiles = new Tile[5][6];
Set<Position> positions = new HashSet<Position>();
Set<Tile> allTiles = new HashSet<Tile>();
for (int x = 0; x < 5; x++) {
for (int y = 0; y < 6; y++) {
Tile tile = new Tile(game, plainsType, x, y);
tiles[x][y] = tile;
allTiles.add(tile);
positions.add(new Position(x, y));
}
}
Map map = new Map(game, tiles);
Iterator<Position> wholeMapIterator = map.getWholeMapIterator();
for (int i = 0; i < 30; i++) {
assertTrue(wholeMapIterator.hasNext());
assertTrue(positions.remove(wholeMapIterator.next()));
}
assertEquals(0, positions.size());
assertFalse(wholeMapIterator.hasNext());
// Check for-Iterator
for (Tile t : map.getAllTiles()){
assertTrue(allTiles.remove(t));
}
assertEquals(0, positions.size());
}
public void testGetAdjacent() {
Game game = getStandardGame();
MapBuilder builder = new MapBuilder(game);
Map map = builder.setDimensions(10, 15).build();
{ // Even case
Iterator<Position> i = map.getAdjacentIterator(map.getTile(4, 8).getPosition());
List<Position> shouldBe = new ArrayList<Position>();
shouldBe.add(new Position(4, 6));
shouldBe.add(new Position(4, 10));
shouldBe.add(new Position(3, 8));
shouldBe.add(new Position(5, 8));
shouldBe.add(new Position(4, 7));
shouldBe.add(new Position(4, 9));
shouldBe.add(new Position(3, 7));
shouldBe.add(new Position(3, 9));
for (int j = 0; j < 8; j++) {
assertTrue(i.hasNext());
Position p = i.next();
assertTrue("" + p.getX() + ", " + p.getY(), shouldBe.contains(p));
}
assertFalse(i.hasNext());
}
{ // Even case 2
Iterator<Position> i = map.getAdjacentIterator(map.getTile(5, 8).getPosition());
List<Position> shouldBe = new ArrayList<Position>();
shouldBe.add(new Position(5, 6));
shouldBe.add(new Position(5, 10));
shouldBe.add(new Position(4, 8));
shouldBe.add(new Position(6, 8));
shouldBe.add(new Position(4, 7));
shouldBe.add(new Position(5, 7));
shouldBe.add(new Position(4, 9));
shouldBe.add(new Position(5, 9));
for (int j = 0; j < 8; j++) {
assertTrue(i.hasNext());
Position p = i.next();
assertTrue("" + p.getX() + ", " + p.getY(), shouldBe.contains(p));
}
assertFalse(i.hasNext());
}
{ // Odd case
Iterator<Position> i = map.getAdjacentIterator(map.getTile(4, 7).getPosition());
List<Position> shouldBe = new ArrayList<Position>();
shouldBe.add(new Position(4, 5));
shouldBe.add(new Position(4, 9));
shouldBe.add(new Position(3, 7));
shouldBe.add(new Position(5, 7));
shouldBe.add(new Position(4, 6));
shouldBe.add(new Position(5, 6));
shouldBe.add(new Position(4, 8));
shouldBe.add(new Position(5, 8));
for (int j = 0; j < 8; j++) {
assertTrue(i.hasNext());
Position p = i.next();
assertTrue("" + p.getX() + ", " + p.getY(), shouldBe.contains(p));
}
assertFalse(i.hasNext());
}
}
public void testRandomDirection() {
Game game = getStandardGame();
MapBuilder builder = new MapBuilder(game);
builder.setDimensions(10, 15).build();
Direction[] dirs = Direction.getRandomDirections("testRandomDirection",
new Random(1));
assertNotNull(dirs);
}
/**
* Tests path discoverability in a map with only one path available
* That path is obstructed by a settlement, so is invalid
*/
public void testNoPathAvailableDueToCampInTheWay() {
Game game = getStandardGame();
Map map = getSingleLandPathMap(game);
game.setMap(map);
// set obstructing indian camp
Tile settlementTile = map.getTile(2,10);
FreeColTestCase.IndianSettlementBuilder builder
= new FreeColTestCase.IndianSettlementBuilder(game);
builder.settlementTile(settlementTile).build();
// set unit
Player dutchPlayer = game.getPlayer("model.nation.dutch");
Tile unitTile = map.getTile(1, 11);
Tile destinationTile = map.getTile(3,7);
Unit colonist = new ServerUnit(game, unitTile, dutchPlayer,
colonistType);
colonist.setDestination(destinationTile);
PathNode path = colonist.findPath(destinationTile);
assertNull("No path should be available", path);
}
/**
* Tests path discoverability in a map with only one path available
* That path is obstructed by a settlement, so is invalid
*/
public void testNoPathAvailableDueToColonyInTheWay() {
Game game = getStandardGame();
Map map = getSingleLandPathMap(game);
game.setMap(map);
// set obstructing french colony
Player frenchPlayer = game.getPlayer("model.nation.french");
Tile settlementTile = map.getTile(2,10);
FreeColTestUtils.getColonyBuilder().player(frenchPlayer)
.colonyTile(settlementTile).build();
assertTrue("French colony was not set properly on the map",
settlementTile.getSettlement() != null);
// set unit
Player dutchPlayer = game.getPlayer("model.nation.dutch");
Tile unitTile = map.getTile(1, 11);
Tile destinationTile = map.getTile(3,7);
Unit colonist = new ServerUnit(game, unitTile, dutchPlayer,
colonistType);
colonist.setDestination(destinationTile);
PathNode path = colonist.findPath(destinationTile);
assertNull("No path should be available", path);
}
public void testMoveThroughTileWithEnemyUnit() {
Game game = getStandardGame();
Map map = getTestMap();
game.setMap(map);
//Setup
Tile enemyUnitTile = map.getTile(2,1);
Player frenchPlayer = game.getPlayer("model.nation.french");
new ServerUnit(game, enemyUnitTile, frenchPlayer, pioneerType);
Tile unitTile = map.getTile(1, 1);
Tile otherTile = map.getTile(1, 2);
Player dutchPlayer = game.getPlayer("model.nation.dutch");
Unit unit = new ServerUnit(game, unitTile, dutchPlayer, pioneerType);
// unit is going somewhere else
Tile unitDestination = map.getTile(3, 1);
unit.setDestination(unitDestination);
// Execute
CostDecider decider = CostDeciders.avoidSettlementsAndBlockingUnits();
assertTrue("No blocking unit, should be legal",
decider.getCost(unit, unitTile, otherTile, 4)
!= CostDecider.ILLEGAL_MOVE);
assertTrue("Blocking unit, should be illegal",
decider.getCost(unit, unitTile, enemyUnitTile, 4)
== CostDecider.ILLEGAL_MOVE);
}
/**
* Tests path discoverability in a map with only one path available
* That path is obstructed by a settlement, so is invalid
*/
public void testNoPathAvailableDueToUnitInTheWay() {
Game game = getStandardGame();
Map map = getSingleLandPathMap(game);
game.setMap(map);
// set obstructing unit
Tile unitObstructionTile = map.getTile(2,10);
Player frenchPlayer = game.getPlayer("model.nation.french");
new ServerUnit(game, unitObstructionTile, frenchPlayer, colonistType);
// set unit
Player dutchPlayer = game.getPlayer("model.nation.dutch");
Tile unitTile = map.getTile(1, 11);
Tile destinationTile = map.getTile(3,7);
Unit colonist = new ServerUnit(game, unitTile, dutchPlayer,
colonistType);
colonist.setDestination(destinationTile);
PathNode path = map.findPath(colonist, colonist.getTile(),
destinationTile, null,
CostDeciders.avoidSettlementsAndBlockingUnits());
assertNull("No path should be available", path);
}
public void testShortestPathObstructed() {
Game game = getStandardGame();
Map map = getShortLongPathMap(getGame());
game.setMap(map);
// set obstructing indian camp
Tile settlementTile = map.getTile(2, 10);
FreeColTestCase.IndianSettlementBuilder builder
= new FreeColTestCase.IndianSettlementBuilder(game);
builder.settlementTile(settlementTile).build();
// set unit
Player dutchPlayer = game.getPlayer("model.nation.dutch");
Tile unitTile = map.getTile(1, 11);
Unit colonist = new ServerUnit(game, unitTile, dutchPlayer,
colonistType);
Tile destinationTile = map.getTile(3,7);
colonist.setDestination(destinationTile);
PathNode path = colonist.findPath(destinationTile);
assertNotNull("A path should be available", path);
}
public void testSearchForColony() {
Game game = getStandardGame();
Map map = getCoastTestMap(plainsType, true);
game.setMap(map);
Player dutchPlayer = game.getPlayer("model.nation.dutch");
Player frenchPlayer = game.getPlayer("model.nation.french");
Tile unitTile = map.getTile(15, 5);
Tile colonyTile = map.getTile(9, 9); // should be on coast
Unit galleon = new ServerUnit(game, unitTile, dutchPlayer, galleonType);
Unit artillery = new ServerUnit(game, galleon, dutchPlayer, artilleryType);
FreeColTestUtils.getColonyBuilder().player(frenchPlayer)
.colonyTile(colonyTile).build();
assertTrue("French colony not on the map",
colonyTile.getSettlement() != null);
dutchPlayer.setStance(frenchPlayer, Stance.WAR);
frenchPlayer.setStance(dutchPlayer, Stance.WAR);
// Test a GoalDecider with subgoals.
// The scoring function is deliberately simple.
GoalDecider gd = new GoalDecider() {
private PathNode found = null;
private int score = -1;
private int scoreTile(Tile tile) {
return tile.getX() + tile.getY();
}
public PathNode getGoal() {
return found;
}
public boolean hasSubGoals() {
return true;
}
public boolean check(Unit u, PathNode pathNode) {
Settlement settlement = pathNode.getLocation().getSettlement();
if (settlement != null) {
int value = scoreTile(pathNode.getTile());
if (value > score) {
score = value;
found = pathNode;
return true;
}
}
return false;
}
};
PathNode path = map.search(artillery, unitTile, gd,
CostDeciders.avoidIllegal(),
FreeColObject.INFINITY, galleon);
assertTrue("Should find the French colony via a drop off",
path != null && path.getTransportDropNode() != null
&& path.getLastNode().getTile() == colonyTile);
// Add another colony
Tile colonyTile2 = map.getTile(5, 5); // should score less
FreeColTestUtils.getColonyBuilder().player(frenchPlayer)
.colonyTile(colonyTile2).build();
assertTrue("French colony not on the map",
colonyTile2.getSettlement() != null);
path = map.search(artillery, unitTile, gd,
CostDeciders.avoidIllegal(),
FreeColObject.INFINITY, galleon);
assertTrue("Should still find the first French colony via a drop off",
path != null && path.getTransportDropNode() != null
&& path.getLastNode().getTile() == colonyTile);
}
public void testLatitude() {
Game game = getStandardGame();
MapBuilder builder = new MapBuilder(game);
Map map = builder.setDimensions(1, 181).build();
assertEquals(181, map.getHeight());
assertEquals(1f, map.getLatitudePerRow());
assertEquals(-90, map.getLatitude(0));
assertEquals(0, map.getRow(-90));
assertEquals(0, map.getLatitude(90));
assertEquals(90, map.getRow(0));
assertEquals(90, map.getLatitude(180));
assertEquals(180, map.getRow(90));
builder = new MapBuilder(game);
map = builder.setDimensions(1, 91).build();
assertEquals(91, map.getHeight());
assertEquals(2f, map.getLatitudePerRow());
assertEquals(-90, map.getLatitude(0));
assertEquals(0, map.getRow(-90));
assertEquals(0, map.getLatitude(45));
assertEquals(45, map.getRow(0));
assertEquals(90, map.getLatitude(90));
assertEquals(90, map.getRow(90));
builder = new MapBuilder(game);
map = builder.setDimensions(1, 91).build();
map.setMinimumLatitude(0);
assertEquals(91, map.getHeight());
assertEquals(1f, map.getLatitudePerRow());
assertEquals(0, map.getLatitude(0));
assertEquals(0, map.getRow(0));
assertEquals(45, map.getLatitude(45));
assertEquals(45, map.getRow(45));
assertEquals(90, map.getLatitude(90));
assertEquals(90, map.getRow(90));
}
public void testFindPath() {
Game game = getStandardGame();
Map map = getCoastTestMap(plainsType, true);
game.setMap(map);
Player dutch = game.getPlayer("model.nation.dutch");
Europe europe = dutch.getEurope();
PathNode path;
// Nearest port should be Europe
Tile seaTile = map.getTile(13, 2);
Unit galleon = new ServerUnit(game, seaTile, dutch,
galleonType);
path = galleon.findOurNearestPort();
assertNotNull("Nearest port should exist.", path);
assertEquals("Nearest port should be Europe.", europe,
path.getLastNode().getLocation());
Tile settlementTile = map.getTile(9, 2);
FreeColTestUtils.getColonyBuilder().player(dutch)
.colonyTile(settlementTile).build();
assertTrue("Dutch colony should be on the map.",
settlementTile.getSettlement() != null);
assertTrue("Dutch colony should be on the shore.",
settlementTile.isShore());
// Nearest port should now be the colony.
path = galleon.findOurNearestPort();
assertNotNull("Nearest port should exist.", path);
assertEquals("Nearest port should be the colony.", settlementTile,
path.getLastNode().getTile());
// Check colonist can find the trivial path
Unit colonist = new ServerUnit(game, settlementTile, dutch,
colonistType);
path = map.findPath(colonist, settlementTile,
settlementTile, null, null);
assertNotNull("Trivial path should exist.", path);
assertNull("Trivial path should be trivial.", path.next);
assertEquals("Trivial path should start at settlement.",
settlementTile, path.getTile());
// Check colonist can not find a path into the sea
path = map.findPath(colonist, settlementTile, seaTile, null, null);
assertNull("Sea path should be illegal.", path);
// Check that a naval unit can find that path.
path = map.findPath(galleon, settlementTile, seaTile, null, null);
assertNotNull("Sea path should be legal for naval unit.", path);
assertEquals("Sea path should start at settlement.", settlementTile,
path.getTile());
assertEquals("Sea path should end at sea tile.", seaTile,
path.getLastNode().getTile());
// Check giving the colonist access to a carrier makes the sea
// path work.
path = map.findPath(colonist, settlementTile, seaTile, galleon, null);
assertNotNull("Sea path should now be legal.", path);
assertEquals("Sea path should start at settlement.", settlementTile,
path.getTile());
assertEquals("Sea path should end at sea tile.", seaTile,
path.getLastNode().getTile());
// Check the path still works if the colonist has to walk to
// the carrier.
Tile landTile = map.getTile(2, 2);
path = map.findPath(colonist, landTile, seaTile, galleon, null);
assertNotNull("Sea path should still be legal.", path);
assertEquals("Sea path should start at land tile.", landTile,
path.getTile());
assertEquals("Sea path should end at sea tile.", seaTile,
path.getLastNode().getTile());
while (!path.isOnCarrier()) path = path.next;
assertEquals("Sea path should include pickup at settlement.",
settlementTile, path.getTile());
// Check the colonist uses the carrier if it is quicker than walking.
Tile shoreTile = map.getTile(9, 13);
assertTrue("Shore tile should be on the shore.", shoreTile.isShore());
path = map.findPath(colonist, settlementTile, shoreTile,
galleon, null);
assertNotNull("Shore path should be legal.", path);
assertTrue("Shore path should have carrier moves.",
path.usesCarrier());
assertNotNull("Shore path should have drop node.",
path.getCarrierMove().getTransportDropNode());
// Check the colonist does not use the carrier if it does not help.
Tile midTile = map.getTile(9, 4);
path = map.findPath(colonist, map.getTile(2, 6), midTile,
galleon, null);
assertNotNull("Middle path should be legal.", path);
assertFalse("Middle path should not not use carrier.",
path.usesCarrier());
// Check path to Europe.
path = map.findPath(colonist, settlementTile, europe,
galleon, null);
assertNotNull("To-Europe path should be valid.", path);
assertEquals("To-Europe path should end in Europe.", europe,
path.getLastNode().getLocation());
// Check path from Europe.
path = map.findPath(colonist, europe, landTile, galleon, null);
assertNotNull("From-Europe path should be valid.", path);
assertEquals("From-Europe path should start in Europe.", europe,
path.getLocation());
// Add another settlement on a lake connected to the existing
// settlement and see if a ship at the new settlement can find
// a path to Europe.
Tile lakeTile = map.getTile(8, 2);
lakeTile.setType(lakeType);
Tile anotherSettlementTile = map.getTile(7, 2);
FreeColTestUtils.getColonyBuilder().player(dutch)
.colonyTile(anotherSettlementTile).build();
path = map.findPath(galleon, anotherSettlementTile, europe,
null, null);
assertNotNull("From-lake-settlement path should be valid.", path);
assertEquals("From-lake-settlement path should end in Europe.", europe,
path.getLastNode().getLocation());
// Put the unit on the carrier, put the carrier out to sea, and
// find a path inland.
colonist.setLocation(galleon);
galleon.setLocation(seaTile);
path = map.findPath(colonist, galleon, landTile, galleon, null);
assertNotNull("From-galleon path should be valid.", path);
assertEquals("From-galleon path should start at sea.", seaTile,
path.getLocation());
assertTrue("From-galleon path should start on carrier.",
path.isOnCarrier());
assertNotNull("From-galleon path should have a drop node.",
path.getTransportDropNode());
}
}