Initial code to compute scores and to handle incremental scoring.
git-svn-id: https://samskivert.googlecode.com/svn/trunk@364 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: AtlantiBoard.java,v 1.8 2001/10/17 02:19:54 mdb Exp $
|
// $Id: AtlantiBoard.java,v 1.9 2001/10/17 04:34:14 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
@@ -47,6 +47,14 @@ public class VenisonBoard
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the piecen color to use when creating new piecens.
|
||||||
|
*/
|
||||||
|
public void setNewPiecenColor (int color)
|
||||||
|
{
|
||||||
|
_newPiecenColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the tiles to be displayed by this board. Any previous tiles
|
* Sets the tiles to be displayed by this board. Any previous tiles
|
||||||
* are forgotten and the new tiles are initialized according to their
|
* are forgotten and the new tiles are initialized according to their
|
||||||
@@ -141,6 +149,30 @@ public class VenisonBoard
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a piecen is removed (after scoring it), this method should be
|
||||||
|
* called to update the board display.
|
||||||
|
*/
|
||||||
|
public void clearPiecen (Object key)
|
||||||
|
{
|
||||||
|
// locate the tile associated with this piecen key
|
||||||
|
int tsize = _tiles.size();
|
||||||
|
for (int i = 0; i < tsize; i++) {
|
||||||
|
VenisonTile tile = (VenisonTile)_tiles.get(i);
|
||||||
|
if (tile.getKey().equals(key)) {
|
||||||
|
// clear the piecen out of the tile
|
||||||
|
tile.clearPiecen();
|
||||||
|
// and repaint
|
||||||
|
repaint();
|
||||||
|
// and get on out
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.warning("Requested to clear piecen for which we could " +
|
||||||
|
"find no associated tile! [key=" + key + "].");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the tile to be placed on the board. The tile will be displayed
|
* Sets the tile to be placed on the board. The tile will be displayed
|
||||||
* in the square under the mouse cursor where it can be legally placed
|
* in the square under the mouse cursor where it can be legally placed
|
||||||
@@ -264,7 +296,7 @@ public class VenisonBoard
|
|||||||
if (_placedTile.claims[fidx] == 0) {
|
if (_placedTile.claims[fidx] == 0) {
|
||||||
if (_placedTile.piecen == null ||
|
if (_placedTile.piecen == null ||
|
||||||
_placedTile.piecen.featureIndex != fidx) {
|
_placedTile.piecen.featureIndex != fidx) {
|
||||||
Piecen p = new Piecen(Piecen.BLUE, 0, 0, fidx);
|
Piecen p = new Piecen(_newPiecenColor, 0, 0, fidx);
|
||||||
_placedTile.setPiecen(p, null);
|
_placedTile.setPiecen(p, null);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
@@ -493,16 +525,17 @@ public class VenisonBoard
|
|||||||
VenisonBoard board = new VenisonBoard();
|
VenisonBoard board = new VenisonBoard();
|
||||||
|
|
||||||
TestDSet set = new TestDSet();
|
TestDSet set = new TestDSet();
|
||||||
set.addTile(new VenisonTile(CITY_TWO, false, WEST, 0, 0));
|
set.addTile(new VenisonTile(CITY_TWO, true, WEST, 0, 0));
|
||||||
set.addTile(new VenisonTile(CITY_TWO, false, WEST, -1, 1));
|
set.addTile(new VenisonTile(CITY_TWO, false, WEST, -1, 1));
|
||||||
set.addTile(new VenisonTile(CITY_TWO, false, WEST, -1, -1));
|
set.addTile(new VenisonTile(CITY_ONE, false, SOUTH, -1, -1));
|
||||||
set.addTile(new VenisonTile(CURVED_ROAD, false, WEST, 0, 2));
|
VenisonTile zero = new VenisonTile(CURVED_ROAD, false, WEST, 0, 2);
|
||||||
|
set.addTile(zero);
|
||||||
VenisonTile one = new VenisonTile(TWO_CITY_TWO, false, NORTH, 0, 1);
|
VenisonTile one = new VenisonTile(TWO_CITY_TWO, false, NORTH, 0, 1);
|
||||||
set.addTile(one);
|
set.addTile(one);
|
||||||
set.addTile(new VenisonTile(CITY_THREE, false, WEST, 1, 1));
|
set.addTile(new VenisonTile(CITY_THREE, false, WEST, 1, 1));
|
||||||
set.addTile(new VenisonTile(CITY_THREE_ROAD, false, EAST, 1, 2));
|
set.addTile(new VenisonTile(CITY_THREE_ROAD, false, EAST, 1, 2));
|
||||||
set.addTile(new VenisonTile(CITY_THREE, false, NORTH, -1, 0));
|
set.addTile(new VenisonTile(CITY_THREE, false, NORTH, -1, 0));
|
||||||
VenisonTile two = new VenisonTile(CITY_FOUR, false, NORTH, -2, 0);
|
VenisonTile two = new VenisonTile(CITY_ONE, false, EAST, -2, 0);
|
||||||
set.addTile(two);
|
set.addTile(two);
|
||||||
board.setTiles(set);
|
board.setTiles(set);
|
||||||
|
|
||||||
@@ -514,8 +547,18 @@ public class VenisonBoard
|
|||||||
CollectionUtil.addAll(tiles, set.elements());
|
CollectionUtil.addAll(tiles, set.elements());
|
||||||
Collections.sort(tiles);
|
Collections.sort(tiles);
|
||||||
|
|
||||||
|
zero.setPiecen(new Piecen(Piecen.GREEN, 0, 0, 2), tiles);
|
||||||
one.setPiecen(new Piecen(Piecen.BLUE, 0, 0, 0), tiles);
|
one.setPiecen(new Piecen(Piecen.BLUE, 0, 0, 0), tiles);
|
||||||
two.setPiecen(new Piecen(Piecen.BLUE, 0, 0, 0), tiles);
|
two.setPiecen(new Piecen(Piecen.RED, 0, 0, 1), tiles);
|
||||||
|
|
||||||
|
Log.info("Incomplete road: " +
|
||||||
|
TileUtil.computeFeatureScore(tiles, zero, 2));
|
||||||
|
|
||||||
|
Log.info("Completed city: " +
|
||||||
|
TileUtil.computeFeatureScore(tiles, two, 1));
|
||||||
|
|
||||||
|
Log.info("Incomplete city: " +
|
||||||
|
TileUtil.computeFeatureScore(tiles, one, 2));
|
||||||
|
|
||||||
frame.getContentPane().add(board, BorderLayout.CENTER);
|
frame.getContentPane().add(board, BorderLayout.CENTER);
|
||||||
frame.pack();
|
frame.pack();
|
||||||
@@ -556,6 +599,9 @@ public class VenisonBoard
|
|||||||
* placements based on the current position of the placing tile. */
|
* placements based on the current position of the placing tile. */
|
||||||
protected boolean[] _validOrients;
|
protected boolean[] _validOrients;
|
||||||
|
|
||||||
|
/** The color to use when creating new piecens. */
|
||||||
|
protected int _newPiecenColor = Piecen.BLUE;
|
||||||
|
|
||||||
/** Our render offset in pixels. */
|
/** Our render offset in pixels. */
|
||||||
protected int _tx, _ty;
|
protected int _tx, _ty;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
|
import com.samskivert.util.ListUtil;
|
||||||
|
|
||||||
import com.threerings.presents.dobj.AttributeChangedEvent;
|
import com.threerings.presents.dobj.AttributeChangedEvent;
|
||||||
import com.threerings.presents.dobj.DSet;
|
import com.threerings.presents.dobj.DSet;
|
||||||
@@ -53,6 +54,13 @@ public class VenisonController
|
|||||||
// get a casted reference to our game object
|
// get a casted reference to our game object
|
||||||
_venobj = (VenisonObject)plobj;
|
_venobj = (VenisonObject)plobj;
|
||||||
|
|
||||||
|
// find out what our index is and use that as our piecen color
|
||||||
|
int oidx = ListUtil.indexOfEqual(_venobj.players, _self.username);
|
||||||
|
if (oidx != -1) {
|
||||||
|
// use our player index as the piecen color directly
|
||||||
|
_panel.board.setNewPiecenColor(oidx);
|
||||||
|
}
|
||||||
|
|
||||||
// grab the tiles and piecens from the game object and configure
|
// grab the tiles and piecens from the game object and configure
|
||||||
// the board with them
|
// the board with them
|
||||||
_panel.board.setTiles(_venobj.tiles);
|
_panel.board.setTiles(_venobj.tiles);
|
||||||
@@ -67,10 +75,8 @@ public class VenisonController
|
|||||||
{
|
{
|
||||||
super.turnDidChange(turnHolder);
|
super.turnDidChange(turnHolder);
|
||||||
|
|
||||||
// if it's our turn, set the tile to be placed. otherwise clear it
|
// if it's our turn, set the tile to be placed
|
||||||
// out
|
|
||||||
if (turnHolder.equals(_self.username)) {
|
if (turnHolder.equals(_self.username)) {
|
||||||
Log.info("Setting tile to be placed: " + _venobj.currentTile);
|
|
||||||
_panel.board.setTileToBePlaced(_venobj.currentTile);
|
_panel.board.setTileToBePlaced(_venobj.currentTile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,6 +119,11 @@ public class VenisonController
|
|||||||
// documentation inherited
|
// documentation inherited
|
||||||
public void elementRemoved (ElementRemovedEvent event)
|
public void elementRemoved (ElementRemovedEvent event)
|
||||||
{
|
{
|
||||||
|
if (event.getName().equals(VenisonObject.PIECENS)) {
|
||||||
|
// a piecen was removed, update the board
|
||||||
|
Log.info("Clearing piecen " + event.getKey() + ".");
|
||||||
|
_panel.board.clearPiecen(event.getKey());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// documentation inherited
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
//
|
//
|
||||||
// $Id: AtlantiObject.java,v 1.6 2001/10/17 02:19:54 mdb Exp $
|
// $Id: AtlantiObject.java,v 1.7 2001/10/17 04:34:14 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
import com.threerings.presents.dobj.DSet;
|
import com.threerings.presents.dobj.DSet;
|
||||||
import com.threerings.parlor.turn.TurnGameObject;
|
import com.threerings.parlor.turn.TurnGameObject;
|
||||||
|
|
||||||
@@ -20,6 +21,9 @@ public class VenisonObject extends TurnGameObject
|
|||||||
/** The field name of the <code>piecens</code> field. */
|
/** The field name of the <code>piecens</code> field. */
|
||||||
public static final String PIECENS = "piecens";
|
public static final String PIECENS = "piecens";
|
||||||
|
|
||||||
|
/** The field name of the <code>scores</code> field. */
|
||||||
|
public static final String SCORES = "scores";
|
||||||
|
|
||||||
/** A set containing all of the tiles that are in play in this
|
/** A set containing all of the tiles that are in play in this
|
||||||
* game. */
|
* game. */
|
||||||
public DSet tiles = new DSet(VenisonTile.class);
|
public DSet tiles = new DSet(VenisonTile.class);
|
||||||
@@ -32,6 +36,9 @@ public class VenisonObject extends TurnGameObject
|
|||||||
* board. */
|
* board. */
|
||||||
public DSet piecens = new DSet(Piecen.class);
|
public DSet piecens = new DSet(Piecen.class);
|
||||||
|
|
||||||
|
/** The scores for each player. */
|
||||||
|
public int[] scores;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests that the <code>tiles</code> field be set to the specified
|
* Requests that the <code>tiles</code> field be set to the specified
|
||||||
* value.
|
* value.
|
||||||
@@ -113,6 +120,15 @@ public class VenisonObject extends TurnGameObject
|
|||||||
requestElementUpdate(PIECENS, elem);
|
requestElementUpdate(PIECENS, elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that the <code>scores</code> field be set to the specified
|
||||||
|
* value.
|
||||||
|
*/
|
||||||
|
public void setScores (int[] value)
|
||||||
|
{
|
||||||
|
requestAttributeChange(SCORES, value);
|
||||||
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// documentation inherited
|
||||||
protected void toString (StringBuffer buf)
|
protected void toString (StringBuffer buf)
|
||||||
{
|
{
|
||||||
@@ -120,5 +136,6 @@ public class VenisonObject extends TurnGameObject
|
|||||||
buf.append(", tiles=").append(tiles);
|
buf.append(", tiles=").append(tiles);
|
||||||
buf.append(", currentTile=").append(currentTile);
|
buf.append(", currentTile=").append(currentTile);
|
||||||
buf.append(", piecens=").append(piecens);
|
buf.append(", piecens=").append(piecens);
|
||||||
|
buf.append(", scores=").append(StringUtil.toString(scores));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: Feature.java,v 1.1 2001/10/17 02:19:54 mdb Exp $
|
// $Id: Feature.java,v 1.2 2001/10/17 04:34:13 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
@@ -334,9 +334,13 @@ public class Feature
|
|||||||
return poly;
|
return poly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a string representation of this feature.
|
||||||
|
*/
|
||||||
public String toString ()
|
public String toString ()
|
||||||
{
|
{
|
||||||
return "[type=" + type + ", edgeMask=" + edgeMask + "]";
|
return "[type=" + FeatureUtil.typeToString(type) +
|
||||||
|
", edgeMask=" + FeatureUtil.edgeMaskToString(edgeMask) + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Maps feature types to colors. */
|
/** Maps feature types to colors. */
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
//
|
//
|
||||||
// $Id: AtlantiManager.java,v 1.8 2001/10/17 02:19:54 mdb Exp $
|
// $Id: AtlantiManager.java,v 1.9 2001/10/17 04:34:14 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
import com.threerings.presents.dobj.DSet;
|
import com.threerings.presents.dobj.DSet;
|
||||||
import com.threerings.presents.dobj.MessageEvent;
|
import com.threerings.presents.dobj.MessageEvent;
|
||||||
|
|
||||||
@@ -61,6 +65,12 @@ public class VenisonManager
|
|||||||
// clear out our board tiles
|
// clear out our board tiles
|
||||||
_tiles.clear();
|
_tiles.clear();
|
||||||
|
|
||||||
|
// create a claim group vector
|
||||||
|
_claimGroupVector = new int[_players.length];
|
||||||
|
|
||||||
|
// clear out the scores
|
||||||
|
_venobj.setScores(new int[_players.length]);
|
||||||
|
|
||||||
// clear out the tile and piecen set
|
// clear out the tile and piecen set
|
||||||
_venobj.setTiles(new DSet(VenisonTile.class));
|
_venobj.setTiles(new DSet(VenisonTile.class));
|
||||||
_venobj.setPiecens(new DSet(Piecen.class));
|
_venobj.setPiecens(new DSet(Piecen.class));
|
||||||
@@ -105,6 +115,110 @@ public class VenisonManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether or not the placement of this tile has completed
|
||||||
|
* features that were previously incomplete.
|
||||||
|
*/
|
||||||
|
protected void scoreCompletedFeatures (VenisonTile tile)
|
||||||
|
{
|
||||||
|
// potentially score all features on the tile
|
||||||
|
for (int i = 0; i < tile.features.length; i++) {
|
||||||
|
// we only need to worry about ROAD and CITY features because
|
||||||
|
// those are the only features on this tile that we might have
|
||||||
|
// completed
|
||||||
|
Feature f = tile.features[i];
|
||||||
|
if (f.type != TileCodes.CITY && f.type != TileCodes.ROAD) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if any piecens are even on a feature in this group
|
||||||
|
int cgroup = tile.claims[i];
|
||||||
|
int[] cgv = getClaimGroupVector(cgroup);
|
||||||
|
if (cgv == null) {
|
||||||
|
// if not, we don't have anything to score
|
||||||
|
Log.info("Not scoring unclaimed feature " +
|
||||||
|
"[ttype=" + tile.type + ", feat=" + f +
|
||||||
|
", cgroup=" + cgroup + "].");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we do have something to score, so we compute the score for
|
||||||
|
// this feature
|
||||||
|
int score = TileUtil.computeFeatureScore(_tiles, tile, i);
|
||||||
|
|
||||||
|
// if the score is positive, it's a completed feature, so we
|
||||||
|
// dole out the points and clear the associated piecens
|
||||||
|
if (score > 0) {
|
||||||
|
Log.info("Scoring feature [ttype=" + tile.type +
|
||||||
|
", feature=" + f + ", score=" + score + "].");
|
||||||
|
|
||||||
|
// adjust the scores
|
||||||
|
for (int p = 0; p < cgv.length; p++) {
|
||||||
|
_venobj.scores[p] += (score * cgv[p]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// broadcast the new scores
|
||||||
|
_venobj.setScores(_venobj.scores);
|
||||||
|
Log.info("New scores: " + StringUtil.toString(_venobj.scores));
|
||||||
|
|
||||||
|
// and free up the scored piecens
|
||||||
|
Iterator iter = _venobj.piecens.elements();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Piecen p = (Piecen)iter.next();
|
||||||
|
if (p.claimGroup == cgroup) {
|
||||||
|
Log.info("Removing piecen " + p + ".");
|
||||||
|
_venobj.removeFromPiecens(p.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.info("Not scoring incomplete feature " +
|
||||||
|
"[ttype=" + tile.type + ", feat=" + f +
|
||||||
|
", score=" + score + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an int array with zeros and ones in the appropriate places
|
||||||
|
* so that a score can be multiplied by a player's position in the
|
||||||
|
* vector to determine whether or not they receive any points for the
|
||||||
|
* scoring of a particular claim group.
|
||||||
|
*
|
||||||
|
* <p> Note that this function returns a static (to this instance)
|
||||||
|
* vector, so it cannot be called again without overwriting values
|
||||||
|
* returned previously.
|
||||||
|
*
|
||||||
|
* @return an array for the specified claim group or null if no
|
||||||
|
* players have a piecen claiming the specified claim group.
|
||||||
|
*/
|
||||||
|
protected int[] getClaimGroupVector (int claimGroup)
|
||||||
|
{
|
||||||
|
// clear out the vector
|
||||||
|
Arrays.fill(_claimGroupVector, 0);
|
||||||
|
|
||||||
|
// iterate over the piecens
|
||||||
|
int max = 0;
|
||||||
|
Iterator iter = _venobj.piecens.elements();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Piecen piecen = (Piecen)iter.next();
|
||||||
|
if (piecen.claimGroup == claimGroup) {
|
||||||
|
// color == player index... somewhat sketchy
|
||||||
|
if (++_claimGroupVector[piecen.color] > max) {
|
||||||
|
// keep track of the highest scorer
|
||||||
|
max = _claimGroupVector[piecen.color];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now cut out everyone with scores less than the highest score
|
||||||
|
for (int i = 0; i < _claimGroupVector.length; i++) {
|
||||||
|
_claimGroupVector[i] = (_claimGroupVector[i] < max) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (max == 0) ? null : _claimGroupVector;
|
||||||
|
}
|
||||||
|
|
||||||
/** Handles place tile requests. */
|
/** Handles place tile requests. */
|
||||||
protected class PlaceTileHandler implements MessageHandler
|
protected class PlaceTileHandler implements MessageHandler
|
||||||
{
|
{
|
||||||
@@ -114,17 +228,22 @@ public class VenisonManager
|
|||||||
|
|
||||||
// make sure this is a valid placement
|
// make sure this is a valid placement
|
||||||
if (TileUtil.isValidPlacement(_tiles, tile)) {
|
if (TileUtil.isValidPlacement(_tiles, tile)) {
|
||||||
// add the tile to the lsit
|
// add the tile to the list and resort it
|
||||||
_tiles.add(tile);
|
_tiles.add(tile);
|
||||||
|
Collections.sort(_tiles);
|
||||||
|
|
||||||
// inherit its claim groups
|
// inherit its claim groups
|
||||||
TileUtil.inheritClaims(_tiles, tile);
|
TileUtil.inheritClaims(_tiles, tile);
|
||||||
|
|
||||||
Log.info("Placed tile " + tile + ".");
|
|
||||||
|
|
||||||
// add the tile to the tiles set
|
// add the tile to the tiles set
|
||||||
_venobj.addToTiles(tile);
|
_venobj.addToTiles(tile);
|
||||||
|
|
||||||
|
// placing a piece may have completed road or city
|
||||||
|
// features. if it did, we score them now
|
||||||
|
scoreCompletedFeatures(tile);
|
||||||
|
|
||||||
|
Log.info("Placed tile " + tile + ".");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.warning("Received invalid placement " + event + ".");
|
Log.warning("Received invalid placement " + event + ".");
|
||||||
}
|
}
|
||||||
@@ -182,4 +301,7 @@ public class VenisonManager
|
|||||||
|
|
||||||
/** A sorted list of the tiles that have been placed on the board. */
|
/** A sorted list of the tiles that have been placed on the board. */
|
||||||
protected List _tiles = new ArrayList();
|
protected List _tiles = new ArrayList();
|
||||||
|
|
||||||
|
/** Used to score features groups. */
|
||||||
|
protected int[] _claimGroupVector;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: FeatureUtil.java,v 1.1 2001/10/17 02:19:54 mdb Exp $
|
// $Id: FeatureUtil.java,v 1.2 2001/10/17 04:34:13 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
@@ -86,6 +86,39 @@ public class FeatureUtil implements TileCodes
|
|||||||
return features;
|
return features;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string describing the supplied type code.
|
||||||
|
*/
|
||||||
|
public static String typeToString (int type)
|
||||||
|
{
|
||||||
|
return TYPE_CODES[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string describing the supplied edge mask.
|
||||||
|
*/
|
||||||
|
public static String edgeMaskToString (int edgeMask)
|
||||||
|
{
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
if ((edgeMask & NORTH_F) != 0) buf.append("NORTH_F|");
|
||||||
|
if ((edgeMask & EAST_F) != 0) buf.append("EAST_F|");
|
||||||
|
if ((edgeMask & SOUTH_F) != 0) buf.append("SOUTH_F|");
|
||||||
|
if ((edgeMask & WEST_F) != 0) buf.append("WEST_F|");
|
||||||
|
if ((edgeMask & NNW_F) != 0) buf.append("NNW_F|");
|
||||||
|
if ((edgeMask & NNE_F) != 0) buf.append("NNE_F|");
|
||||||
|
if ((edgeMask & SSW_F) != 0) buf.append("SSW_F|");
|
||||||
|
if ((edgeMask & SSE_F) != 0) buf.append("SSE_F|");
|
||||||
|
if ((edgeMask & WNW_F) != 0) buf.append("WNW_F|");
|
||||||
|
if ((edgeMask & WSW_F) != 0) buf.append("WSW_F|");
|
||||||
|
if ((edgeMask & ENE_F) != 0) buf.append("ENE_F|");
|
||||||
|
if ((edgeMask & ESE_F) != 0) buf.append("ESE_F|");
|
||||||
|
// strip off the trailing bar if there is one
|
||||||
|
if (buf.length() > 0) {
|
||||||
|
buf.deleteCharAt(buf.length()-1);
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates the feature edge mask into the orientation specified.
|
* Translates the feature edge mask into the orientation specified.
|
||||||
* For a forward translation, provide a positive valued orientation
|
* For a forward translation, provide a positive valued orientation
|
||||||
@@ -300,6 +333,10 @@ public class FeatureUtil implements TileCodes
|
|||||||
{ ENE_F, SSE_F, WSW_F, NNW_F },
|
{ ENE_F, SSE_F, WSW_F, NNW_F },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** String representations of the feature type codes. */
|
||||||
|
protected static final String[] TYPE_CODES = {
|
||||||
|
"CITY", "GRASS", "ROAD", "CLOISTER" };
|
||||||
|
|
||||||
/** A table of features that are used on more than one tile. */
|
/** A table of features that are used on more than one tile. */
|
||||||
protected static Feature[] _reusedFeatures;
|
protected static Feature[] _reusedFeatures;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: TileUtil.java,v 1.7 2001/10/17 02:19:54 mdb Exp $
|
// $Id: TileUtil.java,v 1.8 2001/10/17 04:34:13 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
@@ -384,6 +384,153 @@ public class TileUtil implements TileCodes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the score for the specified feature and returns it. If the
|
||||||
|
* feature is complete (has no unconnected edges), the score will be
|
||||||
|
* positive. If it is incomplete, the score will be negative.
|
||||||
|
*
|
||||||
|
* @param tiles a sorted list of the tiles on the board.
|
||||||
|
* @param tile the tile that contains the feature whose score should
|
||||||
|
* be computed.
|
||||||
|
* @param featureIndex the index of the feature in the containing
|
||||||
|
* tile.
|
||||||
|
*
|
||||||
|
* @return a positive score for a completed feature group, a negative
|
||||||
|
* score for a partial feature group.
|
||||||
|
*/
|
||||||
|
public static int computeFeatureScore (
|
||||||
|
List tiles, VenisonTile tile, int featureIndex)
|
||||||
|
{
|
||||||
|
// determine what kind of feature it is
|
||||||
|
Feature feature = tile.features[featureIndex];
|
||||||
|
|
||||||
|
switch (feature.type) {
|
||||||
|
case ROAD:
|
||||||
|
return computeFeatureScore(tiles, tile, feature, new ArrayList());
|
||||||
|
|
||||||
|
case CITY: {
|
||||||
|
int score =
|
||||||
|
computeFeatureScore(tiles, tile, feature, new ArrayList());
|
||||||
|
// cities receive a 2x multiplier if they are larger than size
|
||||||
|
// two and are completed
|
||||||
|
return (score > 2) ? score*2 : score;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CLOISTER:
|
||||||
|
// cloister's score specially
|
||||||
|
return computeCloisterScore(tiles, tile);
|
||||||
|
|
||||||
|
default:
|
||||||
|
case GRASS:
|
||||||
|
// grass doesn't score
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper function for {@link
|
||||||
|
* #computeFeatureScore(List,VenisonTile,int)}.
|
||||||
|
*/
|
||||||
|
protected static int computeFeatureScore (
|
||||||
|
List tiles, VenisonTile tile, Feature feature, List seen)
|
||||||
|
{
|
||||||
|
// if we've already counted this tile, bail
|
||||||
|
if (seen.contains(tile)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise add this tile to the seen list
|
||||||
|
seen.add(tile);
|
||||||
|
|
||||||
|
// cities have a base score of one but get a one point bonus if
|
||||||
|
// they have a shield on them
|
||||||
|
int score = (feature.type == CITY && tile.hasShield) ? 2 : 1;
|
||||||
|
boolean missedNeighbor = false;
|
||||||
|
|
||||||
|
// now figure out what connected features there are, if any
|
||||||
|
int ftype = feature.type;
|
||||||
|
int fmask = feature.edgeMask;
|
||||||
|
|
||||||
|
// iterate over all of the possible adjacency possibilities
|
||||||
|
for (int c = 0; c < FeatureUtil.ADJACENCY_MAP.length; c += 3) {
|
||||||
|
int mask = FeatureUtil.ADJACENCY_MAP[c];
|
||||||
|
int dir = FeatureUtil.ADJACENCY_MAP[c+1];
|
||||||
|
int opp_mask = FeatureUtil.ADJACENCY_MAP[c+2];
|
||||||
|
VenisonTile neighbor = null;
|
||||||
|
|
||||||
|
// if this feature doesn't have this edge, skip it
|
||||||
|
if ((fmask & mask) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we have a neighbor in this direction
|
||||||
|
dir = (dir + tile.orientation) % 4;
|
||||||
|
switch (dir) {
|
||||||
|
case NORTH: neighbor = findTile(tiles, tile.x, tile.y-1); break;
|
||||||
|
case EAST: neighbor = findTile(tiles, tile.x+1, tile.y); break;
|
||||||
|
case SOUTH: neighbor = findTile(tiles, tile.x, tile.y+1); break;
|
||||||
|
case WEST: neighbor = findTile(tiles, tile.x-1, tile.y); break;
|
||||||
|
}
|
||||||
|
if (neighbor == null) {
|
||||||
|
// if we don't have a neighbor in a direction that we
|
||||||
|
// need, we're an incomplete feature. alas
|
||||||
|
missedNeighbor = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate the target mask into our orientation
|
||||||
|
mask = FeatureUtil.translateMask(mask, tile.orientation);
|
||||||
|
opp_mask = FeatureUtil.translateMask(opp_mask, tile.orientation);
|
||||||
|
|
||||||
|
// propagate, propagate, propagate
|
||||||
|
int fidx = neighbor.getFeatureIndex(opp_mask);
|
||||||
|
if (fidx < 0) {
|
||||||
|
Log.warning("Tile mismatch while scoring [self=" + tile +
|
||||||
|
", target=" + neighbor + ", fidx=" + fidx +
|
||||||
|
", srcEdge=" + mask +
|
||||||
|
", destEdge=" + opp_mask + "].");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// add the score for this neighbor
|
||||||
|
int nscore = computeFeatureScore(
|
||||||
|
tiles, neighbor, neighbor.features[fidx], seen);
|
||||||
|
// if our neighbor returned a negative score, we convert
|
||||||
|
// it back to positive and make a note to make it negative
|
||||||
|
// at the end
|
||||||
|
if (nscore < 0) {
|
||||||
|
score += -nscore;
|
||||||
|
missedNeighbor = true;
|
||||||
|
} else {
|
||||||
|
score += nscore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return missedNeighbor ? -score : score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper function for {@link
|
||||||
|
* #computeFeatureScore(List,VenisonTile,int)}.
|
||||||
|
*/
|
||||||
|
protected static int computeCloisterScore (List tiles, VenisonTile tile)
|
||||||
|
{
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
// all we need to know are how many neighbors this guy has (we
|
||||||
|
// count ourselves as well, just for code simplicity)
|
||||||
|
for (int dx = -1; dx < 1; dx++) {
|
||||||
|
for (int dy = -1; dy < 1; dy++) {
|
||||||
|
if (findTile(tiles, tile.x + dx, tile.y + dy) != null) {
|
||||||
|
score++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// incomplete cloisters return a negative score
|
||||||
|
return (score == 9) ? 9 : -score;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locates and returns the tile with the specified coordinates.
|
* Locates and returns the tile with the specified coordinates.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user