use of soc.game.SOCBoard in project JSettlers2 by jdmonin.
the class SOCRobotDM method recalcLongestRoadETAAux.
/**
* Does a depth first search of legal possible road edges from the end point of the longest
* path connecting a graph of nodes, and returns which roads or how many roads
* would need to be built to take longest road.
*<P>
* Do not call if {@link SOCGameOption#K_SC_0RVP} is set, because
* this method needs {@link SOCPlayer#getLRPaths()} which will be empty.
*<P>
* Combined implementation for use by SOCRobotDM and {@link SOCPlayerTracker}.
*
* @param pl Calculate this player's longest road;
* typically SOCRobotDM.ourPlayerData or SOCPlayerTracker.player
* @param wantsStack If true, return the Stack; otherwise, return numRoads.
* @param startNode the path endpoint, such as from
* {@link SOCPlayer#getLRPaths()}.(i){@link SOCLRPathData#getBeginning() .getBeginning()}
* or {@link SOCLRPathData#getEnd() .getEnd()}
* @param pathLength the length of that path
* @param lrLength length of longest road in the game
* @param searchDepth how many roads out to search
*
* @return if <tt>wantsStack</tt>: a {@link Stack} containing the path of roads with the last one
* (farthest from <tt>startNode</tt>) on top, or <tt>null</tt> if it can't be done.
* If ! <tt>wantsStack</tt>: Integer: the number of roads needed, or 500 if it can't be done
*/
static Object recalcLongestRoadETAAux(SOCPlayer pl, final boolean wantsStack, final int startNode, final int pathLength, final int lrLength, final int searchDepth) {
// D.ebugPrintln("=== recalcLongestRoadETAAux("+Integer.toHexString(startNode)+","+pathLength+","+lrLength+","+searchDepth+")");
//
// We're doing a depth first search of all possible road paths.
// For similar code, see SOCPlayer.calcLongestRoad2
// Both methods rely on a stack holding NodeLenVis (pop to curNode in loop);
// they differ in actual element type within the stack because they are
// gathering slightly different results (length or a stack of edges).
//
int longest = 0;
int numRoads = 500;
Pair<NodeLenVis<Integer>, List<Integer>> bestPathNode = null;
final SOCBoard board = pl.getGame().getBoard();
Stack<Pair<NodeLenVis<Integer>, List<Integer>>> pending = new Stack<Pair<NodeLenVis<Integer>, List<Integer>>>();
// Holds as-yet unvisited nodes:
// Pair members are <NodeLenVis, null or node-coordinate list of all parents (from DFS traversal order)>.
// Lists have most-distant node at beginning (item 0), and most-immediate at end of list (n-1).
// That list is used at the end to build the returned Stack which is the road path needed.
pending.push(new Pair<NodeLenVis<Integer>, List<Integer>>(new NodeLenVis<Integer>(startNode, pathLength, new Vector<Integer>()), null));
while (!pending.empty()) {
final Pair<NodeLenVis<Integer>, List<Integer>> dataPair = pending.pop();
final NodeLenVis<Integer> curNode = dataPair.getA();
// D.ebugPrintln("curNode = "+curNode);
final int coord = curNode.node;
int len = curNode.len;
final Vector<Integer> visited = curNode.vis;
boolean pathEnd = false;
//
if (len > 0) {
final int pn = pl.getPlayerNumber();
SOCPlayingPiece p = board.settlementAtNode(coord);
if ((p != null) && (p.getPlayerNumber() != pn)) {
pathEnd = true;
// D.ebugPrintln("^^^ path end at "+Integer.toHexString(coord));
}
}
if (!pathEnd) {
//
// check if we've connected to another road graph of this player
//
Iterator<SOCLRPathData> lrPathsIter = pl.getLRPaths().iterator();
while (lrPathsIter.hasNext()) {
SOCLRPathData pathData = lrPathsIter.next();
if ((startNode != pathData.getBeginning()) && (startNode != pathData.getEnd()) && ((coord == pathData.getBeginning()) || (coord == pathData.getEnd()))) {
pathEnd = true;
len += pathData.getLength();
break;
}
}
}
if (!pathEnd) {
//
if ((len - pathLength) >= searchDepth) {
pathEnd = true;
}
// D.ebugPrintln("Reached search depth");
}
if (!pathEnd) {
/**
* For each of the 3 adjacent edges of coord's node,
* check for unvisited legal road possibilities.
* When they are found, push that edge's far-end node
* onto the pending stack.
*/
pathEnd = true;
for (int dir = 0; dir < 3; ++dir) {
int j = board.getAdjacentEdgeToNode(coord, dir);
if (pl.isLegalRoad(j)) {
final Integer edge = new Integer(j);
boolean match = false;
for (Enumeration<Integer> ev = visited.elements(); ev.hasMoreElements(); ) {
Integer vis = ev.nextElement();
if (vis.equals(edge)) {
match = true;
break;
}
}
if (!match) {
Vector<Integer> newVis = new Vector<Integer>(visited);
newVis.addElement(edge);
List<Integer> nodeParentList = dataPair.getB();
if (nodeParentList == null)
nodeParentList = new ArrayList<Integer>();
else
// clone before we add to it
nodeParentList = new ArrayList<Integer>(nodeParentList);
// curNode's coord will be parent to new pending element
nodeParentList.add(coord);
// edge's other node
j = board.getAdjacentNodeToNode(coord, dir);
pending.push(new Pair<NodeLenVis<Integer>, List<Integer>>(new NodeLenVis<Integer>(j, len + 1, newVis), nodeParentList));
pathEnd = false;
}
}
}
}
if (pathEnd) {
if (len > longest) {
longest = len;
numRoads = curNode.len - pathLength;
bestPathNode = dataPair;
} else if ((len == longest) && (curNode.len < numRoads)) {
numRoads = curNode.len - pathLength;
bestPathNode = dataPair;
}
}
}
if (!wantsStack) {
// As used by SOCPlayerTracker.
int rv;
if (longest > lrLength)
rv = numRoads;
else
rv = 500;
// <-- Early return: ! wantsStack ---
return new Integer(rv);
}
if ((longest > lrLength) && (bestPathNode != null)) {
// D.ebugPrintln("Converting nodes to road coords.");
//
// Return the path in a stack, with the last road (the one from bestPathNode) on top.
// Convert pairs of node coords to edge coords for roads.
// List is ordered from farthest parent at 0 to bestPathNode's parent at (n-1),
// so iterate same way to build the stack.
//
Stack<SOCPossibleRoad> path = new Stack<SOCPossibleRoad>();
int coordC, coordP;
List<Integer> nodeList = bestPathNode.getB();
if ((nodeList == null) || nodeList.isEmpty())
// <--- early return, no node list: should not happen ---
return null;
// append bestPathNode
nodeList.add(new Integer(bestPathNode.getA().node));
final int L = nodeList.size();
// root ancestor
coordP = nodeList.get(0);
for (int i = 1; i < L; ++i) {
coordC = nodeList.get(i);
path.push(new SOCPossibleRoad(pl, board.getEdgeBetweenAdjacentNodes(coordC, coordP), null));
coordP = coordC;
}
return path;
}
return null;
}
use of soc.game.SOCBoard in project JSettlers2 by jdmonin.
the class SOCDisplaylessPlayerClient method handleBOARDLAYOUT2.
/**
* handle the "board layout" message, new format
* @param games Games the client is playing, for method reuse by SOCPlayerClient
* @param mes the message
* @since 1.1.08
* @return True if game was found and layout understood, false otherwise
*/
public static boolean handleBOARDLAYOUT2(Map<String, SOCGame> games, SOCBoardLayout2 mes) {
SOCGame ga = games.get(mes.getGame());
if (ga == null)
return false;
SOCBoard bd = ga.getBoard();
final int bef = mes.getBoardEncodingFormat();
if (bef == SOCBoard.BOARD_ENCODING_LARGE) {
// v3
((SOCBoardLarge) bd).setLandHexLayout(mes.getIntArrayPart("LH"));
ga.setPlayersLandHexCoordinates();
int hex = mes.getIntPart("RH");
if (hex != 0)
bd.setRobberHex(hex, false);
hex = mes.getIntPart("PH");
if (hex != 0)
((SOCBoardLarge) bd).setPirateHex(hex, false);
int[] portLayout = mes.getIntArrayPart("PL");
if (portLayout != null)
bd.setPortsLayout(portLayout);
int[] x = mes.getIntArrayPart("PX");
if (x != null)
((SOCBoardLarge) bd).setPlayerExcludedLandAreas(x);
x = mes.getIntArrayPart("RX");
if (x != null)
((SOCBoardLarge) bd).setRobberExcludedLandAreas(x);
x = mes.getIntArrayPart("CV");
if (x != null)
((SOCBoardLarge) bd).setVillageAndClothLayout(x);
x = mes.getIntArrayPart("LS");
if (x != null)
((SOCBoardLarge) bd).addLoneLegalSettlements(ga, x);
HashMap<String, int[]> others = mes.getAddedParts();
if (others != null)
((SOCBoardLarge) bd).setAddedLayoutParts(others);
} else if (bef <= SOCBoard.BOARD_ENCODING_6PLAYER) {
// v1 or v2
bd.setHexLayout(mes.getIntArrayPart("HL"));
bd.setNumberLayout(mes.getIntArrayPart("NL"));
bd.setRobberHex(mes.getIntPart("RH"), false);
int[] portLayout = mes.getIntArrayPart("PL");
if (portLayout != null)
bd.setPortsLayout(portLayout);
} else {
// Should not occur: Server has sent an unrecognized format
System.err.println("Cannot recognize game encoding v" + bef + " for game " + ga.getName());
return false;
}
ga.updateAtBoardLayout();
return true;
}
use of soc.game.SOCBoard in project JSettlers2 by jdmonin.
the class SOCPlayerTracker method updateLRPotential.
/**
* update the potential LR value of a possible road or ship
* by placing dummy roads/ships and calculating LR (longest road).
* If <tt>level</tt> > 0, add the new roads or ships adjacent
* to <tt>dummy</tt> and recurse.
*
* @param posRoad the possible road or ship
* @param dummy the dummy player
* @param lrLength the current LR length
* @param level how many levels of recursion, or 0 to not recurse
*/
public void updateLRPotential(SOCPossibleRoad posRoad, SOCPlayer dummy, SOCRoad dummyRoad, final int lrLength, final int level) {
// D.ebugPrintln("$$$ updateLRPotential for road at "+Integer.toHexString(posRoad.getCoordinates())+" level="+level);
//
// if we've reached the bottom level of recursion,
// or if there are no more roads to place from this one.
// then calc potential LR value
//
SOCBoard board = game.getBoard();
boolean noMoreExpansion;
if (level <= 0) {
noMoreExpansion = true;
} else {
noMoreExpansion = false;
Enumeration<Integer> adjEdgeEnum = board.getAdjacentEdgesToEdge(dummyRoad.getCoordinates()).elements();
while (adjEdgeEnum.hasMoreElements()) {
final int adjEdge = adjEdgeEnum.nextElement().intValue();
if ((dummyRoad.isRoadNotShip() && dummy.isPotentialRoad(adjEdge)) || ((!dummyRoad.isRoadNotShip()) && dummy.isPotentialShip(adjEdge))) {
noMoreExpansion = false;
break;
}
}
}
if (noMoreExpansion) {
//
// only update the potential LR if it's bigger than the
// current value
//
int newPotentialLRValue = dummy.calcLongestRoad2() - lrLength;
// D.ebugPrintln("$$$ newPotentialLRValue = "+newPotentialLRValue);
if (newPotentialLRValue > posRoad.getLRPotential()) {
posRoad.setLRPotential(newPotentialLRValue);
}
} else {
//
// we need to add new roads/ships adjacent to dummyRoad, and recurse
//
Enumeration<Integer> adjEdgeEnum = board.getAdjacentEdgesToEdge(dummyRoad.getCoordinates()).elements();
while (adjEdgeEnum.hasMoreElements()) {
final int adjEdge = adjEdgeEnum.nextElement().intValue();
if ((dummyRoad.isRoadNotShip() && dummy.isPotentialRoad(adjEdge)) || ((!dummyRoad.isRoadNotShip()) && dummy.isPotentialShip(adjEdge))) {
SOCRoad newDummyRoad;
if (dummyRoad.isRoadNotShip())
newDummyRoad = new SOCRoad(dummy, adjEdge, board);
else
newDummyRoad = new SOCShip(dummy, adjEdge, board);
dummy.putPiece(newDummyRoad, true);
updateLRPotential(posRoad, dummy, newDummyRoad, lrLength, level - 1);
dummy.removePiece(newDummyRoad, null);
}
}
}
}
use of soc.game.SOCBoard in project JSettlers2 by jdmonin.
the class SOCPlayerTracker method expandRoadOrShip.
/**
* Expand a possible road or ship, to see what placements it makes possible.
*<UL>
*<LI> Creates {@code dummyRoad}: A copy of {@code targetRoad} owned by {@code dummy}
*<LI> Calls {@link SOCPlayer#putPiece(SOCPlayingPiece, boolean) dummy.putPiece(dummyRoad, true)}
*<LI> Adds to or updates {@link #possibleSettlements} at <tt>targetRoad</tt>'s nodes, if potential
*<LI> If {@code level > 0}: Calls itself recursively to go more levels out from the current pieces,
* adding/updating {@link #possibleRoads} and {@link #possibleSettlements}
*<LI> Calls {@link SOCPlayer#removePiece(SOCPlayingPiece, SOCPlayingPiece) dummy.removePiece(dummyRoad, null)}
*</UL>
*<P>
* <b>Scenario {@code _SC_PIRI}</b>: Ships in this scenario never expand east (never away from the
* pirate fortress). Scenario rules require the route to be as short as possible. Even if another (human)
* player might want to do so, they couldn't interfere with the bot's own route, so we don't track
* that possibility.
*
* @param targetRoad the possible road
* @param player the player who owns the original road
* @param dummy the dummy player used to see what's legal; created by caller copying {@code player}
* @param trackers player trackers
* @param level how many levels (additional pieces) to expand;
* 0 to only check <tt>targetRoad</tt> for potential settlements
* and not expand past it for new roads, ships, or further settlements.
* If {@code level > 0} but {@code dummy} has no more roads or ships
* (depending on {@link SOCPossibleRoad#isRoadNotShip() targetRoad.isRoadNotShip()}),
* acts as if {@code level == 0}.
*/
public void expandRoadOrShip(final SOCPossibleRoad targetRoad, final SOCPlayer player, final SOCPlayer dummy, HashMap<Integer, SOCPlayerTracker> trackers, final int level) {
// D.ebugPrintln("$$$ expandRoad at "+Integer.toHexString(targetRoad.getCoordinates())+" level="+level);
final SOCBoard board = game.getBoard();
final int tgtRoadEdge = targetRoad.getCoordinates();
final boolean isRoadNotShip = targetRoad.isRoadNotShip();
final SOCRoad dummyRoad;
if (isRoadNotShip || ((targetRoad instanceof SOCPossibleShip) && ((SOCPossibleShip) targetRoad).isCoastalRoadAndShip))
dummyRoad = new SOCRoad(dummy, tgtRoadEdge, board);
else
// TODO better handling for coastal roads/ships
dummyRoad = new SOCShip(dummy, tgtRoadEdge, board);
dummy.putPiece(dummyRoad, true);
//
for (Integer adjNode : board.getAdjacentNodesToEdge(tgtRoadEdge)) {
if (dummy.canPlaceSettlement(adjNode.intValue())) {
//
// see if possible settlement is already in the list
//
// D.ebugPrintln("$$$ seeing if "+Integer.toHexString(adjNode.intValue())+" is already in the list");
SOCPossibleSettlement posSet = possibleSettlements.get(adjNode);
if (posSet != null) {
//
if (!(posSet.getNecessaryRoads().isEmpty() || posSet.getNecessaryRoads().contains(targetRoad))) {
//
// add target road to settlement's nr list and this settlement to the road's np list
//
// D.ebugPrintln("$$$ adding road "+Integer.toHexString(targetRoad.getCoordinates())+" to the settlement "+Integer.toHexString(posSet.getCoordinates()));
posSet.addNecessaryRoad(targetRoad);
targetRoad.addNewPossibility(posSet);
//
if ((targetRoad.getNumberOfNecessaryRoads() + 1) < posSet.getNumberOfNecessaryRoads()) {
posSet.setNumberOfNecessaryRoads(targetRoad.getNumberOfNecessaryRoads() + 1);
}
}
} else {
//
// else, add new possible settlement
//
// D.ebugPrintln("$$$ adding new possible settlement at "+Integer.toHexString(adjNode.intValue()));
List<SOCPossibleRoad> nr = new ArrayList<SOCPossibleRoad>();
nr.add(targetRoad);
SOCPossibleSettlement newPosSet = new SOCPossibleSettlement(player, adjNode.intValue(), nr);
newPosSet.setNumberOfNecessaryRoads(targetRoad.getNumberOfNecessaryRoads() + 1);
possibleSettlements.put(adjNode, newPosSet);
targetRoad.addNewPossibility(newPosSet);
updateSettlementConflicts(newPosSet, trackers);
}
}
}
if ((level > 0) && (0 < dummy.getNumPieces(isRoadNotShip ? SOCPlayingPiece.ROAD : SOCPlayingPiece.SHIP))) {
//
// check for new possible roads or ships.
// The above getNumPieces check ignores any possible ship <-> road transition at a coastal settlement.
//
ArrayList<SOCPossibleRoad> newPossibleRoads = new ArrayList<SOCPossibleRoad>();
ArrayList<SOCPossibleRoad> roadsToExpand = new ArrayList<SOCPossibleRoad>();
// ships in _SC_PIRI never expand east
final boolean isShipInSC_PIRI = (!isRoadNotShip) && game.isGameOptionSet(SOCGameOption.K_SC_PIRI);
// D.ebugPrintln("$$$ checking roads adjacent to "+Integer.toHexString(targetRoad.getCoordinates()));
//
// check adjacent edges to road or ship
//
Enumeration<Integer> adjEdgesEnum = board.getAdjacentEdgesToEdge(tgtRoadEdge).elements();
while (adjEdgesEnum.hasMoreElements()) {
Integer adjEdge = adjEdgesEnum.nextElement();
final int edge = adjEdge.intValue();
if (isShipInSC_PIRI) {
final int tgtEdgeCol = tgtRoadEdge & 0xFF, adjEdgeCol = edge & 0xFF;
if (// adjacent goes north/south from eastern node of diagonal target edge
(adjEdgeCol > tgtEdgeCol) || ((adjEdgeCol == tgtEdgeCol) && ((tgtRoadEdge & 0x100) != 0))) // adjacent goes northeast/southeast from vertical target edge (tgtRoadEdge is on odd row)
{
// <--- Ignore this eastern adjacent edge ---
continue;
}
}
// D.ebugPrintln("$$$ edge "+Integer.toHexString(adjEdge.intValue())+" is legal:"+dummy.isPotentialRoad(adjEdge.intValue()));
//
// see if edge is a potential road
// or ship to continue this route
//
boolean edgeIsPotentialRoute = (isRoadNotShip) ? dummy.isPotentialRoad(edge) : dummy.isPotentialShip(edge);
// If true, this edge transitions
// between ships <-> roads, at a
// coastal settlement
boolean edgeRequiresCoastalSettlement = false;
if ((!edgeIsPotentialRoute) && game.hasSeaBoard) {
// Determine if can transition ship <-> road
// at a coastal settlement
final int nodeBetween = ((SOCBoardLarge) board).getNodeBetweenAdjacentEdges(tgtRoadEdge, edge);
if (dummy.canPlaceSettlement(nodeBetween)) {
// check opposite type at transition
edgeIsPotentialRoute = (isRoadNotShip) ? dummy.isPotentialShip(edge) : dummy.isPotentialRoad(edge);
if (edgeIsPotentialRoute)
edgeRequiresCoastalSettlement = true;
}
}
if (edgeIsPotentialRoute) {
// Add 1 to road distance, unless
// it requires a coastal settlement
// (extra effort to build that)
final int incrDistance = edgeRequiresCoastalSettlement ? 3 : 1;
//
// see if possible road is already in the list
//
SOCPossibleRoad pr = possibleRoads.get(adjEdge);
if (pr != null) {
// For now, can't differ along a coastal route.
if (edgeRequiresCoastalSettlement && (isRoadNotShip != pr.isRoadNotShip())) {
// <--- road vs ship mismatch ---
continue;
}
//
// if so, and it needs 1 or more roads other than this one,
//
// D.ebugPrintln("$$$ pr "+Integer.toHexString(pr.getCoordinates())+" already in list");
List<SOCPossibleRoad> nr = pr.getNecessaryRoads();
if (!(nr.isEmpty() || nr.contains(targetRoad))) {
//
// add the target road to its nr list and the new road to the target road's np list
//
// D.ebugPrintln("$$$ adding "+Integer.toHexString(targetRoad.getCoordinates())+" to nr list");
nr.add(targetRoad);
targetRoad.addNewPossibility(pr);
//
if ((targetRoad.getNumberOfNecessaryRoads() + incrDistance) < pr.getNumberOfNecessaryRoads()) {
pr.setNumberOfNecessaryRoads(targetRoad.getNumberOfNecessaryRoads() + incrDistance);
}
}
if (!pr.hasBeenExpanded()) {
roadsToExpand.add(pr);
pr.setExpandedFlag();
}
} else {
//
// else, add new possible road or ship
//
// D.ebugPrintln("$$$ adding new pr at "+Integer.toHexString(adjEdge.intValue()));
ArrayList<SOCPossibleRoad> neededRoads = new ArrayList<SOCPossibleRoad>();
neededRoads.add(targetRoad);
SOCPossibleRoad newPR;
boolean isRoad = isRoadNotShip;
if (edgeRequiresCoastalSettlement)
isRoad = !isRoad;
// use coastal road/ship type (isCoastalRoadAndShip) only if the road/ship
// being expanded is coastal, or if we can require a coastal settlement to
// switch from road-only or ship-only
final boolean isCoastal = dummy.isPotentialRoad(edge) && dummy.isPotentialShip(edge) && (edgeRequiresCoastalSettlement || ((targetRoad instanceof SOCPossibleShip) && ((SOCPossibleShip) targetRoad).isCoastalRoadAndShip));
if (isRoad && !isCoastal) {
newPR = new SOCPossibleRoad(player, edge, neededRoads);
} else {
newPR = new SOCPossibleShip(player, edge, isCoastal, neededRoads);
System.err.println("L1072: " + toString() + ": new PossibleShip(" + isCoastal + ") at 0x" + Integer.toHexString(edge));
}
newPR.setNumberOfNecessaryRoads(targetRoad.getNumberOfNecessaryRoads() + incrDistance);
targetRoad.addNewPossibility(newPR);
newPossibleRoads.add(newPR);
roadsToExpand.add(newPR);
newPR.setExpandedFlag();
}
}
}
//
for (SOCPossibleRoad newPR : newPossibleRoads) {
possibleRoads.put(Integer.valueOf(newPR.getCoordinates()), newPR);
}
//
for (SOCPossibleRoad expandPR : roadsToExpand) {
expandRoadOrShip(expandPR, player, dummy, trackers, level - 1);
}
}
//
// remove the dummy road
//
dummy.removePiece(dummyRoad, null);
}
use of soc.game.SOCBoard in project JSettlers2 by jdmonin.
the class RobberStrategy method getBestRobberHex.
/**
* Determine the best hex to move the robber.
*/
public static int getBestRobberHex(SOCGame game, SOCPlayer ourPlayerData, HashMap<Integer, SOCPlayerTracker> playerTrackers, Random rand) {
log.debug("%%% MOVEROBBER");
final int[] hexes = game.getBoard().getLandHexCoords();
final int prevRobberHex = game.getBoard().getRobberHex();
/**
* decide which player we want to thwart
*/
int[] winGameETAs = new int[game.maxPlayers];
for (int i = game.maxPlayers - 1; i >= 0; --i) winGameETAs[i] = 100;
Iterator<SOCPlayerTracker> trackersIter = playerTrackers.values().iterator();
while (trackersIter.hasNext()) {
SOCPlayerTracker tracker = trackersIter.next();
final int trackerPN = tracker.getPlayer().getPlayerNumber();
log.debug("%%%%%%%%% TRACKER FOR PLAYER " + trackerPN);
try {
tracker.recalcWinGameETA();
winGameETAs[trackerPN] = tracker.getWinGameETA();
log.debug("winGameETA = " + tracker.getWinGameETA());
} catch (NullPointerException e) {
log.debug("Null Pointer Exception calculating winGameETA");
winGameETAs[trackerPN] = 500;
}
}
final int ourPlayerNumber = ourPlayerData.getPlayerNumber();
int victimNum = -1;
for (int pnum = 0; pnum < game.maxPlayers; pnum++) {
if (game.isSeatVacant(pnum))
continue;
if ((victimNum < 0) && (pnum != ourPlayerNumber)) {
// The first pick
log.debug("Picking a robber victim: pnum=" + pnum);
victimNum = pnum;
} else if ((pnum != ourPlayerNumber) && (winGameETAs[pnum] < winGameETAs[victimNum])) {
// A better pick
log.debug("Picking a better robber victim: pnum=" + pnum);
victimNum = pnum;
}
}
// Postcondition: victimNum != -1 due to "First pick" in loop.
/**
* figure out the best way to thwart that player
*/
SOCPlayer victim = game.getPlayer(victimNum);
SOCBuildingSpeedEstimate estimate = new SOCBuildingSpeedEstimate();
int bestHex = prevRobberHex;
int worstSpeed = 0;
// can't move robber to desert
final boolean skipDeserts = game.isGameOptionSet("RD");
SOCBoard gboard = (skipDeserts ? game.getBoard() : null);
for (int i = 0; i < hexes.length; i++) {
/**
* only check hexes that we're not touching,
* and not the robber hex, and possibly not desert hexes
*/
if ((hexes[i] != prevRobberHex) && ourPlayerData.getNumbers().hasNoResourcesForHex(hexes[i]) && !(skipDeserts && (gboard.getHexTypeFromCoord(hexes[i]) == SOCBoard.DESERT_HEX))) {
estimate.recalculateEstimates(victim.getNumbers(), hexes[i]);
int[] speeds = estimate.getEstimatesFromNothingFast(victim.getPortFlags());
int totalSpeed = 0;
for (int j = SOCBuildingSpeedEstimate.MIN; j < SOCBuildingSpeedEstimate.MAXPLUSONE; j++) {
totalSpeed += speeds[j];
}
log.debug("total Speed = " + totalSpeed);
if (totalSpeed > worstSpeed) {
bestHex = hexes[i];
worstSpeed = totalSpeed;
log.debug("bestHex = " + Integer.toHexString(bestHex));
log.debug("worstSpeed = " + worstSpeed);
}
}
}
log.debug("%%% bestHex = " + Integer.toHexString(bestHex));
/**
* Pick a spot at random if we can't decide.
* Don't pick deserts if the game option is set.
* Don't pick one of our hexes if at all possible.
* It's not likely we'll need to pick one of our hexes
* (we try 30 times to avoid it), so there isn't code here
* to pick the 'least bad' one.
* (TODO) consider that: It would be late in the game
* if the board's that crowded with pieces.
* Use similar algorithm as picking for opponent,
* but apply it worst vs best.
*/
if (bestHex == prevRobberHex) {
int numRand = 0;
while ((bestHex == prevRobberHex) || (skipDeserts && (gboard.getHexTypeFromCoord(bestHex) == SOCBoard.DESERT_HEX)) || ((numRand < 30) && ourPlayerData.getNumbers().hasNoResourcesForHex(bestHex))) {
bestHex = hexes[Math.abs(rand.nextInt()) % hexes.length];
log.debug("%%% random pick = " + Integer.toHexString(bestHex));
++numRand;
}
}
return bestHex;
}
Aggregations