A few changes:

1. Make sure a tile or piecen submitted is submitted by the current turn
holder.

2. Wait to end the turn until after we've received the elementAdded
notification for piecen placement and done the scoring. This ensures that
all events that need to be dispatched before the turn ends are dispatched
and that piecen removal is completed before we end the game and
potentially do more scoring.

3. When scoring a tile, always use an array that we can modify during the
scoring process to ensure that we don't score a claim group twice by
virtue of it being completed by a tile with two of the same feature, both
of which are linked into the same claim group. By removing piecens from
the array immediately, we can tell that we scored a claim group the first
time because the piecens belonging to the claim group will have been
removed from the local array whereas they would not yet have been removed
from the distributed set by virtue of the element removed events not yet
having been dispatched. Distributed programming is tricky.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@437 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2001-11-07 10:42:24 +00:00
parent c87ecd0eb3
commit d23fa8e8a7
@@ -1,5 +1,5 @@
// //
// $Id: AtlantiManager.java,v 1.16 2001/10/18 23:57:02 mdb Exp $ // $Id: AtlantiManager.java,v 1.17 2001/11/07 10:42:24 mdb Exp $
package com.threerings.venison; package com.threerings.venison;
@@ -139,19 +139,13 @@ public class VenisonManager
{ {
super.gameDidEnd(); super.gameDidEnd();
// create a piecen array that we can manipulate while scoring
Piecen[] piecens = new Piecen[_venobj.piecens.size()];
Iterator iter = _venobj.piecens.elements();
for (int i = 0; iter.hasNext(); i++) {
piecens[i] = (Piecen)iter.next();
}
// compute the final scores by iterating over each tile and // compute the final scores by iterating over each tile and
// scoring its features // scoring its features
iter = _venobj.tiles.elements(); Piecen[] piecens = getPiecens();
Iterator iter = _venobj.tiles.elements();
while (iter.hasNext()) { while (iter.hasNext()) {
VenisonTile tile = (VenisonTile)iter.next(); VenisonTile tile = (VenisonTile)iter.next();
scoreFeatures(tile, piecens); scoreFeatures(tile, piecens, true);
} }
// lastly, we have to score the farms (cue the ominous drums)... // lastly, we have to score the farms (cue the ominous drums)...
@@ -161,19 +155,34 @@ public class VenisonManager
_venobj.setScores(_venobj.scores); _venobj.setScores(_venobj.scores);
} }
/**
* Creates an array of piecens based on the contents of the piecens
* set in the game object, suitable for passing to {@link
* #scoreFeatures}.
*/
protected Piecen[] getPiecens ()
{
// create a piecen array that we can manipulate while scoring
Piecen[] piecens = new Piecen[_venobj.piecens.size()];
Iterator iter = _venobj.piecens.elements();
for (int i = 0; iter.hasNext(); i++) {
piecens[i] = (Piecen)iter.next();
}
return piecens;
}
/** /**
* Scores the features on this tile. * Scores the features on this tile.
* *
* @param tile the tile whose features should be scored. * @param tile the tile whose features should be scored.
* @param piecens during the final tally, we don't look for piecens on * @param piecens an array of the pieces on the board which we can
* the board and remove them when we score them, we instead look for * manipulate directly without having to wait for element removed
* the piecens in this supplied array and remove them from there both * events to be dispatched.
* to preserve the appearance of the board during final scoring and to * @param finalTally during the final tally, we score differently and
* circumvent the need to process an element removed event every time * we don't remove piecens from the board as we score them.
* we remove a piecen. Passing a non-null piecens array also signifies
* that we are doing the final tally rather than an incremental score.
*/ */
protected void scoreFeatures (VenisonTile tile, Piecen[] piecens) protected void scoreFeatures (
VenisonTile tile, Piecen[] piecens, boolean finalTally)
{ {
// potentially score all features on the tile // potentially score all features on the tile
for (int i = 0; i < tile.features.length; i++) { for (int i = 0; i < tile.features.length; i++) {
@@ -203,7 +212,7 @@ public class VenisonManager
// if the score is positive, it's a completed feature and we // if the score is positive, it's a completed feature and we
// score it regardless, we score incomplete features only // score it regardless, we score incomplete features only
// during the final tally // during the final tally
if (score > 0 || piecens != null) { if (score > 0 || finalTally) {
String qual = (score > 0) ? "Completed" : "Incomplete"; String qual = (score > 0) ? "Completed" : "Incomplete";
// convert the score into a positive value // convert the score into a positive value
@@ -231,12 +240,12 @@ public class VenisonManager
Log.info("New scores: " + StringUtil.toString(_venobj.scores)); Log.info("New scores: " + StringUtil.toString(_venobj.scores));
// broadcast the new scores if this isn't the final tally // broadcast the new scores if this isn't the final tally
if (piecens == null) { if (!finalTally) {
_venobj.setScores(_venobj.scores); _venobj.setScores(_venobj.scores);
} }
// and free up the scored piecens // and free up the scored piecens
removePiecens(cgroup, piecens); removePiecens(cgroup, piecens, finalTally);
} else { } else {
// Log.info("Not scoring incomplete feature " + // Log.info("Not scoring incomplete feature " +
@@ -287,7 +296,7 @@ public class VenisonManager
// if it's completed or if we're doing the final // if it's completed or if we're doing the final
// tally, we score it // tally, we score it
if (score > 0 || piecens != null) { if (score > 0 || finalTally) {
String qual = (score > 0) ? String qual = (score > 0) ?
"complete" : "incomplete"; "complete" : "incomplete";
@@ -306,13 +315,13 @@ public class VenisonManager
// only broadcast the updated scores if this isn't // only broadcast the updated scores if this isn't
// the final tally // the final tally
if (piecens == null) { if (!finalTally) {
_venobj.setScores(_venobj.scores); _venobj.setScores(_venobj.scores);
} }
// and clear out the piecen (only removing it from // and clear out the piecen (only removing it from
// the piecen set if we're not in the final tally) // the piecen set if we're not in the final tally)
removePiecen(p, piecens == null); removePiecen(p, !finalTally);
} }
} }
} }
@@ -442,10 +451,12 @@ public class VenisonManager
// now report the scoring and transfer the counts to the score // now report the scoring and transfer the counts to the score
// array // array
for (int i = 0; i < _players.length; i++) { for (int i = 0; i < _players.length; i++) {
_venobj.scores[i] += cityScores[i]; if (cityScores[i] > 0) {
String message = _players[i] + " scores " + cityScores[i] + _venobj.scores[i] += cityScores[i];
" points for farmed cities."; String message = _players[i] + " scores " + cityScores[i] +
ChatProvider.sendSystemMessage(_venobj.getOid(), message); " points for farmed cities.";
ChatProvider.sendSystemMessage(_venobj.getOid(), message);
}
} }
} }
@@ -460,8 +471,7 @@ public class VenisonManager
* returned previously. * returned previously.
* *
* @param claimGroup the claim group that we're scoring. * @param claimGroup the claim group that we're scoring.
* @param piecens an array to use when looking for matching piecens * @param piecens an array to use when looking for matching piecens.
* instead of looking at the piecens set in the game object.
* *
* @return an array for the specified claim group or null if no * @return an array for the specified claim group or null if no
* players have a piecen claiming the specified claim group. * players have a piecen claiming the specified claim group.
@@ -473,30 +483,15 @@ public class VenisonManager
// iterate over the piecens // iterate over the piecens
int max = 0; int max = 0;
if (piecens == null) { for (int i = 0; i < piecens.length; i++) {
Iterator iter = _venobj.piecens.elements(); Piecen piecen = piecens[i];
while (iter.hasNext()) { if (piecen == null) {
Piecen piecen = (Piecen)iter.next(); continue;
if (piecen.claimGroup == claimGroup) { } else if (piecen.claimGroup == claimGroup) {
// color == player index... somewhat sketchy // color == player index... somewhat sketchy
if (++_claimGroupVector[piecen.owner] > max) { if (++_claimGroupVector[piecen.owner] > max) {
// keep track of the highest scorer // keep track of the highest scorer
max = _claimGroupVector[piecen.owner]; max = _claimGroupVector[piecen.owner];
}
}
}
} else {
for (int i = 0; i < piecens.length; i++) {
Piecen piecen = piecens[i];
if (piecen == null) {
continue;
} else if (piecen.claimGroup == claimGroup) {
// color == player index... somewhat sketchy
if (++_claimGroupVector[piecen.owner] > max) {
// keep track of the highest scorer
max = _claimGroupVector[piecen.owner];
}
} }
} }
} }
@@ -513,9 +508,20 @@ public class VenisonManager
* Removes piecens either from the supplied array or from the game * Removes piecens either from the supplied array or from the game
* object piecen set if no array is supplied. * object piecen set if no array is supplied.
*/ */
protected void removePiecens (int claimGroup, Piecen[] piecens) protected void removePiecens (int claimGroup, Piecen[] piecens,
boolean finalTally)
{ {
if (piecens == null) { // we always clear the piecens from the array
for (int i = 0; i < piecens.length; i++) {
if (piecens[i] == null) {
continue;
} else if (piecens[i].claimGroup == claimGroup) {
piecens[i] = null;
}
}
// if this isn't the final tally, we also clear 'em from the board
if (!finalTally) {
Iterator iter = _venobj.piecens.elements(); Iterator iter = _venobj.piecens.elements();
while (iter.hasNext()) { while (iter.hasNext()) {
Piecen p = (Piecen)iter.next(); Piecen p = (Piecen)iter.next();
@@ -523,15 +529,6 @@ public class VenisonManager
removePiecen(p, true); removePiecen(p, true);
} }
} }
} else {
for (int i = 0; i < piecens.length; i++) {
if (piecens[i] == null) {
continue;
} else if (piecens[i].claimGroup == claimGroup) {
piecens[i] = null;
}
}
} }
} }
@@ -582,8 +579,11 @@ public class VenisonManager
} else { } else {
// check to see if we added the piecen to a completed // check to see if we added the piecen to a completed
// feature, in which case we score and remove it // feature, in which case we score and remove it
scoreFeatures(tile, null); scoreFeatures(tile, getPiecens(), false);
} }
// now that we've scored the piecen, we can end the turn
endTurn();
} }
} }
@@ -603,9 +603,16 @@ public class VenisonManager
public void handleEvent (MessageEvent event) public void handleEvent (MessageEvent event)
{ {
VenisonTile tile = (VenisonTile)event.getArgs()[0]; VenisonTile tile = (VenisonTile)event.getArgs()[0];
int pidx = getTurnHolderIndex();
// make sure it's this player's turn
if (_playerOids[pidx] != event.getSourceOid()) {
Log.warning("Requested to place tile by non-turn holder " +
"[event=" + event +
", turnHolder=" + _venobj.turnHolder + "].");
// make sure this is a valid placement // make sure this is a valid placement
if (TileUtil.isValidPlacement(_tiles, tile)) { } else if (TileUtil.isValidPlacement(_tiles, tile)) {
// add the tile to the list and resort it // add the tile to the list and resort it
_tiles.add(tile); _tiles.add(tile);
Collections.sort(_tiles); Collections.sort(_tiles);
@@ -618,14 +625,13 @@ public class VenisonManager
// placing a piece may have completed road or city // placing a piece may have completed road or city
// features. if it did, we score them now // features. if it did, we score them now
scoreFeatures(tile, null); scoreFeatures(tile, getPiecens(), false);
Log.info("Placed tile " + tile + "."); Log.info("Placed tile " + tile + ".");
// if the player has no free piecens or if there are no // if the player has no free piecens or if there are no
// unclaimed features on this tile, we end their turn // unclaimed features on this tile, we end their turn
// straight away // straight away
int pidx = getTurnHolderIndex();
int pcount = TileUtil.countPiecens(_tiles, pidx); int pcount = TileUtil.countPiecens(_tiles, pidx);
if (pcount >= PIECENS_PER_PLAYER || if (pcount >= PIECENS_PER_PLAYER ||
!tile.hasUnclaimedFeature()) { !tile.hasUnclaimedFeature()) {
@@ -648,8 +654,14 @@ public class VenisonManager
int pidx = getTurnHolderIndex(); int pidx = getTurnHolderIndex();
int pcount = TileUtil.countPiecens(_venobj.piecens, pidx); int pcount = TileUtil.countPiecens(_venobj.piecens, pidx);
// make sure it's this player's turn
if (_playerOids[pidx] != event.getSourceOid()) {
Log.warning("Requested to place piecen by non-turn holder " +
"[event=" + event +
", turnHolder=" + _venobj.turnHolder + "].");
// do some checking before we place the piecen // do some checking before we place the piecen
if (pcount >= PIECENS_PER_PLAYER) { } else if (pcount >= PIECENS_PER_PLAYER) {
Log.warning("Requested to place piecen for player that " + Log.warning("Requested to place piecen for player that " +
"has all of their piecens in play " + "has all of their piecens in play " +
"[event=" + event + "]."); "[event=" + event + "].");
@@ -667,12 +679,11 @@ public class VenisonManager
// claim groups // claim groups
tile.setPiecen(piecen, _tiles); tile.setPiecen(piecen, _tiles);
// and add the piecen to the game object // and add the piecen to the game object. when we receive
// the piecen added event, we'll score it and then end the
// turn
_venobj.addToPiecens(piecen); _venobj.addToPiecens(piecen);
} }
// end the turn
endTurn();
} }
} }