Revamps galore! Player info display! Working scoring for all cases (we
hope)! It's a glorious day! git-svn-id: https://samskivert.googlecode.com/svn/trunk@367 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: AtlantiBoard.java,v 1.9 2001/10/17 04:34:14 mdb Exp $
|
// $Id: AtlantiBoard.java,v 1.10 2001/10/17 23:27:52 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
@@ -173,6 +173,18 @@ public class VenisonBoard
|
|||||||
"find no associated tile! [key=" + key + "].");
|
"find no associated tile! [key=" + key + "].");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn off the ability to place a piecen on the most recently played
|
||||||
|
* tile. This function assumes that the mouse will not be over a valid
|
||||||
|
* piecen placement at the time this function is called (it expects
|
||||||
|
* that it will be over a button of some sort that says something to
|
||||||
|
* the effect of "skip placement for this turn").
|
||||||
|
*/
|
||||||
|
public void cancelPiecenPlacement ()
|
||||||
|
{
|
||||||
|
_placingPiecen = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
|||||||
@@ -55,10 +55,10 @@ public class VenisonController
|
|||||||
_venobj = (VenisonObject)plobj;
|
_venobj = (VenisonObject)plobj;
|
||||||
|
|
||||||
// find out what our index is and use that as our piecen color
|
// find out what our index is and use that as our piecen color
|
||||||
int oidx = ListUtil.indexOfEqual(_venobj.players, _self.username);
|
_selfIndex = ListUtil.indexOfEqual(_venobj.players, _self.username);
|
||||||
if (oidx != -1) {
|
if (_selfIndex != -1) {
|
||||||
// use our player index as the piecen color directly
|
// use our player index as the piecen color directly
|
||||||
_panel.board.setNewPiecenColor(oidx);
|
_panel.board.setNewPiecenColor(_selfIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// grab the tiles and piecens from the game object and configure
|
// grab the tiles and piecens from the game object and configure
|
||||||
@@ -137,6 +137,13 @@ public class VenisonController
|
|||||||
_venobj.getOid(), PLACE_TILE_REQUEST, args);
|
_venobj.getOid(), PLACE_TILE_REQUEST, args);
|
||||||
_ctx.getDObjectManager().postEvent(mevt);
|
_ctx.getDObjectManager().postEvent(mevt);
|
||||||
|
|
||||||
|
// if we have no piecens to place, we immediately disable
|
||||||
|
// piecen placement in the board
|
||||||
|
int pcount = TileUtil.countPiecens(_venobj.piecens, _selfIndex);
|
||||||
|
if (pcount >= PIECENS_PER_PLAYER) {
|
||||||
|
_panel.board.cancelPiecenPlacement();
|
||||||
|
}
|
||||||
|
|
||||||
// enable the noplace button
|
// enable the noplace button
|
||||||
_panel.noplace.setEnabled(true);
|
_panel.noplace.setEnabled(true);
|
||||||
|
|
||||||
@@ -153,6 +160,9 @@ public class VenisonController
|
|||||||
_panel.noplace.setEnabled(false);
|
_panel.noplace.setEnabled(false);
|
||||||
|
|
||||||
} else if (action.getActionCommand().equals(PLACE_NOTHING)) {
|
} else if (action.getActionCommand().equals(PLACE_NOTHING)) {
|
||||||
|
// turn off piecen placement in the board
|
||||||
|
_panel.board.cancelPiecenPlacement();
|
||||||
|
|
||||||
// the user doesn't want to place anything this turn. send a
|
// the user doesn't want to place anything this turn. send a
|
||||||
// place nothing request to the server
|
// place nothing request to the server
|
||||||
MessageEvent mevt = new MessageEvent(
|
MessageEvent mevt = new MessageEvent(
|
||||||
@@ -177,4 +187,7 @@ public class VenisonController
|
|||||||
|
|
||||||
/** A reference to our body object. */
|
/** A reference to our body object. */
|
||||||
protected BodyObject _self;
|
protected BodyObject _self;
|
||||||
|
|
||||||
|
/** Our player index or -1 if we're not a player. */
|
||||||
|
protected int _selfIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ public class VenisonPanel
|
|||||||
*/
|
*/
|
||||||
public VenisonPanel (ParlorContext ctx, VenisonController controller)
|
public VenisonPanel (ParlorContext ctx, VenisonController controller)
|
||||||
{
|
{
|
||||||
|
// give ourselves a wee bit of a border
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||||
|
|
||||||
HGroupLayout gl = new HGroupLayout(HGroupLayout.STRETCH);
|
HGroupLayout gl = new HGroupLayout(HGroupLayout.STRETCH);
|
||||||
gl.setOffAxisPolicy(HGroupLayout.STRETCH);
|
gl.setOffAxisPolicy(HGroupLayout.STRETCH);
|
||||||
setLayout(gl);
|
setLayout(gl);
|
||||||
@@ -47,9 +50,15 @@ public class VenisonPanel
|
|||||||
// create our side panel
|
// create our side panel
|
||||||
VGroupLayout sgl = new VGroupLayout(VGroupLayout.STRETCH);
|
VGroupLayout sgl = new VGroupLayout(VGroupLayout.STRETCH);
|
||||||
sgl.setOffAxisPolicy(VGroupLayout.STRETCH);
|
sgl.setOffAxisPolicy(VGroupLayout.STRETCH);
|
||||||
|
sgl.setJustification(VGroupLayout.TOP);
|
||||||
JPanel sidePanel = new JPanel(sgl);
|
JPanel sidePanel = new JPanel(sgl);
|
||||||
|
|
||||||
|
// add a player info view to the side panel
|
||||||
|
sidePanel.add(new JLabel("Scores:"), VGroupLayout.FIXED);
|
||||||
|
sidePanel.add(new PlayerInfoView(), VGroupLayout.FIXED);
|
||||||
|
|
||||||
// add a turn indicator to the side panel
|
// add a turn indicator to the side panel
|
||||||
|
sidePanel.add(new JLabel("Current turn:"), VGroupLayout.FIXED);
|
||||||
sidePanel.add(new TurnIndicatorView(), VGroupLayout.FIXED);
|
sidePanel.add(new TurnIndicatorView(), VGroupLayout.FIXED);
|
||||||
|
|
||||||
// add a "place nothing" button
|
// add a "place nothing" button
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
//
|
||||||
|
// $Id: PlayerInfoView.java,v 1.1 2001/10/17 23:27:52 mdb Exp $
|
||||||
|
|
||||||
|
package com.threerings.venison;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.GridBagLayout;
|
||||||
|
import java.awt.GridBagConstraints;
|
||||||
|
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
|
import com.threerings.presents.dobj.AttributeChangeListener;
|
||||||
|
import com.threerings.presents.dobj.AttributeChangedEvent;
|
||||||
|
|
||||||
|
import com.threerings.crowd.client.PlaceView;
|
||||||
|
import com.threerings.crowd.data.PlaceObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays each of the players in the game, their piece color and their
|
||||||
|
* score.
|
||||||
|
*/
|
||||||
|
public class PlayerInfoView
|
||||||
|
extends JPanel implements PlaceView, AttributeChangeListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructs a new player info panel, ready for insertion into a
|
||||||
|
* Venison game panel.
|
||||||
|
*/
|
||||||
|
public PlayerInfoView ()
|
||||||
|
{
|
||||||
|
setLayout(new GridBagLayout());
|
||||||
|
|
||||||
|
// we don't add our player info until we know how many players are
|
||||||
|
// in the game (which we find out once we enter the game room)
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
public void willEnterPlace (PlaceObject plobj)
|
||||||
|
{
|
||||||
|
// we want to grab a reference to the game object and add
|
||||||
|
// ourselves as an attribute change listener
|
||||||
|
_venobj = (VenisonObject)plobj;
|
||||||
|
_venobj.addListener(this);
|
||||||
|
|
||||||
|
// we need a score labels array so that we can keep track of the
|
||||||
|
// score labels
|
||||||
|
_scoreLabels = new JLabel[_venobj.players.length];
|
||||||
|
|
||||||
|
// now that we're here, we can add an entry for every player
|
||||||
|
for (int i = 0; i < _venobj.players.length; i++) {
|
||||||
|
addPlayer(i, _venobj.players[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have scores, update them
|
||||||
|
if (_venobj.scores.length == _venobj.players.length) {
|
||||||
|
updateScores();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a player to the display.
|
||||||
|
*/
|
||||||
|
protected void addPlayer (int idx, String username)
|
||||||
|
{
|
||||||
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
|
|
||||||
|
// create a label for their username
|
||||||
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
c.weightx = 1.0;
|
||||||
|
JLabel unlabel = new JLabel(username);
|
||||||
|
unlabel.setForeground(Feature.PIECEN_COLOR_MAP[idx]);
|
||||||
|
add(unlabel, c);
|
||||||
|
|
||||||
|
// create a label for their score
|
||||||
|
c.weightx = 0.0;
|
||||||
|
c.fill = GridBagConstraints.NONE;
|
||||||
|
c.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
_scoreLabels[idx] = new JLabel("0");
|
||||||
|
_scoreLabels[idx].setForeground(Color.black);
|
||||||
|
add(_scoreLabels[idx], c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
public void didLeavePlace (PlaceObject plobj)
|
||||||
|
{
|
||||||
|
// remove our listening self
|
||||||
|
_venobj.removeListener(this);
|
||||||
|
_venobj = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
public void attributeChanged (AttributeChangedEvent event)
|
||||||
|
{
|
||||||
|
// we care about the scores (which change)
|
||||||
|
if (event.getName().equals(VenisonObject.SCORES)) {
|
||||||
|
updateScores();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the scores from the game object and updates the view.
|
||||||
|
*/
|
||||||
|
protected void updateScores ()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _venobj.scores.length; i++) {
|
||||||
|
_scoreLabels[i].setText(Integer.toString(_venobj.scores[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A reference to the game object. */
|
||||||
|
protected VenisonObject _venobj;
|
||||||
|
|
||||||
|
/** References to our score label components. */
|
||||||
|
protected JLabel[] _scoreLabels;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: AtlantiCodes.java,v 1.2 2001/10/17 02:19:54 mdb Exp $
|
// $Id: AtlantiCodes.java,v 1.3 2001/10/17 23:27:52 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
@@ -8,6 +8,9 @@ package com.threerings.venison;
|
|||||||
*/
|
*/
|
||||||
public interface VenisonCodes
|
public interface VenisonCodes
|
||||||
{
|
{
|
||||||
|
/** The number of piecens provided to each player. */
|
||||||
|
public static final int PIECENS_PER_PLAYER = 7;
|
||||||
|
|
||||||
/** The name of the command posted by the {@link VenisonBoard} when
|
/** The name of the command posted by the {@link VenisonBoard} when
|
||||||
* the user places a tile into a valid position. */
|
* the user places a tile into a valid position. */
|
||||||
public static final String TILE_PLACED = "tile_placed";
|
public static final String TILE_PLACED = "tile_placed";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: AtlantiTile.java,v 1.8 2001/10/17 02:19:54 mdb Exp $
|
// $Id: AtlantiTile.java,v 1.9 2001/10/17 23:27:52 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
@@ -159,11 +159,8 @@ public class VenisonTile
|
|||||||
* @param featureIndex the index of the feature to update.
|
* @param featureIndex the index of the feature to update.
|
||||||
* @param claimGroup the claim group to associate with the feature.
|
* @param claimGroup the claim group to associate with the feature.
|
||||||
*/
|
*/
|
||||||
public void setFeatureGroup (int featureIndex, int claimGroup)
|
public void setClaimGroup (int featureIndex, int claimGroup)
|
||||||
{
|
{
|
||||||
// Log.info("Setting feature group [tile=" + this +
|
|
||||||
// ", fidx=" + featureIndex + ", cgroup=" + claimGroup + "].");
|
|
||||||
|
|
||||||
// update the claim group slot for this feature
|
// update the claim group slot for this feature
|
||||||
claims[featureIndex] = claimGroup;
|
claims[featureIndex] = claimGroup;
|
||||||
|
|
||||||
@@ -179,7 +176,7 @@ public class VenisonTile
|
|||||||
* Piecen#featureIndex} field is assumed to be initialized to the
|
* Piecen#featureIndex} field is assumed to be initialized to the
|
||||||
* feature index of this tile on which the piecen is to be placed.
|
* feature index of this tile on which the piecen is to be placed.
|
||||||
*
|
*
|
||||||
* <p> Note that this will call {@link TileUtil#setFeatureGroup} to
|
* <p> Note that this will call {@link TileUtil#setClaimGroup} to
|
||||||
* propagate the claiming of this feature to all neighboring tiles if
|
* propagate the claiming of this feature to all neighboring tiles if
|
||||||
* a non-null tiles array is supplied to the function.
|
* a non-null tiles array is supplied to the function.
|
||||||
*
|
*
|
||||||
@@ -210,8 +207,10 @@ public class VenisonTile
|
|||||||
int claimGroup = TileUtil.nextClaimGroup();
|
int claimGroup = TileUtil.nextClaimGroup();
|
||||||
Log.info("Creating claim group [cgroup=" + claimGroup +
|
Log.info("Creating claim group [cgroup=" + claimGroup +
|
||||||
", tile=" + this + ", fidx=" + piecen.featureIndex + "].");
|
", tile=" + this + ", fidx=" + piecen.featureIndex + "].");
|
||||||
TileUtil.setFeatureGroup(
|
TileUtil.setClaimGroup(
|
||||||
tiles, this, piecen.featureIndex, claimGroup, 0);
|
tiles, this, piecen.featureIndex, claimGroup);
|
||||||
|
// update our piecen with the claim group as well
|
||||||
|
piecen.claimGroup = claimGroup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,7 +253,7 @@ public class VenisonTile
|
|||||||
// if we have a piecen on this tile, render it as well
|
// if we have a piecen on this tile, render it as well
|
||||||
if (piecen != null && piecen.featureIndex == i) {
|
if (piecen != null && piecen.featureIndex == i) {
|
||||||
features[i].paintPiecen(
|
features[i].paintPiecen(
|
||||||
g, orientation, piecen.color, piecen.claimGroup);
|
g, orientation, piecen.owner, piecen.claimGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,6 +261,12 @@ public class VenisonTile
|
|||||||
g.setColor(Color.black);
|
g.setColor(Color.black);
|
||||||
g.drawRect(0, 0, TILE_WIDTH, TILE_HEIGHT);
|
g.drawRect(0, 0, TILE_WIDTH, TILE_HEIGHT);
|
||||||
|
|
||||||
|
// if we have a shield, draw a square in the lower right
|
||||||
|
if (hasShield) {
|
||||||
|
g.setColor(Color.orange);
|
||||||
|
g.drawRect(TILE_WIDTH-15, TILE_HEIGHT-15, 10, 10);
|
||||||
|
}
|
||||||
|
|
||||||
// translate back out
|
// translate back out
|
||||||
g.translate(-sx, -sy);
|
g.translate(-sx, -sy);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: Piecen.java,v 1.1 2001/10/17 02:19:54 mdb Exp $
|
// $Id: Piecen.java,v 1.2 2001/10/17 23:27:52 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
@@ -42,8 +42,8 @@ public class Piecen
|
|||||||
/** A color constant. */
|
/** A color constant. */
|
||||||
public static final int GREEN = 4;
|
public static final int GREEN = 4;
|
||||||
|
|
||||||
/** The color of this piecen. */
|
/** The owner of this piecen. */
|
||||||
public int color;
|
public int owner;
|
||||||
|
|
||||||
/** The x and y coordinates of the tile on which this piecen is
|
/** The x and y coordinates of the tile on which this piecen is
|
||||||
* placed. */
|
* placed. */
|
||||||
@@ -59,9 +59,9 @@ public class Piecen
|
|||||||
/**
|
/**
|
||||||
* Construts a piecen with the specified configuration.
|
* Construts a piecen with the specified configuration.
|
||||||
*/
|
*/
|
||||||
public Piecen (int color, int x, int y, int featureIndex)
|
public Piecen (int owner, int x, int y, int featureIndex)
|
||||||
{
|
{
|
||||||
this.color = color;
|
this.owner = owner;
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.featureIndex = featureIndex;
|
this.featureIndex = featureIndex;
|
||||||
@@ -85,7 +85,7 @@ public class Piecen
|
|||||||
public void writeTo (DataOutputStream out)
|
public void writeTo (DataOutputStream out)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
out.writeInt(color);
|
out.writeInt(owner);
|
||||||
out.writeInt(x);
|
out.writeInt(x);
|
||||||
out.writeInt(y);
|
out.writeInt(y);
|
||||||
out.writeInt(featureIndex);
|
out.writeInt(featureIndex);
|
||||||
@@ -95,7 +95,7 @@ public class Piecen
|
|||||||
public void readFrom (DataInputStream in)
|
public void readFrom (DataInputStream in)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
color = in.readInt();
|
owner = in.readInt();
|
||||||
x = in.readInt();
|
x = in.readInt();
|
||||||
y = in.readInt();
|
y = in.readInt();
|
||||||
featureIndex = in.readInt();
|
featureIndex = in.readInt();
|
||||||
@@ -127,7 +127,7 @@ public class Piecen
|
|||||||
*/
|
*/
|
||||||
public String toString ()
|
public String toString ()
|
||||||
{
|
{
|
||||||
return "[color=" + color + ", pos=" + x + "/" + y +
|
return "[owner=" + owner + ", pos=" + x + "/" + y +
|
||||||
", fidx=" + featureIndex + "]";
|
", feat=" + featureIndex + ", claim=" + claimGroup + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: TileCodes.java,v 1.5 2001/10/17 02:19:54 mdb Exp $
|
// $Id: TileCodes.java,v 1.6 2001/10/17 23:27:52 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
@@ -118,4 +118,8 @@ public interface TileCodes
|
|||||||
|
|
||||||
/** A constant indicating a cloister. */
|
/** A constant indicating a cloister. */
|
||||||
public static final int CLOISTER = 3;
|
public static final int CLOISTER = 3;
|
||||||
|
|
||||||
|
|
||||||
|
/** A flag used to mark a tile as part of a completed city. */
|
||||||
|
public static final int COMPLETED_CITY = 0x01;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: AtlantiManager.java,v 1.10 2001/10/17 05:01:51 mdb Exp $
|
// $Id: AtlantiManager.java,v 1.11 2001/10/17 23:27:52 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
@@ -9,9 +9,14 @@ import java.util.Collections;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.samskivert.util.HashIntMap;
|
||||||
import com.samskivert.util.StringUtil;
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
|
import com.threerings.presents.dobj.ElementAddedEvent;
|
||||||
|
import com.threerings.presents.dobj.ElementRemovedEvent;
|
||||||
|
import com.threerings.presents.dobj.ElementUpdatedEvent;
|
||||||
import com.threerings.presents.dobj.DSet;
|
import com.threerings.presents.dobj.DSet;
|
||||||
|
import com.threerings.presents.dobj.SetListener;
|
||||||
import com.threerings.presents.dobj.MessageEvent;
|
import com.threerings.presents.dobj.MessageEvent;
|
||||||
|
|
||||||
import com.threerings.parlor.turn.TurnGameManager;
|
import com.threerings.parlor.turn.TurnGameManager;
|
||||||
@@ -20,7 +25,7 @@ import com.threerings.parlor.turn.TurnGameManager;
|
|||||||
* The main coordinator of the Venison game on the server side.
|
* The main coordinator of the Venison game on the server side.
|
||||||
*/
|
*/
|
||||||
public class VenisonManager
|
public class VenisonManager
|
||||||
extends TurnGameManager implements VenisonCodes
|
extends TurnGameManager implements VenisonCodes, SetListener
|
||||||
{
|
{
|
||||||
// documentation inherited
|
// documentation inherited
|
||||||
protected Class getPlaceObjectClass ()
|
protected Class getPlaceObjectClass ()
|
||||||
@@ -62,6 +67,11 @@ public class VenisonManager
|
|||||||
_tilesInBox = TileUtil.getStandardTileSet();
|
_tilesInBox = TileUtil.getStandardTileSet();
|
||||||
Collections.shuffle(_tilesInBox);
|
Collections.shuffle(_tilesInBox);
|
||||||
|
|
||||||
|
// shave off most of the tiles for the moment
|
||||||
|
while (_tilesInBox.size() > 15) {
|
||||||
|
_tilesInBox.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
// clear out our board tiles
|
// clear out our board tiles
|
||||||
_tiles.clear();
|
_tiles.clear();
|
||||||
|
|
||||||
@@ -76,8 +86,9 @@ public class VenisonManager
|
|||||||
_venobj.setPiecens(new DSet(Piecen.class));
|
_venobj.setPiecens(new DSet(Piecen.class));
|
||||||
|
|
||||||
// and add the starting tile
|
// and add the starting tile
|
||||||
_venobj.addToTiles(VenisonTile.STARTING_TILE);
|
VenisonTile start = TileUtil.getStartingTile();
|
||||||
_tiles.add(VenisonTile.STARTING_TILE);
|
_venobj.addToTiles(start);
|
||||||
|
_tiles.add(start);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void turnWillStart ()
|
protected void turnWillStart ()
|
||||||
@@ -116,10 +127,47 @@ public class VenisonManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether or not the placement of this tile has completed
|
* At the end of the game, we need to compute the final scores.
|
||||||
* features that were previously incomplete.
|
|
||||||
*/
|
*/
|
||||||
protected void scoreCompletedFeatures (VenisonTile tile)
|
protected void 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
|
||||||
|
// scoring its features
|
||||||
|
iter = _venobj.tiles.elements();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
VenisonTile tile = (VenisonTile)iter.next();
|
||||||
|
scoreFeatures(tile, piecens);
|
||||||
|
}
|
||||||
|
|
||||||
|
// lastly, we have to score the farms (cue the ominous drums)...
|
||||||
|
scoreFarms();
|
||||||
|
|
||||||
|
// update the final scores
|
||||||
|
_venobj.setScores(_venobj.scores);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scores the features on this tile.
|
||||||
|
*
|
||||||
|
* @param tile the tile whose features should be scored.
|
||||||
|
* @param piecens during the final tally, we don't look for piecens on
|
||||||
|
* the board and remove them when we score them, we instead look for
|
||||||
|
* the piecens in this supplied array and remove them from there both
|
||||||
|
* to preserve the appearance of the board during final scoring and to
|
||||||
|
* circumvent the need to process an element removed event every time
|
||||||
|
* 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)
|
||||||
{
|
{
|
||||||
// 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++) {
|
||||||
@@ -133,12 +181,12 @@ public class VenisonManager
|
|||||||
|
|
||||||
// see if any piecens are even on a feature in this group
|
// see if any piecens are even on a feature in this group
|
||||||
int cgroup = tile.claims[i];
|
int cgroup = tile.claims[i];
|
||||||
int[] cgv = getClaimGroupVector(cgroup);
|
int[] cgv = getClaimGroupVector(cgroup, piecens);
|
||||||
if (cgv == null) {
|
if (cgv == null) {
|
||||||
// if not, we don't have anything to score
|
// if not, we don't have anything to score
|
||||||
Log.info("Not scoring unclaimed feature " +
|
// Log.info("Not scoring unclaimed feature " +
|
||||||
"[ttype=" + tile.type + ", feat=" + f +
|
// "[ttype=" + tile.type + ", feat=" + f +
|
||||||
", cgroup=" + cgroup + "].");
|
// ", cgroup=" + cgroup + "].");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,46 +194,47 @@ public class VenisonManager
|
|||||||
// this feature
|
// this feature
|
||||||
int score = TileUtil.computeFeatureScore(_tiles, tile, i);
|
int score = TileUtil.computeFeatureScore(_tiles, tile, i);
|
||||||
|
|
||||||
// if the score is positive, it's a completed feature, so we
|
// if the score is positive, it's a completed feature and we
|
||||||
// dole out the points and clear the associated piecens
|
// score it regardless, we score incomplete features only
|
||||||
if (score > 0) {
|
// during the final tally
|
||||||
Log.info("Scoring feature [ttype=" + tile.type +
|
if (score > 0 || piecens != null) {
|
||||||
", feature=" + f + ", score=" + score + "].");
|
// convert the score into a positive value
|
||||||
|
if (score > 0) {
|
||||||
|
Log.info("Scoring complete feature " +
|
||||||
|
"[ttype=" + tile.type + ", feature=" + f +
|
||||||
|
", score=" + score +
|
||||||
|
", cgv=" + StringUtil.toString(cgv) + "].");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.info("Scoring incomplete feature [ttype=" + tile.type +
|
||||||
|
", feature=" + f + ", score=" + score + "].");
|
||||||
|
}
|
||||||
|
score = Math.abs(score);
|
||||||
|
|
||||||
// adjust the scores
|
// adjust the scores
|
||||||
for (int p = 0; p < cgv.length; p++) {
|
for (int p = 0; p < cgv.length; p++) {
|
||||||
_venobj.scores[p] += (score * cgv[p]);
|
_venobj.scores[p] += (score * cgv[p]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// broadcast the new scores
|
|
||||||
_venobj.setScores(_venobj.scores);
|
|
||||||
Log.info("New scores: " + StringUtil.toString(_venobj.scores));
|
Log.info("New scores: " + StringUtil.toString(_venobj.scores));
|
||||||
|
|
||||||
// and free up the scored piecens
|
// broadcast the new scores if this isn't the final tally
|
||||||
Iterator iter = _venobj.piecens.elements();
|
if (piecens == null) {
|
||||||
while (iter.hasNext()) {
|
_venobj.setScores(_venobj.scores);
|
||||||
Piecen p = (Piecen)iter.next();
|
|
||||||
if (p.claimGroup == cgroup) {
|
|
||||||
Log.info("Removing piecen " + p + ".");
|
|
||||||
_venobj.removeFromPiecens(p.getKey());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// and free up the scored piecens
|
||||||
|
removePiecens(cgroup, piecens);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.info("Not scoring incomplete feature " +
|
// Log.info("Not scoring incomplete feature " +
|
||||||
"[ttype=" + tile.type + ", feat=" + f +
|
// "[ttype=" + tile.type + ", feat=" + f +
|
||||||
", score=" + score + "].");
|
// ", score=" + score + "].");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we also may have completed a cloister, so we check that as well
|
// we also may have completed a cloister, so we check that as well
|
||||||
for (int dx = -1; dx < 2; dx++) {
|
for (int dx = -1; dx < 2; dx++) {
|
||||||
for (int dy = -1; dy < 2; dy++) {
|
for (int dy = -1; dy < 2; dy++) {
|
||||||
// skip ourselves
|
|
||||||
if (dx == 0 && dy == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find our neighbor and make sure they exist
|
// find our neighbor and make sure they exist
|
||||||
VenisonTile neighbor =
|
VenisonTile neighbor =
|
||||||
TileUtil.findTile(_tiles, tile.x + dx, tile.y + dy);
|
TileUtil.findTile(_tiles, tile.x + dx, tile.y + dy);
|
||||||
@@ -205,43 +254,177 @@ public class VenisonManager
|
|||||||
|
|
||||||
// tile has a piecen
|
// tile has a piecen
|
||||||
if (p == null) {
|
if (p == null) {
|
||||||
Log.info("Skipping non-piecen having " +
|
// Log.info("Skipping non-piecen having " +
|
||||||
"cloister tile [tile=" + neighbor +
|
// "cloister tile [tile=" + neighbor +
|
||||||
", feat=" + f + "].");
|
// ", feat=" + f + "].");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// piecen is on cloister feature
|
// piecen is on cloister feature
|
||||||
if (neighbor.claims[i] != p.claimGroup) {
|
if (neighbor.claims[i] != p.claimGroup) {
|
||||||
Log.info("Skipping cloister tile with piecen on " +
|
// Log.info("Skipping cloister tile with piecen on " +
|
||||||
"non-cloister [tile=" + neighbor +
|
// "non-cloister [tile=" + neighbor +
|
||||||
", feat=" + f + "].");
|
// ", feat=" + f + "].");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// score the cloister to see if it is completed
|
// score the cloister
|
||||||
int score = TileUtil.computeFeatureScore(
|
int score = TileUtil.computeFeatureScore(
|
||||||
_tiles, neighbor, i);
|
_tiles, neighbor, i);
|
||||||
|
|
||||||
// it is, yay!
|
// if it's completed or if we're doing the final
|
||||||
if (score > 0) {
|
// tally, we score it
|
||||||
|
if (score > 0 || piecens != null) {
|
||||||
|
if (score > 0) {
|
||||||
|
Log.info("Scored completed cloister " +
|
||||||
|
"[tile=" + neighbor +
|
||||||
|
", f=" + f + ", p=" + p + "].");
|
||||||
|
} else {
|
||||||
|
Log.info("Scored incomplete cloister " +
|
||||||
|
"[tile=" + neighbor +
|
||||||
|
", f=" + f + ", p=" + p + "].");
|
||||||
|
}
|
||||||
|
|
||||||
|
// coerce the score into positive land
|
||||||
|
score = Math.abs(score);
|
||||||
|
|
||||||
// add the score to the owning player
|
// add the score to the owning player
|
||||||
_venobj.scores[p.color] += score;
|
_venobj.scores[p.owner] += score;
|
||||||
_venobj.setScores(_venobj.scores);
|
|
||||||
// and clear out the piecen
|
// only broadcast the updated scores if this isn't
|
||||||
_venobj.removeFromPiecens(p.getKey());
|
// the final tally
|
||||||
Log.info("Scored cloister [tile=" + neighbor +
|
if (piecens == null) {
|
||||||
", f=" + f + ", p=" + p + "].");
|
_venobj.setScores(_venobj.scores);
|
||||||
|
}
|
||||||
|
|
||||||
|
// and clear out the piecen (only removing it from
|
||||||
|
// the piecen set if we're not in the final tally)
|
||||||
|
removePiecen(p, piecens == null);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.info("Not scoring incomplete cloister " +
|
// Log.info("Not scoring incomplete cloister " +
|
||||||
"[tile=" + neighbor + ", feat=" + f + "].");
|
// "[tile=" + neighbor + ", feat=" + f + "].");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scores the farms, which is the final act of scoring.
|
||||||
|
*/
|
||||||
|
protected void scoreFarms ()
|
||||||
|
{
|
||||||
|
HashIntMap cities = new HashIntMap();
|
||||||
|
|
||||||
|
// clear out the claims for incompleted cities and claim unclaimed
|
||||||
|
// completed cities
|
||||||
|
TileUtil.prepCitiesForScoring(_tiles);
|
||||||
|
|
||||||
|
// do the big process-ola
|
||||||
|
int tsize = _tiles.size();
|
||||||
|
for (int i = 0; i < tsize; i++) {
|
||||||
|
VenisonTile tile = (VenisonTile)_tiles.get(i);
|
||||||
|
|
||||||
|
// iterate over all of the city features in this tile
|
||||||
|
for (int f = 0; f < tile.features.length; f++) {
|
||||||
|
// get the claim group for this feature
|
||||||
|
int cityClaim = tile.claims[f];
|
||||||
|
|
||||||
|
// skip unclaimed and non-city features
|
||||||
|
if (tile.features[f].type != TileCodes.CITY ||
|
||||||
|
cityClaim == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the list associated with this claim group
|
||||||
|
int[] claims = (int[])cities.get(cityClaim);
|
||||||
|
if (claims == null) {
|
||||||
|
// create a claim vector if we've not got one. if a
|
||||||
|
// city had 35 separately claimed farms around it, all
|
||||||
|
// the piecens in the game would be in play and the
|
||||||
|
// city would not have been claimed which would be an
|
||||||
|
// extremely pathological case, but we love pathology
|
||||||
|
// (especially when we don't have a resizable int list
|
||||||
|
// class handy)
|
||||||
|
claims = new int[35];
|
||||||
|
cities.put(cityClaim, claims);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate over all of the grass features that are
|
||||||
|
// connected to city features on this tile and add their
|
||||||
|
// claim groups the list for this city feature
|
||||||
|
int[] grasses = FeatureUtil.CITY_GRASS_MAP[tile.type-1];
|
||||||
|
for (int g = 0; g < grasses.length; g++) {
|
||||||
|
int farmClaim = tile.claims[grasses[g]];
|
||||||
|
|
||||||
|
// only worry about claimed grass regions
|
||||||
|
if (farmClaim == 0) {
|
||||||
|
Log.info("Ignoring unclaimed farm group " +
|
||||||
|
"[tile=" + tile +
|
||||||
|
", fidx=" + grasses[g] + "].");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// and the farm claim group to the list
|
||||||
|
for (int c = 0; c < claims.length; c++) {
|
||||||
|
// don't add the farm claim twice
|
||||||
|
if (claims[c] == farmClaim) {
|
||||||
|
break;
|
||||||
|
} else if (claims[c] == 0) {
|
||||||
|
claims[c] = farmClaim;
|
||||||
|
Log.info("Noting city/farm abuttal " +
|
||||||
|
"[tile=" + tile +
|
||||||
|
", cityClaim=" + cityClaim +
|
||||||
|
", farmClaim=" + farmClaim + "].");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now for each city, we look to see who has the most piecens that
|
||||||
|
// are connected to the city by farms
|
||||||
|
Iterator iter = cities.keys();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
int cityClaim = ((Integer)iter.next()).intValue();
|
||||||
|
int[] farmClaims = (int[])cities.get(cityClaim);
|
||||||
|
int[] pcount = new int[_players.length];
|
||||||
|
int max = 0;
|
||||||
|
|
||||||
|
Iterator piter = _venobj.piecens.elements();
|
||||||
|
while (piter.hasNext()) {
|
||||||
|
Piecen p = (Piecen)piter.next();
|
||||||
|
// see if the piecen is on any of the farms
|
||||||
|
for (int c = 0; c < farmClaims.length; c++) {
|
||||||
|
if (p.claimGroup == farmClaims[c]) {
|
||||||
|
Log.info("Counting piecen [cityClaim=" + cityClaim +
|
||||||
|
", farmClaim=" + farmClaims[c] +
|
||||||
|
", piecen=" + p + "].");
|
||||||
|
// increment their count and track the max
|
||||||
|
if (max < ++pcount[p.owner]) {
|
||||||
|
max = pcount[p.owner];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info("Counted city [cityClaim=" + cityClaim +
|
||||||
|
", counts=" + StringUtil.toString(pcount) + "].");
|
||||||
|
|
||||||
|
// now score four points for every player that has the max
|
||||||
|
for (int i = 0; i < pcount.length; i++) {
|
||||||
|
if (pcount[i] == max) {
|
||||||
|
Log.info("Scoring city for player [cgroup=" + cityClaim +
|
||||||
|
", player=" + _players[i] +
|
||||||
|
", pcount=" + pcount[i] + "].");
|
||||||
|
_venobj.scores[i] += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an int array with zeros and ones in the appropriate places
|
* 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
|
* so that a score can be multiplied by a player's position in the
|
||||||
@@ -252,24 +435,44 @@ public class VenisonManager
|
|||||||
* vector, so it cannot be called again without overwriting values
|
* vector, so it cannot be called again without overwriting values
|
||||||
* returned previously.
|
* returned previously.
|
||||||
*
|
*
|
||||||
|
* @param claimGroup the claim group that we're scoring.
|
||||||
|
* @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.
|
||||||
*/
|
*/
|
||||||
protected int[] getClaimGroupVector (int claimGroup)
|
protected int[] getClaimGroupVector (int claimGroup, Piecen[] piecens)
|
||||||
{
|
{
|
||||||
// clear out the vector
|
// clear out the vector
|
||||||
Arrays.fill(_claimGroupVector, 0);
|
Arrays.fill(_claimGroupVector, 0);
|
||||||
|
|
||||||
// iterate over the piecens
|
// iterate over the piecens
|
||||||
int max = 0;
|
int max = 0;
|
||||||
Iterator iter = _venobj.piecens.elements();
|
if (piecens == null) {
|
||||||
while (iter.hasNext()) {
|
Iterator iter = _venobj.piecens.elements();
|
||||||
Piecen piecen = (Piecen)iter.next();
|
while (iter.hasNext()) {
|
||||||
if (piecen.claimGroup == claimGroup) {
|
Piecen piecen = (Piecen)iter.next();
|
||||||
// color == player index... somewhat sketchy
|
if (piecen.claimGroup == claimGroup) {
|
||||||
if (++_claimGroupVector[piecen.color] > max) {
|
// color == player index... somewhat sketchy
|
||||||
// keep track of the highest scorer
|
if (++_claimGroupVector[piecen.owner] > max) {
|
||||||
max = _claimGroupVector[piecen.color];
|
// keep track of the highest scorer
|
||||||
|
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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,6 +485,94 @@ public class VenisonManager
|
|||||||
return (max == 0) ? null : _claimGroupVector;
|
return (max == 0) ? null : _claimGroupVector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes piecens either from the supplied array or from the game
|
||||||
|
* object piecen set if no array is supplied.
|
||||||
|
*/
|
||||||
|
protected void removePiecens (int claimGroup, Piecen[] piecens)
|
||||||
|
{
|
||||||
|
if (piecens == null) {
|
||||||
|
Iterator iter = _venobj.piecens.elements();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Piecen p = (Piecen)iter.next();
|
||||||
|
if (p.claimGroup == claimGroup) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the piecen from the board and optionally removes it from
|
||||||
|
* the piecen set.
|
||||||
|
*
|
||||||
|
* @param piecen the piecen to be removed.
|
||||||
|
* @param removeFromPiecens if true, the piecen will also be removed
|
||||||
|
* from the piecens set in the game object.
|
||||||
|
*/
|
||||||
|
protected void removePiecen (Piecen piecen, boolean removeFromPiecens)
|
||||||
|
{
|
||||||
|
// locate the tile that contains this piecen
|
||||||
|
int tidx = _tiles.indexOf(piecen);
|
||||||
|
if (tidx == -1) {
|
||||||
|
Log.warning("Requested to remove piecen that is not " +
|
||||||
|
"associated with any tile [piecen=" + piecen + "].");
|
||||||
|
} else {
|
||||||
|
VenisonTile tile = (VenisonTile)_tiles.get(tidx);
|
||||||
|
// and clear the piecen
|
||||||
|
tile.clearPiecen();
|
||||||
|
}
|
||||||
|
|
||||||
|
// also remove from the piecens dset if requested
|
||||||
|
if (removeFromPiecens) {
|
||||||
|
_venobj.removeFromPiecens(piecen.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
public void elementAdded (ElementAddedEvent event)
|
||||||
|
{
|
||||||
|
// we react to piecen additions by potentially scoring the placed
|
||||||
|
// piecen. we allow the piecen to be added to the piecens set
|
||||||
|
// before scoring so that the players can see the piecen pop up on
|
||||||
|
// their screen and then disappear with a scoring notice rather
|
||||||
|
// than never show up at all; plus it simplifies our code
|
||||||
|
if (event.getName().equals(VenisonObject.PIECENS)) {
|
||||||
|
Piecen piecen = (Piecen)event.getElement();
|
||||||
|
|
||||||
|
// make sure this is a valid placement
|
||||||
|
VenisonTile tile = (VenisonTile)_venobj.tiles.get(piecen.getKey());
|
||||||
|
if (tile == null) {
|
||||||
|
Log.warning("Can't find tile for piecen scoring " +
|
||||||
|
piecen + ".");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// check to see if we added the piecen to a completed
|
||||||
|
// feature, in which case we score and remove it
|
||||||
|
scoreFeatures(tile, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
public void elementUpdated (ElementUpdatedEvent event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
public void elementRemoved (ElementRemovedEvent event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/** Handles place tile requests. */
|
/** Handles place tile requests. */
|
||||||
protected class PlaceTileHandler implements MessageHandler
|
protected class PlaceTileHandler implements MessageHandler
|
||||||
{
|
{
|
||||||
@@ -303,7 +594,7 @@ 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
|
||||||
scoreCompletedFeatures(tile);
|
scoreFeatures(tile, null);
|
||||||
|
|
||||||
Log.info("Placed tile " + tile + ".");
|
Log.info("Placed tile " + tile + ".");
|
||||||
|
|
||||||
@@ -319,10 +610,17 @@ public class VenisonManager
|
|||||||
public void handleEvent (MessageEvent event)
|
public void handleEvent (MessageEvent event)
|
||||||
{
|
{
|
||||||
Piecen piecen = (Piecen)event.getArgs()[0];
|
Piecen piecen = (Piecen)event.getArgs()[0];
|
||||||
|
|
||||||
// make sure this is a valid placement
|
|
||||||
VenisonTile tile = (VenisonTile)_venobj.tiles.get(piecen.getKey());
|
VenisonTile tile = (VenisonTile)_venobj.tiles.get(piecen.getKey());
|
||||||
if (tile == null) {
|
int pidx = getTurnHolderIndex();
|
||||||
|
int pcount = TileUtil.countPiecens(_venobj.piecens, pidx);
|
||||||
|
|
||||||
|
// do some checking before we place the piecen
|
||||||
|
if (pcount >= PIECENS_PER_PLAYER) {
|
||||||
|
Log.warning("Requested to place piecen for player that " +
|
||||||
|
"has all of their piecens in play " +
|
||||||
|
"[event=" + event + "].");
|
||||||
|
|
||||||
|
} else if (tile == null) {
|
||||||
Log.warning("Can't find tile for requested piecen " +
|
Log.warning("Can't find tile for requested piecen " +
|
||||||
"placement " + piecen + ".");
|
"placement " + piecen + ".");
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: FeatureUtil.java,v 1.2 2001/10/17 04:34:13 mdb Exp $
|
// $Id: FeatureUtil.java,v 1.3 2001/10/17 23:27:52 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
@@ -61,6 +61,30 @@ public class FeatureUtil implements TileCodes
|
|||||||
WNW_F, WEST, ENE_F,
|
WNW_F, WEST, ENE_F,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** A mapping for city tiles to the grass features that are adjacent
|
||||||
|
* to the city tiles. */
|
||||||
|
public static final int[][] CITY_GRASS_MAP = new int[][] {
|
||||||
|
{ },
|
||||||
|
{ 1 }, // CITY_THREE
|
||||||
|
{ 1, 2 }, // CITY_THREE_ROAD
|
||||||
|
{ 1 }, // CITY_TWO
|
||||||
|
{ 1 }, // CITY_TWO_ROAD
|
||||||
|
{ 1, 2 }, // CITY_TWO_ROAD_ACROSS
|
||||||
|
{ 0 }, // TWO_CITY_TWO
|
||||||
|
{ 0 }, // TWO_CITY_TWO_ACROSS
|
||||||
|
{ 0 }, // CITY_ONE
|
||||||
|
{ 0 }, // CITY_ONE_ROAD_RIGHT
|
||||||
|
{ 0 }, // CITY_ONE_ROAD_LEFT
|
||||||
|
{ 0 }, // CITY_ONE_ROAD_TEE
|
||||||
|
{ 0 }, // CITY_ONE_ROAD_STRAIGHT
|
||||||
|
{ },
|
||||||
|
{ },
|
||||||
|
{ },
|
||||||
|
{ },
|
||||||
|
{ },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the feature array for the tile of the specified type.
|
* Returns the feature array for the tile of the specified type.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: TileUtil.java,v 1.9 2001/10/17 05:01:51 mdb Exp $
|
// $Id: TileUtil.java,v 1.10 2001/10/17 23:27:52 mdb Exp $
|
||||||
|
|
||||||
package com.threerings.venison;
|
package com.threerings.venison;
|
||||||
|
|
||||||
@@ -12,11 +12,22 @@ import java.util.List;
|
|||||||
|
|
||||||
import com.samskivert.util.IntTuple;
|
import com.samskivert.util.IntTuple;
|
||||||
|
|
||||||
|
import com.threerings.presents.dobj.DSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility functions relating to the Venison tiles.
|
* Utility functions relating to the Venison tiles.
|
||||||
*/
|
*/
|
||||||
public class TileUtil implements TileCodes
|
public class TileUtil implements TileCodes
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Returns an instance of the starting tile (properly cloned so that
|
||||||
|
* it can be messed with by the server).
|
||||||
|
*/
|
||||||
|
public static VenisonTile getStartingTile ()
|
||||||
|
{
|
||||||
|
return (VenisonTile)VenisonTile.STARTING_TILE.clone();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list containing the standard tile set for the Venison
|
* Returns a list containing the standard tile set for the Venison
|
||||||
* game. The list is a clone, so it can be bent, folded and modified
|
* game. The list is a clone, so it can be bent, folded and modified
|
||||||
@@ -24,7 +35,14 @@ public class TileUtil implements TileCodes
|
|||||||
*/
|
*/
|
||||||
public static List getStandardTileSet ()
|
public static List getStandardTileSet ()
|
||||||
{
|
{
|
||||||
return (List)TILE_SET.clone();
|
// we need to deep copy the default tile set, so we can't just use
|
||||||
|
// clone
|
||||||
|
List tiles = new ArrayList();
|
||||||
|
int tsize = TILE_SET.size();
|
||||||
|
for (int i = 0; i < tsize; i++) {
|
||||||
|
tiles.add(((VenisonTile)TILE_SET.get(i)).clone());
|
||||||
|
}
|
||||||
|
return tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,119 +207,48 @@ public class TileUtil implements TileCodes
|
|||||||
*/
|
*/
|
||||||
public static void inheritClaims (List tiles, VenisonTile tile)
|
public static void inheritClaims (List tiles, VenisonTile tile)
|
||||||
{
|
{
|
||||||
// obtain our neighboring tiles
|
List flist = new ArrayList();
|
||||||
VenisonTile[] neighbors = new VenisonTile[4];
|
|
||||||
neighbors[NORTH] = findTile(tiles, tile.x, tile.y-1);
|
|
||||||
neighbors[EAST] = findTile(tiles, tile.x+1, tile.y);
|
|
||||||
neighbors[SOUTH] = findTile(tiles, tile.x, tile.y+1);
|
|
||||||
neighbors[WEST] = findTile(tiles, tile.x-1, tile.y);
|
|
||||||
|
|
||||||
// for each feature in the tile, determine whether or not the
|
// for each feature in the tile, load up its claim group and make
|
||||||
// neighboring tile's matching feature is claimed
|
// sure all features in that group (which will include our new
|
||||||
|
// feature) now have the same claim number
|
||||||
for (int i = 0; i < tile.features.length; i ++) {
|
for (int i = 0; i < tile.features.length; i ++) {
|
||||||
int ftype = tile.features[i].type;
|
int claimGroup = 0;
|
||||||
int fmask = tile.features[i].edgeMask;
|
|
||||||
int cgroup = 0;
|
|
||||||
|
|
||||||
// iterate over all of the possible adjacency possibilities,
|
// clear out the tilefeatures list before enumerating
|
||||||
// first looking for a claim group to inherit
|
flist.clear();
|
||||||
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];
|
|
||||||
|
|
||||||
// if this feature doesn't have this edge, skip it
|
// enumerate the claim group for this feature
|
||||||
if ((fmask & mask) == 0) {
|
enumerateGroup(tiles, tile, i, flist);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// translate the target direction accordingly
|
// find the first non-zero claim number
|
||||||
dir = (dir + tile.orientation) % 4;
|
for (int t = 0; t < flist.size(); t++) {
|
||||||
|
TileFeature feat = (TileFeature)flist.get(t);
|
||||||
// make sure we have a neighbor in the appropriate
|
int fcg = feat.tile.claims[feat.featureIndex];
|
||||||
// direction
|
if (fcg != 0) {
|
||||||
if (neighbors[dir] == null) {
|
claimGroup = fcg;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// it looks like we have a match, so translate the target
|
|
||||||
// stuff into our orientation
|
|
||||||
mask = FeatureUtil.translateMask(mask, tile.orientation);
|
|
||||||
opp_mask = FeatureUtil.translateMask(
|
|
||||||
opp_mask, tile.orientation);
|
|
||||||
|
|
||||||
// inherit the group of the opposing feature
|
|
||||||
cgroup = neighbors[dir].getFeatureGroup(opp_mask);
|
|
||||||
|
|
||||||
// and bail as soon as we find a non-zero claim group
|
|
||||||
if (cgroup != 0) {
|
|
||||||
Log.info("Inherited claim [tile=" + tile +
|
|
||||||
", fidx=" + i + ", cgroup=" + cgroup +
|
|
||||||
", source=" + neighbors[dir] + "].");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we didn't inherit a claim group, skip to the next
|
// if we found no non-zero claim number, we've nothing to
|
||||||
// feature
|
// inherit
|
||||||
if (cgroup == 0) {
|
if (claimGroup == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize the feature's claim group
|
// otherwise, assign our new claim number to all members of
|
||||||
tile.claims[i] = cgroup;
|
// the group (potentially causing some to inherit the new
|
||||||
|
// claim number)
|
||||||
// otherwise, iterate over all of the possible adjacency
|
for (int t = 0; t < flist.size(); t++) {
|
||||||
// possibilities, propagating our group to connected features
|
TileFeature feat = (TileFeature)flist.get(t);
|
||||||
for (int c = 0; c < FeatureUtil.ADJACENCY_MAP.length; c += 3) {
|
// set the claim group in the tile
|
||||||
int mask = FeatureUtil.ADJACENCY_MAP[c];
|
feat.tile.claims[feat.featureIndex] = claimGroup;
|
||||||
int dir = FeatureUtil.ADJACENCY_MAP[c+1];
|
// also set the claim group on the piecen if the tile has
|
||||||
int opp_mask = FeatureUtil.ADJACENCY_MAP[c+2];
|
// an associated piecen that is on this feature
|
||||||
|
Piecen p = feat.tile.piecen;
|
||||||
// if this feature doesn't have this edge, skip it
|
if (p != null && p.featureIndex == feat.featureIndex) {
|
||||||
if ((fmask & mask) == 0) {
|
p.claimGroup = claimGroup;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// translate the target direction accordingly
|
|
||||||
dir = (dir + tile.orientation) % 4;
|
|
||||||
|
|
||||||
// make sure we have a neighbor in the appropriate
|
|
||||||
// direction
|
|
||||||
if (neighbors[dir] == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// it looks like we have a match, so translate the target
|
|
||||||
// stuff into our orientation
|
|
||||||
mask = FeatureUtil.translateMask(mask, tile.orientation);
|
|
||||||
opp_mask = FeatureUtil.translateMask(
|
|
||||||
opp_mask, tile.orientation);
|
|
||||||
|
|
||||||
// make sure the neighbor in question isn't the one from
|
|
||||||
// which we inherited the group
|
|
||||||
int ogroup = neighbors[dir].getFeatureGroup(opp_mask);
|
|
||||||
if (ogroup == cgroup) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we've already been assigned to a group, we propagate
|
|
||||||
// our group to the opposing feature
|
|
||||||
int fidx = neighbors[dir].getFeatureIndex(opp_mask);
|
|
||||||
if (fidx >= 0) {
|
|
||||||
Log.info("Propagating group [fidx=" + i +
|
|
||||||
", cgroup=" + cgroup +
|
|
||||||
", dir=" + dir + "].");
|
|
||||||
setFeatureGroup(tiles, neighbors[dir],
|
|
||||||
fidx, cgroup, mask);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.warning("Can't join-propagate feature " +
|
|
||||||
"[self=" + tile +
|
|
||||||
", target=" + neighbors[dir] +
|
|
||||||
", fidx=" + fidx + ", cgroup=" + cgroup +
|
|
||||||
", destEdge=" + opp_mask +
|
|
||||||
", srcEdge=" + mask + "].");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -316,71 +263,18 @@ public class TileUtil implements TileCodes
|
|||||||
* being set.
|
* being set.
|
||||||
* @param featureIndex the index of the feature.
|
* @param featureIndex the index of the feature.
|
||||||
* @param claimGroup the claim group value to set.
|
* @param claimGroup the claim group value to set.
|
||||||
* @param entryEdgeMask the edge from which we are propagating this
|
|
||||||
* claim group (to avoid traversing back over that edge when
|
|
||||||
* propagating the group further).
|
|
||||||
*/
|
*/
|
||||||
public static void setFeatureGroup (
|
public static void setClaimGroup (
|
||||||
List tiles, VenisonTile tile, int featureIndex,
|
List tiles, VenisonTile tile, int featureIndex, int claimGroup)
|
||||||
int claimGroup, int entryEdgeMask)
|
|
||||||
{
|
{
|
||||||
// set the claim group for this feature on this tile
|
// load up this feature group
|
||||||
tile.setFeatureGroup(featureIndex, claimGroup);
|
List flist = new ArrayList();
|
||||||
|
enumerateGroup(tiles, tile, featureIndex, flist);
|
||||||
|
|
||||||
// now propagate this feature to connected features
|
// and assign the claim number to all features in the group
|
||||||
int ftype = tile.features[featureIndex].type;
|
for (int t = 0; t < flist.size(); t++) {
|
||||||
int fmask = tile.features[featureIndex].edgeMask;
|
TileFeature feat = (TileFeature)flist.get(t);
|
||||||
|
feat.tile.claims[feat.featureIndex] = claimGroup;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// figure out if this would be the tile from which we
|
|
||||||
// propagated into our current tile and skip it if so
|
|
||||||
opp_mask = FeatureUtil.translateMask(opp_mask, tile.orientation);
|
|
||||||
if ((opp_mask & entryEdgeMask) != 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) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// it looks like we have a match, so translate the target mask
|
|
||||||
// into our orientation
|
|
||||||
mask = FeatureUtil.translateMask(mask, tile.orientation);
|
|
||||||
|
|
||||||
// propagate, propagate, propagate
|
|
||||||
int fidx = neighbor.getFeatureIndex(opp_mask);
|
|
||||||
if (fidx >= 0) {
|
|
||||||
// only set the feature in the neighbor if they aren't
|
|
||||||
// already set to the appropriate group (prevent loops)
|
|
||||||
if (neighbor.claims[fidx] != claimGroup) {
|
|
||||||
setFeatureGroup(tiles, neighbor, fidx, claimGroup, mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.warning("Can't propagate feature [self=" + tile +
|
|
||||||
", target=" + neighbor + ", fidx=" + fidx +
|
|
||||||
", cgroup=" + claimGroup + ", srcEdge=" + mask +
|
|
||||||
", destEdge=" + opp_mask + "].");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,112 +295,40 @@ public class TileUtil implements TileCodes
|
|||||||
public static int computeFeatureScore (
|
public static int computeFeatureScore (
|
||||||
List tiles, VenisonTile tile, int featureIndex)
|
List tiles, VenisonTile tile, int featureIndex)
|
||||||
{
|
{
|
||||||
// determine what kind of feature it is
|
|
||||||
Feature feature = tile.features[featureIndex];
|
Feature feature = tile.features[featureIndex];
|
||||||
|
|
||||||
switch (feature.type) {
|
if (feature.type == CLOISTER) {
|
||||||
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
|
// cloister's score specially
|
||||||
return computeCloisterScore(tiles, tile);
|
return computeCloisterScore(tiles, tile);
|
||||||
|
|
||||||
default:
|
} else if (feature.type == GRASS) {
|
||||||
case GRASS:
|
|
||||||
// grass doesn't score
|
// grass doesn't score
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// if we're here, it's a road or city feature, which we score by
|
||||||
* A helper function for {@link
|
// loading up the group and counting its size
|
||||||
* #computeFeatureScore(List,VenisonTile,int)}.
|
List flist = new ArrayList();
|
||||||
*/
|
boolean complete = enumerateGroup(tiles, tile, featureIndex, flist);
|
||||||
protected static int computeFeatureScore (
|
int score = flist.size();
|
||||||
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
|
// for city groups, we need to add a bonus of one for every tile
|
||||||
seen.add(tile);
|
// that contains a shield and mutiply by two if the city is
|
||||||
|
// complete and larger than two tiles
|
||||||
// cities have a base score of one but get a one point bonus if
|
if (feature.type == CITY) {
|
||||||
// they have a shield on them
|
for (int t = 0; t < flist.size(); t++) {
|
||||||
int score = (feature.type == CITY && tile.hasShield) ? 2 : 1;
|
TileFeature feat = (TileFeature)flist.get(t);
|
||||||
boolean missedNeighbor = false;
|
if (feat.tile.hasShield) {
|
||||||
|
score++;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (complete && score > 2) {
|
||||||
|
score *= 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return missedNeighbor ? -score : score;
|
// incomplete scores are reported in the negative
|
||||||
|
return complete ? score : -score;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -531,6 +353,161 @@ public class TileUtil implements TileCodes
|
|||||||
return (score == 9) ? 9 : -score;
|
return (score == 9) ? 9 : -score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears out the claim group information for incomplete cities so
|
||||||
|
* that we can ignore them during farm scoring. Assigns new claim
|
||||||
|
* group values to any completed cities that were unclaimed.
|
||||||
|
*
|
||||||
|
* @param tiles a sorted list of tiles on the board.
|
||||||
|
*/
|
||||||
|
public static void prepCitiesForScoring (List tiles)
|
||||||
|
{
|
||||||
|
List flist = new ArrayList();
|
||||||
|
|
||||||
|
// iterate over the tiles, marking every city completed or not
|
||||||
|
int tsize = tiles.size();
|
||||||
|
for (int i = 0; i < tsize; i++) {
|
||||||
|
VenisonTile tile = (VenisonTile)tiles.get(i);
|
||||||
|
|
||||||
|
// iterate over each feature on this tile
|
||||||
|
for (int f = 0; f < tile.features.length; f++) {
|
||||||
|
// skip non-city features
|
||||||
|
if (tile.features[f].type != TileCodes.CITY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear out the feature group list before processing
|
||||||
|
flist.clear();
|
||||||
|
|
||||||
|
// enumerate the features in the group with this feature
|
||||||
|
if (enumerateGroup(tiles, tile, f, flist)) {
|
||||||
|
// if it's complete, we want to ensure that all of
|
||||||
|
// these features have a claim number
|
||||||
|
if (tile.claims[f] != 0) {
|
||||||
|
// it's complete and has a claim number. move on
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign the claim number to all features in the group
|
||||||
|
int claimGroup = nextClaimGroup();
|
||||||
|
for (int t = 0; t < flist.size(); t++) {
|
||||||
|
TileFeature feat = (TileFeature)flist.get(t);
|
||||||
|
feat.tile.claims[feat.featureIndex] = claimGroup;
|
||||||
|
if (t == 0) {
|
||||||
|
Log.info("Claiming complete city " +
|
||||||
|
"[claim=" + feat.tile.claims[
|
||||||
|
feat.featureIndex] + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// it's incomplete, so we want to clear out the claim
|
||||||
|
// number from all tiles in the group
|
||||||
|
for (int t = 0; t < flist.size(); t++) {
|
||||||
|
TileFeature feat = (TileFeature)flist.get(t);
|
||||||
|
if (t == 0) {
|
||||||
|
Log.info("Clearing incomplete city " +
|
||||||
|
"[claim=" + feat.tile.claims[
|
||||||
|
feat.featureIndex] + "].");
|
||||||
|
}
|
||||||
|
feat.tile.claims[feat.featureIndex] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumerates all of the features that are in the group of which the
|
||||||
|
* specified feature is a member.
|
||||||
|
*
|
||||||
|
* @param tiles a sorted list of the tiles on the board.
|
||||||
|
* @param tile the tile that contains the feature whose group is to be
|
||||||
|
* enumerated.
|
||||||
|
* @param featureIndex the index of the feature whose group is to be
|
||||||
|
* enumerated.
|
||||||
|
* @param group the list into which instances of {@link FeatureGroup}
|
||||||
|
* will be placed that represent the features that are members of the
|
||||||
|
* group.
|
||||||
|
*
|
||||||
|
* @return true if the group is complete (has no unconnected
|
||||||
|
* features), false if it is not.
|
||||||
|
*/
|
||||||
|
protected static boolean enumerateGroup (
|
||||||
|
List tiles, VenisonTile tile, int featureIndex, List group)
|
||||||
|
{
|
||||||
|
// create a tilefeature for this feature
|
||||||
|
TileFeature feat = new TileFeature(tile, featureIndex);
|
||||||
|
|
||||||
|
// determine whether or not this feature is already in the group
|
||||||
|
if (group.contains(feat)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise add this feature to the group and process this
|
||||||
|
// feature's neighbors
|
||||||
|
group.add(feat);
|
||||||
|
|
||||||
|
boolean complete = true;
|
||||||
|
int ftype = tile.features[featureIndex].type;
|
||||||
|
int fmask = tile.features[featureIndex].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 opp_mask = FeatureUtil.ADJACENCY_MAP[c+2];
|
||||||
|
|
||||||
|
// if this feature doesn't have this edge, skip it
|
||||||
|
if ((fmask & mask) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// look up our neighbor
|
||||||
|
VenisonTile neighbor = null;
|
||||||
|
int dir = FeatureUtil.ADJACENCY_MAP[c+1];
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we have a neighbor in this direction
|
||||||
|
if (neighbor == null) {
|
||||||
|
// if we don't have a neighbor in a direction that we
|
||||||
|
// need, we're an incomplete feature. alas
|
||||||
|
complete = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate the target mask into our orientation
|
||||||
|
mask = FeatureUtil.translateMask(mask, tile.orientation);
|
||||||
|
opp_mask = FeatureUtil.translateMask(opp_mask, tile.orientation);
|
||||||
|
|
||||||
|
// obtain the index of the feature on the opposing tile
|
||||||
|
int nFeatureIndex = neighbor.getFeatureIndex(opp_mask);
|
||||||
|
if (nFeatureIndex < 0) {
|
||||||
|
Log.warning("Tile mismatch while grouping [tile=" + tile +
|
||||||
|
"featIdx=" + featureIndex +
|
||||||
|
", neighbor=" + neighbor +
|
||||||
|
", nFeatIdx=" + nFeatureIndex +
|
||||||
|
", srcEdge=" + mask +
|
||||||
|
", destEdge=" + opp_mask + "].");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add this feature and its neighbors to the group
|
||||||
|
if (!enumerateGroup(tiles, neighbor, nFeatureIndex, group)) {
|
||||||
|
// if our neighbor was incomplete, we become incomplete.
|
||||||
|
// as dr. evil might say, "you incomplete me."
|
||||||
|
complete = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return complete;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locates and returns the tile with the specified coordinates.
|
* Locates and returns the tile with the specified coordinates.
|
||||||
*
|
*
|
||||||
@@ -546,6 +523,22 @@ public class TileUtil implements TileCodes
|
|||||||
return (tidx >= 0) ? (VenisonTile)tiles.get(tidx) : null;
|
return (tidx >= 0) ? (VenisonTile)tiles.get(tidx) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of piecens on the board owned the specified
|
||||||
|
* player.
|
||||||
|
*/
|
||||||
|
public static int countPiecens (DSet piecens, int playerIndex)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
Iterator iter = piecens.elements();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
if (((Piecen)iter.next()).owner == playerIndex) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the edge type for specified edge of the specified tile
|
* Returns the edge type for specified edge of the specified tile
|
||||||
* type.
|
* type.
|
||||||
@@ -572,10 +565,46 @@ public class TileUtil implements TileCodes
|
|||||||
/** Used to generate our standard tile set. */
|
/** Used to generate our standard tile set. */
|
||||||
protected static void addTiles (int count, List list, VenisonTile tile)
|
protected static void addTiles (int count, List list, VenisonTile tile)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count-1; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
list.add(tile.clone());
|
list.add(tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Used to keep track of actual features on tiles. */
|
||||||
|
protected static final class TileFeature
|
||||||
|
{
|
||||||
|
/** The tile that contains the feature. */
|
||||||
|
public VenisonTile tile;
|
||||||
|
|
||||||
|
/** The index of the feature in the tile. */
|
||||||
|
public int featureIndex;
|
||||||
|
|
||||||
|
/** Constructs a new tile feature. */
|
||||||
|
public TileFeature (VenisonTile tile, int featureIndex)
|
||||||
|
{
|
||||||
|
this.tile = tile;
|
||||||
|
this.featureIndex = featureIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Properly implement equality. */
|
||||||
|
public boolean equals (Object other)
|
||||||
|
{
|
||||||
|
if (other == null ||
|
||||||
|
!(other instanceof TileFeature)) {
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
TileFeature feat = (TileFeature)other;
|
||||||
|
return (feat.tile == tile &&
|
||||||
|
feat.featureIndex == featureIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generate a string representation. */
|
||||||
|
public String toString ()
|
||||||
|
{
|
||||||
|
return "[tile=" + tile + ", fidx=" + featureIndex + "]";
|
||||||
}
|
}
|
||||||
list.add(tile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Used to generate claim group values. */
|
/** Used to generate claim group values. */
|
||||||
|
|||||||
Reference in New Issue
Block a user