diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiBoard.java b/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiBoard.java
index 0f9b4245..a8f05a7f 100644
--- a/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiBoard.java
+++ b/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiBoard.java
@@ -1,5 +1,5 @@
//
-// $Id: AtlantiBoard.java,v 1.7 2001/10/16 09:31:46 mdb Exp $
+// $Id: AtlantiBoard.java,v 1.8 2001/10/17 02:19:54 mdb Exp $
package com.threerings.venison;
@@ -7,11 +7,14 @@ import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
+import java.util.List;
import com.samskivert.swing.Controller;
import com.samskivert.swing.util.SwingUtil;
+import com.samskivert.util.CollectionUtil;
import com.threerings.presents.dobj.DSet;
@@ -45,38 +48,97 @@ public class VenisonBoard
}
/**
- * Sets the tiles that are displayed by this board. Causes the board
- * to recompute its size based on the new tile layout.
+ * Sets the tiles to be displayed by this board. Any previous tiles
+ * are forgotten and the new tiles are initialized according to their
+ * geometry to set up initial claim groups.
*
- * @param tiles the set of {@link VenisonTile} objects to be displayed
+ * @param tset the set of {@link VenisonTile} objects to be displayed
* by this board.
*/
- public void setTiles (DSet tiles)
+ public void setTiles (DSet tset)
{
- // grab the new tiles
- _tiles = tiles;
+ // clear out our old tiles list
+ _tiles.clear();
- // update our display
- refreshTiles();
+ // copy the tiles from the set into our local list
+ CollectionUtil.addAll(_tiles, tset.elements());
+
+ // sort the list
+ Collections.sort(_tiles);
+
+ // recompute our desired dimensions and then have our parent
+ // adjust to our changed size
+ computeDimensions();
}
/**
- * Instructs the board to refresh its display in case changes have
- * occurred in the tiles set.
+ * Sets the piecens to be placed on the appropriate tiles of the
+ * board. This should only be done when first entering the game room
+ * and subsequent piecen placement should be done via {@link
+ * #placePiecen}.
*/
- public void refreshTiles ()
+ public void setPiecens (DSet piecens)
{
- // recompute our desired dimensions and then have our parent
- // adjust to our changed size
- if (_tiles != null) {
- computeDimensions();
+ // just iterate over the set placing each of the piecens in turn
+ Iterator iter = piecens.elements();
+ while (iter.hasNext()) {
+ placePiecen((Piecen)iter.next());
+ }
+ }
+
+ /**
+ * Instructs the board to add the specified tile to the display. The
+ * tile will have its claims inherited accordingly.
+ */
+ public void addTile (VenisonTile tile)
+ {
+ Log.info("Adding tile to board " + tile + ".");
+
+ // if we add a tile that is the same as our most recently placed
+ // tile, leave the placed tile. otherwise clear it out
+ if (!tile.equals(_placedTile)) {
+ _placedTile = null;
}
- // we may need to revalidate if our dimensions changed
- revalidate();
+ // add the tile
+ _tiles.add(tile);
- // we also have to repaint in case our dimensions didn't change
- repaint();
+ // resort the list
+ Collections.sort(_tiles);
+
+ // have the new tile inherit its claim groups
+ TileUtil.inheritClaims(_tiles, tile);
+
+ // recompute our desired dimensions and then have our parent
+ // adjust to our changed size
+ computeDimensions();
+ }
+
+ /**
+ * Places the specified piecen on the appropriate tile and updates
+ * claim groups as necessary.
+ */
+ public void placePiecen (Piecen piecen)
+ {
+ // if we still have a placed tile, we get rid of it
+ _placedTile = null;
+
+ Log.info("Placing " + piecen + ".");
+
+ // locate the tile associated with this piecen
+ int tidx = _tiles.indexOf(piecen);
+ if (tidx != -1) {
+ VenisonTile tile = (VenisonTile)_tiles.get(tidx);
+ // set the piecen on the tile (supplying our tile list so that
+ // the necessary claim group adjustments can be made)
+ tile.setPiecen(piecen, _tiles);
+ // and repaint
+ repaint();
+
+ } else {
+ Log.warning("Requested to place piecen for which we could " +
+ "find no associated tile! [piecen=" + piecen + "].");
+ }
}
/**
@@ -103,6 +165,7 @@ public class VenisonBoard
if (_placingTile != null) {
updatePlacingInfo(true);
}
+
// and repaint
repaint();
}
@@ -135,12 +198,10 @@ public class VenisonBoard
g.translate(_tx, _ty);
// iterate over our tiles, painting each of them
- if (_tiles != null) {
- Iterator iter = _tiles.elements();
- while (iter.hasNext()) {
- VenisonTile tile = (VenisonTile)iter.next();
- tile.paint(g2, _origX, _origY);
- }
+ int tsize = _tiles.size();
+ for (int i = 0; i < tsize; i++) {
+ VenisonTile tile = (VenisonTile)_tiles.get(i);
+ tile.paint(g2, _origX, _origY);
}
// if we have a placing tile, draw that one as well
@@ -155,8 +216,23 @@ public class VenisonBoard
g.drawRect(sx, sy, TILE_WIDTH, TILE_HEIGHT);
}
+ // if we have a recently placed tile, draw that one as well
+ if (_placedTile != null) {
+ // draw the tile
+ _placedTile.paint(g2, _origX, _origY);
+
+ // draw a white rectangle around the placed tile
+ g.setColor(Color.white);
+ int sx = (_placedTile.x + _origX) * TILE_WIDTH;
+ int sy = (_placedTile.y + _origY) * TILE_HEIGHT;
+ g.drawRect(sx, sy, TILE_WIDTH, TILE_HEIGHT);
+ }
+
// undo our translations
g.translate(-_tx, -_ty);
+
+ g.setColor(Color.black);
+ g2.draw(getBounds());
}
/** Called by our adapter when the mouse moves. */
@@ -166,11 +242,52 @@ public class VenisonBoard
_mouseX = evt.getX() - _tx;
_mouseY = evt.getY() - _ty;
- // if we have a tile to be placed, update its coordinates
if (_placingTile != null) {
+ // if we have a tile to be placed, update its coordinates
if (updatePlacingInfo(false)) {
repaint();
}
+
+ } else if (_placedTile != null && _placingPiecen) {
+ // if we have a recently placed tile, we're doing piecen
+ // placement; first convert the mouse coords into tile coords
+ int mx = _mouseX - (_placedTile.x + _origX) * TILE_WIDTH;
+ int my = _mouseY - (_placedTile.y + _origY) * TILE_HEIGHT;
+ boolean changed = false;
+
+ // now see if we're inside the placing tile
+ if (mx >= 0 && mx < TILE_WIDTH && my >= 0 && my < TILE_HEIGHT) {
+ int fidx = _placedTile.getFeatureIndex(mx, my);
+
+ // if the feature is not already claimed, we can put a
+ // piece there to indicate that it can be claimed
+ if (_placedTile.claims[fidx] == 0) {
+ if (_placedTile.piecen == null ||
+ _placedTile.piecen.featureIndex != fidx) {
+ Piecen p = new Piecen(Piecen.BLUE, 0, 0, fidx);
+ _placedTile.setPiecen(p, null);
+ changed = true;
+ }
+
+ } else {
+ // we may need to clear out a piecen since we've moved
+ if (_placedTile.piecen != null) {
+ _placedTile.clearPiecen();
+ changed = true;
+ }
+ }
+
+ } else {
+ // we may need to clear out a piecen since we've moved
+ if (_placedTile.piecen != null) {
+ _placedTile.clearPiecen();
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ repaint();
+ }
}
}
@@ -185,11 +302,25 @@ public class VenisonBoard
_placedTile = _placingTile;
_placingTile = null;
+ // inherit claims on the placed tile
+ TileUtil.inheritClaims(_tiles, _placedTile);
+
// post the action
Controller.postAction(this, TILE_PLACED);
- // and repaint
- repaint();
+ // move into placing piecen mode
+ _placingPiecen = true;
+
+ // recompute our dimensions (which will relayout or repaint)
+ computeDimensions();
+
+ } else if (_placingPiecen && _placedTile != null &&
+ _placedTile.piecen != null) {
+ // clear out placing piecen mode
+ _placingPiecen = false;
+
+ // post the action
+ Controller.postAction(this, PIECEN_PLACED);
}
}
@@ -219,8 +350,7 @@ public class VenisonBoard
// we also need to recompute the valid orientations for the
// tile in this new position
- _validOrients = TileUtil.computeValidOrients(
- _tiles.elements(), _placingTile);
+ _validOrients = TileUtil.computeValidOrients(_tiles, _placingTile);
// if we've changed positions, clear out our valid placement
// flag
@@ -290,7 +420,7 @@ public class VenisonBoard
// documentation inherited
public Dimension getPreferredSize ()
{
- if (_tiles == null) {
+ if (_tiles.size() == 0) {
return new Dimension(100, 100);
} else {
@@ -300,28 +430,33 @@ public class VenisonBoard
/**
* Determines how big we want to be based on where the tiles have been
- * laid out.
+ * laid out. This will cause the component to be re-layed out if the
+ * dimensions change or repainted if not.
*/
protected void computeDimensions ()
{
int maxX = 0, maxY = 0;
int minX = 0, minY = 0;
+ // if we have a recently placed tile, start with that one
+ if (_placedTile != null) {
+ minX = maxX = _placedTile.x;
+ minY = maxY = _placedTile.y;
+ }
+
// figure out what our boundaries are
- if (_tiles != null) {
- Iterator iter = _tiles.elements();
- while (iter.hasNext()) {
- VenisonTile tile = (VenisonTile)iter.next();
- if (tile.x > maxX) {
- maxX = tile.x;
- } else if (tile.x < minX) {
- minX = tile.x;
- }
- if (tile.y > maxY) {
- maxY = tile.y;
- } else if (tile.y < minY) {
- minY = tile.y;
- }
+ int tsize = _tiles.size();
+ for (int i = 0; i < tsize; i++) {
+ VenisonTile tile = (VenisonTile)_tiles.get(i);
+ if (tile.x > maxX) {
+ maxX = tile.x;
+ } else if (tile.x < minX) {
+ minX = tile.x;
+ }
+ if (tile.y > maxY) {
+ maxY = tile.y;
+ } else if (tile.y < minY) {
+ minY = tile.y;
}
}
@@ -329,11 +464,25 @@ public class VenisonBoard
minX -= 1; minY -= 1;
maxX += 1; maxY += 1;
+ // keep track of these to know if we've change dimensions
+ int oldOrigX = _origX, oldOrigY = _origY;
+ int oldWidth = _width, oldHeight = _height;
+
// now we can compute our width and the origin offset
_origX = -minX;
_origY = -minY;
_width = maxX - minX + 1;
_height = maxY - minY + 1;
+
+ if (_origX != oldOrigX || _origY != oldOrigY ||
+ oldWidth != _width || oldHeight != _height) {
+ // if the dimensions changed, we need to relayout
+ revalidate();
+
+ } else {
+ // otherwise just repaint
+ repaint();
+ }
}
/** Test code. */
@@ -348,26 +497,25 @@ public class VenisonBoard
set.addTile(new VenisonTile(CITY_TWO, false, WEST, -1, 1));
set.addTile(new VenisonTile(CITY_TWO, false, WEST, -1, -1));
set.addTile(new VenisonTile(CURVED_ROAD, false, WEST, 0, 2));
- VenisonTile target =
- new VenisonTile(DISCONNECTED_CITY_TWO, false, NORTH, 0, 1);
- set.addTile(target);
+ VenisonTile one = new VenisonTile(TWO_CITY_TWO, false, NORTH, 0, 1);
+ set.addTile(one);
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, false, NORTH, -1, 0));
- set.addTile(new VenisonTile(CITY_FOUR, false, NORTH, -2, 0));
+ VenisonTile two = new VenisonTile(CITY_FOUR, false, NORTH, -2, 0);
+ set.addTile(two);
board.setTiles(set);
VenisonTile placing = new VenisonTile(CITY_TWO, false, NORTH, 0, 0);
board.setTileToBePlaced(placing);
// set a feature group to test propagation
- VenisonTile[] tiles = new VenisonTile[set.size()];
- Iterator iter = set.elements();
- for (int i = 0; iter.hasNext(); i++) {
- tiles[i] = (VenisonTile)iter.next();
- }
- Arrays.sort(tiles);
- TileUtil.setFeatureGroup(tiles, target, 0, 1, 0);
+ List tiles = new ArrayList();
+ CollectionUtil.addAll(tiles, set.elements());
+ Collections.sort(tiles);
+
+ one.setPiecen(new Piecen(Piecen.BLUE, 0, 0, 0), tiles);
+ two.setPiecen(new Piecen(Piecen.BLUE, 0, 0, 0), tiles);
frame.getContentPane().add(board, BorderLayout.CENTER);
frame.pack();
@@ -389,7 +537,7 @@ public class VenisonBoard
}
/** A reference to our tile set. */
- protected DSet _tiles;
+ protected ArrayList _tiles = new ArrayList();
/** The tile currently being placed by the user. */
protected VenisonTile _placingTile;
@@ -397,6 +545,9 @@ public class VenisonBoard
/** The last tile being placed by the user. */
protected VenisonTile _placedTile;
+ /** A flag indicating whether or not we're placing a piecen. */
+ protected boolean _placingPiecen = false;
+
/** Whether or not the current position and orientation of the placing
* tile is valid. */
protected boolean _validPlacement = false;
diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiController.java b/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiController.java
index 85f9e21f..7e1eba55 100644
--- a/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiController.java
+++ b/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiController.java
@@ -7,7 +7,11 @@ import java.awt.event.ActionEvent;
import com.threerings.presents.dobj.AttributeChangedEvent;
import com.threerings.presents.dobj.DSet;
+import com.threerings.presents.dobj.ElementAddedEvent;
+import com.threerings.presents.dobj.ElementRemovedEvent;
+import com.threerings.presents.dobj.ElementUpdatedEvent;
import com.threerings.presents.dobj.MessageEvent;
+import com.threerings.presents.dobj.SetListener;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.PlaceObject;
@@ -23,7 +27,7 @@ import com.threerings.venison.Log;
* the Venison game.
*/
public class VenisonController
- extends TurnGameController implements VenisonCodes
+ extends TurnGameController implements VenisonCodes, SetListener
{
// documentation inherited
protected void didInit ()
@@ -48,6 +52,14 @@ public class VenisonController
// get a casted reference to our game object
_venobj = (VenisonObject)plobj;
+
+ // grab the tiles and piecens from the game object and configure
+ // the board with them
+ _panel.board.setTiles(_venobj.tiles);
+ _panel.board.setPiecens(_venobj.piecens);
+
+ // TBD: check to see if it's our turn and set things up
+ // accordingly
}
// documentation inherited
@@ -61,9 +73,6 @@ public class VenisonController
Log.info("Setting tile to be placed: " + _venobj.currentTile);
_panel.board.setTileToBePlaced(_venobj.currentTile);
}
-
- // and refresh the tiles
- _panel.board.refreshTiles();
}
// documentation inherited
@@ -74,21 +83,74 @@ public class VenisonController
// handle the setting of the board state
if (event.getName().equals(VenisonObject.TILES)) {
_panel.board.setTiles(_venobj.tiles);
+
+ } else if (event.getName().equals(VenisonObject.PIECENS)) {
+ _panel.board.setPiecens(_venobj.piecens);
}
}
+ // documentation inherited
+ public void elementAdded (ElementAddedEvent event)
+ {
+ // we care about additions to TILES and PIECENS
+ if (event.getName().equals(VenisonObject.TILES)) {
+ // a tile was added, add it to the board
+ VenisonTile tile = (VenisonTile)event.getElement();
+ _panel.board.addTile(tile);
+
+ } else if (event.getName().equals(VenisonObject.PIECENS)) {
+ // a piecen was added, place it on the board
+ Piecen piecen = (Piecen)event.getElement();
+ _panel.board.placePiecen(piecen);
+ }
+ }
+
+ // documentation inherited
+ public void elementUpdated (ElementUpdatedEvent event)
+ {
+ }
+
+ // documentation inherited
+ public void elementRemoved (ElementRemovedEvent event)
+ {
+ }
+
// documentation inherited
public boolean handleAction (ActionEvent action)
{
if (action.getActionCommand().equals(TILE_PLACED)) {
// the user placed the tile into a valid location. grab the
- // placed tile from the board and submit it to the server as
- // our move
+ // placed tile from the board and submit it to the server
Object[] args = new Object[] { _panel.board.getPlacedTile() };
MessageEvent mevt = new MessageEvent(
_venobj.getOid(), PLACE_TILE_REQUEST, args);
_ctx.getDObjectManager().postEvent(mevt);
+ // enable the noplace button
+ _panel.noplace.setEnabled(true);
+
+ } else if (action.getActionCommand().equals(PIECEN_PLACED)) {
+ // the user placed a piecen on the tile. grab the piecen from
+ // the placed tile and submit it to the server
+ Object[] args = new Object[] {
+ _panel.board.getPlacedTile().piecen };
+ MessageEvent mevt = new MessageEvent(
+ _venobj.getOid(), PLACE_PIECEN_REQUEST, args);
+ _ctx.getDObjectManager().postEvent(mevt);
+
+ // disable the noplace button
+ _panel.noplace.setEnabled(false);
+
+ } else if (action.getActionCommand().equals(PLACE_NOTHING)) {
+ // the user doesn't want to place anything this turn. send a
+ // place nothing request to the server
+ MessageEvent mevt = new MessageEvent(
+ _venobj.getOid(), PLACE_NOTHING_REQUEST, null);
+ _ctx.getDObjectManager().postEvent(mevt);
+
+ // disable the noplace button
+ _panel.noplace.setEnabled(false);
+
} else {
return super.handleAction(action);
}
diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiPanel.java b/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiPanel.java
index 81f16cbb..aebc5046 100644
--- a/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiPanel.java
+++ b/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiPanel.java
@@ -20,11 +20,14 @@ import com.threerings.parlor.util.ParlorContext;
* The top-level user interface component for the Venison game display.
*/
public class VenisonPanel
- extends JPanel implements PlaceView, ControllerProvider
+ extends JPanel implements PlaceView, ControllerProvider, VenisonCodes
{
/** A reference to the board that is accessible to the controller. */
public VenisonBoard board;
+ /** A reference to our _noplace button. */
+ public JButton noplace;
+
/**
* Constructs a new Venison game display.
*/
@@ -49,6 +52,13 @@ public class VenisonPanel
// add a turn indicator to the side panel
sidePanel.add(new TurnIndicatorView(), VGroupLayout.FIXED);
+ // add a "place nothing" button
+ noplace = new JButton("Place nothing");
+ noplace.setEnabled(false);
+ noplace.setActionCommand(PLACE_NOTHING);
+ noplace.addActionListener(Controller.DISPATCHER);
+ sidePanel.add(noplace, VGroupLayout.FIXED);
+
// add our side panel to the main display
add(sidePanel, HGroupLayout.FIXED);
diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiCodes.java b/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiCodes.java
index 8e03a268..7e0e7a24 100644
--- a/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiCodes.java
+++ b/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiCodes.java
@@ -1,5 +1,5 @@
//
-// $Id: AtlantiCodes.java,v 1.1 2001/10/12 20:34:13 mdb Exp $
+// $Id: AtlantiCodes.java,v 1.2 2001/10/17 02:19:54 mdb Exp $
package com.threerings.venison;
@@ -12,6 +12,14 @@ public interface VenisonCodes
* the user places a tile into a valid position. */
public static final String TILE_PLACED = "tile_placed";
+ /** The name of the command posted by the {@link VenisonBoard} when
+ * the user places a piecen onto an unclaimed feature. */
+ public static final String PIECEN_PLACED = "piecen_placed";
+
+ /** The name of the command posted by the "place nothing" button in
+ * the side bar. */
+ public static final String PLACE_NOTHING = "place_nothing";
+
/** The message submitted by the client to the server when they have
* chosen where they wish to place their tile. */
public static final String PLACE_TILE_REQUEST = "place_tile";
@@ -19,4 +27,8 @@ public interface VenisonCodes
/** The message submitted by the client to the server when they have
* chosen where they wish to place their piecen. */
public static final String PLACE_PIECEN_REQUEST = "place_piecen";
+
+ /** The message submitted by the client to the server when they decide
+ * that they don't want to (or can't) place any piecen this turn. */
+ public static final String PLACE_NOTHING_REQUEST = "place_nothing";
}
diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiObject.java b/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiObject.java
index b9ae8441..a83d3f22 100644
--- a/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiObject.java
+++ b/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiObject.java
@@ -1,5 +1,5 @@
//
-// $Id: AtlantiObject.java,v 1.5 2001/10/16 17:11:58 mdb Exp $
+// $Id: AtlantiObject.java,v 1.6 2001/10/17 02:19:54 mdb Exp $
package com.threerings.venison;
@@ -17,6 +17,9 @@ public class VenisonObject extends TurnGameObject
/** The field name of the currentTile field. */
public static final String CURRENT_TILE = "currentTile";
+ /** The field name of the piecens field. */
+ public static final String PIECENS = "piecens";
+
/** A set containing all of the tiles that are in play in this
* game. */
public DSet tiles = new DSet(VenisonTile.class);
@@ -25,6 +28,10 @@ public class VenisonObject extends TurnGameObject
* only valid while it is someone's turn. */
public VenisonTile currentTile = VenisonTile.STARTING_TILE;
+ /** A set containing all of the piecens that are placed on the
+ * board. */
+ public DSet piecens = new DSet(Piecen.class);
+
/**
* Requests that the tiles field be set to the specified
* value.
@@ -70,11 +77,48 @@ public class VenisonObject extends TurnGameObject
requestAttributeChange(CURRENT_TILE, value);
}
+ /**
+ * Requests that the piecens field be set to the specified
+ * value.
+ */
+ public void setPiecens (DSet value)
+ {
+ requestAttributeChange(PIECENS, value);
+ }
+
+ /**
+ * Requests that the specified element be added to the
+ * piecens set.
+ */
+ public void addToPiecens (DSet.Element elem)
+ {
+ requestElementAdd(PIECENS, elem);
+ }
+
+ /**
+ * Requests that the element matching the supplied key be removed from
+ * the piecens set.
+ */
+ public void removeFromPiecens (Object key)
+ {
+ requestElementRemove(PIECENS, key);
+ }
+
+ /**
+ * Requests that the specified element be updated in the
+ * piecens set.
+ */
+ public void updatePiecens (DSet.Element elem)
+ {
+ requestElementUpdate(PIECENS, elem);
+ }
+
// documentation inherited
protected void toString (StringBuffer buf)
{
super.toString(buf);
buf.append(", tiles=").append(tiles);
buf.append(", currentTile=").append(currentTile);
+ buf.append(", piecens=").append(piecens);
}
}
diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiTile.java b/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiTile.java
index 6668db85..d4a5ad2a 100644
--- a/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiTile.java
+++ b/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiTile.java
@@ -1,5 +1,5 @@
//
-// $Id: AtlantiTile.java,v 1.7 2001/10/16 17:12:32 mdb Exp $
+// $Id: AtlantiTile.java,v 1.8 2001/10/17 02:19:54 mdb Exp $
package com.threerings.venison;
@@ -7,16 +7,13 @@ import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.GeneralPath;
-
import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
-import java.util.ArrayList;
-
+import java.util.List;
import com.samskivert.util.IntTuple;
+import com.samskivert.util.StringUtil;
import com.threerings.presents.dobj.DSet;
@@ -50,11 +47,11 @@ public class VenisonTile
public int[] claims;
/** A reference to our static feature descriptions. */
- public int[] features;
+ public Feature[] features;
/** A reference to the piecen on this tile or null if no piecen has
* been placed on this tile. */
- // public Piecen piecen;
+ public Piecen piecen;
/**
* Constructs a tile with all of the supplied tile information.
@@ -97,15 +94,15 @@ public class VenisonTile
* @return the index of the matching feature or -1 if no feature
* matched.
*/
- public int getFeatureIndex (int featureMask)
+ public int getFeatureIndex (int edgeMask)
{
// translate the feature mask into our orientation
- featureMask = TileUtil.translateMask(featureMask, -orientation);
+ edgeMask = FeatureUtil.translateMask(edgeMask, -orientation);
- for (int i = 0; i < features.length; i += 2) {
- int fmask = features[i+1];
- if ((fmask & featureMask) != 0) {
- return i/2;
+ // look for a feature with a matching edge mask
+ for (int i = 0; i < features.length; i ++) {
+ if ((features[i].edgeMask & edgeMask) != 0) {
+ return i;
}
}
@@ -113,6 +110,32 @@ public class VenisonTile
return -1;
}
+ /**
+ * Returns the index of the feature that contains the supplied mouse
+ * coordinates (which will have been translated relative to the tile's
+ * origin).
+ *
+ * @return the index of the feature that contains the mouse
+ * coordinates. Some feature should always contain the mouse.
+ */
+ public int getFeatureIndex (int mouseX, int mouseY)
+ {
+ // we search our features in reverse order because road features
+ // overlap grass features geometrically and are known to be
+ // specified after the grass features
+ for (int i = features.length-1; i >= 0; i--) {
+ if (features[i].contains(mouseX, mouseY, orientation)) {
+ return i;
+ }
+ }
+
+ // something is hosed; fake it
+ Log.warning("Didn't find matching feature for mouse coordinates!? " +
+ "[tile=" + this + ", mx=" + mouseX +
+ ", my=" + mouseY + "].");
+ return 0;
+ }
+
/**
* Looks for a feature in this tile that matches the supplied feature
* edge mask and returns the claim group to which that feature belongs
@@ -122,9 +145,9 @@ public class VenisonTile
* supplied mask belongs, or zero if no feature matched the supplied
* mask.
*/
- public int getFeatureGroup (int featureMask)
+ public int getFeatureGroup (int edgeMask)
{
- int fidx = getFeatureIndex(featureMask);
+ int fidx = getFeatureIndex(edgeMask);
return fidx < 0 ? 0 : claims[fidx];
}
@@ -138,11 +161,67 @@ public class VenisonTile
*/
public void setFeatureGroup (int featureIndex, int claimGroup)
{
- Log.info("Setting feature group [tile=" + this +
- ", fidx=" + featureIndex + ", cgroup=" + claimGroup + "].");
+// Log.info("Setting feature group [tile=" + this +
+// ", fidx=" + featureIndex + ", cgroup=" + claimGroup + "].");
+
+ // update the claim group slot for this feature
claims[featureIndex] = claimGroup;
- // TBD: update the piecen
+ // if we have a piecen placed on the feature identified by this
+ // feature index, we need to update its claim group as well
+ if (piecen != null && piecen.featureIndex == featureIndex) {
+ piecen.claimGroup = claimGroup;
+ }
+ }
+
+ /**
+ * Places the specified piecen on this tile. The {@link
+ * Piecen#featureIndex} field is assumed to be initialized to the
+ * feature index of this tile on which the piecen is to be placed.
+ *
+ *
Note that this will call {@link TileUtil#setFeatureGroup} to + * propagate the claiming of this feature to all neighboring tiles if + * a non-null tiles array is supplied to the function. + * + * @param piecen the piecen to place on this tile (with an + * appropriately configured feature index). + * @param tiles a sorted list of all of the tiles on the board that + * we can use to propagate our new claim group to all features + * connected to this newly claimed feature or null if propagation of + * the claim group is not desired at this time. + */ + public void setPiecen (Piecen piecen, List tiles) + { + // are we sure about that? + if (claims[piecen.featureIndex] != 0) { + Log.warning("Aiya! Requested to add a piecen to a feature " + + "that has already been claimed [tile=" + this + + ", piecen=" + piecen + "]."); + } + + // keep a reference to this piecen and configure its position + this.piecen = piecen; + piecen.x = x; + piecen.y = y; + + // assign a brand spanking new claim group to the feature and the + // piecen and propagate it to neighboring features + if (tiles != null) { + int claimGroup = TileUtil.nextClaimGroup(); + Log.info("Creating claim group [cgroup=" + claimGroup + + ", tile=" + this + ", fidx=" + piecen.featureIndex + "]."); + TileUtil.setFeatureGroup( + tiles, this, piecen.featureIndex, claimGroup, 0); + } + } + + /** + * Clears out any piecen reference that was previously set (does not + * clear out its associated claim group, however). + */ + public void clearPiecen () + { + piecen = null; } /** @@ -160,11 +239,6 @@ public class VenisonTile { int tidx = type-1; - // create our shapes if we haven't already - if (_shapes[tidx][orientation] == null) { - createShapes(); - } - // compute our screen coordinates int sx = (x + xoff) * TILE_WIDTH; int sy = (y + yoff) * TILE_HEIGHT; @@ -172,16 +246,16 @@ public class VenisonTile // translate to our screen coordinates g.translate(sx, sy); - // draw our shapes using the proper orientation - GeneralPath[] paths = _shapes[tidx][orientation]; - IntTuple[] types = _types[tidx]; - for (int i = 0; i < paths.length; i++) { - if (claims[types[i].right] != 0) { - g.setColor(CLAIMED_COLOR_MAP[types[i].left]); - } else { - g.setColor(COLOR_MAP[types[i].left]); + // render our features and piecen + for (int i = 0; i < features.length; i++) { + // paint the feature + features[i].paint(g, orientation, claims[i]); + + // if we have a piecen on this tile, render it as well + if (piecen != null && piecen.featureIndex == i) { + features[i].paintPiecen( + g, orientation, piecen.color, piecen.claimGroup); } - g.fill(paths[i]); } // draw a rectangular outline @@ -205,9 +279,12 @@ public class VenisonTile */ public int compareTo (Object other) { - // we will either be compared to another tile or to a coordinate - // object - if (other instanceof VenisonTile) { + // we will either be compared to another tile, to a piecen or to a + // coordinate object (int tuple) + if (other == null) { + return -1; + + } else if (other instanceof VenisonTile) { VenisonTile tile = (VenisonTile)other; return (tile.x == x) ? y - tile.y : x - tile.x; @@ -215,12 +292,22 @@ public class VenisonTile IntTuple coord = (IntTuple)other; return (coord.left == x) ? y - coord.right : x - coord.left; + } else if (other instanceof Piecen) { + Piecen piecen = (Piecen)other; + return (piecen.x == x) ? y - piecen.y : x - piecen.x; + } else { // who knows... return -1; } } + // documentation inherited + public boolean equals (Object other) + { + return (compareTo(other) == 0); + } + // documentation inherited public Object getKey () { @@ -258,10 +345,10 @@ public class VenisonTile { if (type > 0) { // grab a reference to our feature information - features = TileUtil.TILE_FEATURES[type-1]; + features = FeatureUtil.getTileFeatures(type); // create our claims array - claims = new int[features.length/2]; + claims = new int[features.length]; } else { Log.warning("Requested to init features without valid type " + @@ -270,88 +357,6 @@ public class VenisonTile } } - /** - * Creates the path objects that describe the shapes that make up this - * tile and sticks it into the appropriate slot in the shapes array. - */ - protected void createShapes () - { - int tidx = type-1; - ArrayList polys = new ArrayList(); - ArrayList types = new ArrayList(); - - // the first feature is the background color - Object[] features = (Object[])TileUtil.TILE_FEATURE_GEOMS[tidx]; - IntTuple base = (IntTuple)features[0]; - - // add a polygon containing the whole tile - types.add(base); - Polygon poly = new Polygon(); - poly.addPoint(0, 0); - poly.addPoint(TILE_WIDTH, 0); - poly.addPoint(TILE_WIDTH, TILE_HEIGHT); - poly.addPoint(0, TILE_HEIGHT); - polys.add(poly); - - // the remainder are tuple/coordinate pairs - for (int f = 1; f < features.length; f += 2) { - IntTuple ftype = (IntTuple)features[f]; - int[] coords = (int[])features[f+1]; - - // keep track of this shape's type - types.add(ftype); - - // if this is a road segment, we need to create a special - // polygon - if (ftype.left == ROAD) { - poly = TileUtil.roadSegmentToPolygon( - coords[0], coords[1], coords[2], coords[3]); - - } else { - // otherwise create the polygon directly from the coords - poly = new Polygon(); - for (int c = 0; c < coords.length; c += 2) { - // scale the coords accordingly - int fx = (coords[c] * TILE_WIDTH) / 4; - int fy = (coords[c+1] * TILE_HEIGHT) / 4; - poly.addPoint(fx, fy); - } - } - - polys.add(poly); - } - - // now create general paths from our polygons and convert - // everything into the appropriate arrays - GeneralPath[] paths = new GeneralPath[polys.size()]; - for (int i = 0; i < polys.size(); i++) { - GeneralPath path = new GeneralPath(); - path.append(((Polygon)polys.get(i)).getPathIterator(null), false); - path.closePath(); - paths[i] = path; - } - - // keep the first one around - _shapes[tidx][NORTH] = paths; - - // and rotate it three times to get the other orientations - AffineTransform xform = new AffineTransform(); - for (int o = 1; o < 4; o++) { - xform.translate(TILE_WIDTH, 0); - xform.rotate(Math.PI/2); - GeneralPath[] rpaths = new GeneralPath[paths.length]; - for (int i = 0; i < paths.length; i++) { - rpaths[i] = (GeneralPath)paths[i].clone(); - rpaths[i].transform(xform); - } - _shapes[tidx][o] = rpaths; - } - - // also fill in the feature type info - _types[tidx] = new IntTuple[types.size()]; - types.toArray(_types[tidx]); - } - /** * Generates a string representation of this tile instance. */ @@ -359,30 +364,8 @@ public class VenisonTile { return "[type=" + type + ", shield=" + hasShield + ", orient=" + ORIENT_NAMES[orientation] + - ", pos=" + x + "/" + y + "]"; + ", pos=" + x + "/" + y + + ", claims=" + StringUtil.toString(claims) + + ", piecen=" + piecen + "]"; } - - /** Path objects that describe closed shapes which be used to render - * the tiles (one for each orientation of each tile type). */ - protected static GeneralPath[][][] _shapes = - new GeneralPath[TILE_TYPES][4][]; - - /** The feature type of each shape in shapes array. */ - protected static IntTuple[][] _types = new IntTuple[TILE_TYPES][]; - - /** Maps feature types to colors. */ - protected static Color[] COLOR_MAP = { - Color.red, // CITY - Color.green, // GRASS - Color.black, // ROAD - Color.yellow // CLOISTER - }; - - /** Maps feature types to colors for claimed features. */ - protected static Color[] CLAIMED_COLOR_MAP = { - Color.red.darker(), // CITY - Color.green.darker(), // GRASS - Color.black.brighter(), // ROAD - Color.yellow.darker(), // CLOISTER - }; } diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/data/Feature.java b/projects/atlanti/src/java/com/samskivert/atlanti/data/Feature.java new file mode 100644 index 00000000..1a4983dc --- /dev/null +++ b/projects/atlanti/src/java/com/samskivert/atlanti/data/Feature.java @@ -0,0 +1,366 @@ +// +// $Id: Feature.java,v 1.1 2001/10/17 02:19:54 mdb Exp $ + +package com.threerings.venison; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Shape; + +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.RoundRectangle2D; + +import com.samskivert.util.StringUtil; + +/** + * Represents all of the necessary information for a particular feature of + * a particular tile. + */ +public class Feature + implements TileCodes +{ + /** The type of this feature. */ + public int type; + + /** The edge mask associated with this feature. */ + public int edgeMask; + + /** The location at which a piecen on this feature would be drawn (for + * each of the four tile orientations. */ + public Point2D[] piecenSpots = new Point2D[4]; + + /** The polygons used to render and hit test this feature (one for + * each orientation). */ + public GeneralPath[] polys = new GeneralPath[4]; + + /** + * Constructs a feature with a feature description array containing + * all of the necessary feature information. + */ + public Feature (int[] desc) + { + type = desc[0]; + edgeMask = desc[1]; + + // fetch our natural piecen spot, scale it and adjust for half units + int px = (desc[2] * TILE_WIDTH) / 4; + if (px < 0) { + px *= -1; + px -= TILE_WIDTH/8; + } + int py = (desc[3] * TILE_HEIGHT) / 4; + if (py < 0) { + py *= -1; + py -= TILE_HEIGHT/8; + } + piecenSpots[NORTH] = new Point2D.Float(px, py); + + // create our natural feature polygon + if (type == ROAD) { + // roads are handled specially; they are either one or two + // segment roads + if (desc.length == 8) { + polys[NORTH] = roadSegmentToPolygon( + desc[4], desc[5], desc[6], desc[7]); + + } else if (desc.length == 10) { + polys[NORTH] = roadSegmentToPolygon( + desc[4], desc[5], desc[6], desc[7], desc[8], desc[9]); + + } else { + Log.warning("Feature constructed with bogus road geometry " + + "[desc=" + StringUtil.toString(desc) + "]."); + } + + } else { + GeneralPath poly = new GeneralPath(); + for (int i = 4; i < desc.length; i += 2) { + // scale the coords accordingly + int fx = (desc[i] * TILE_WIDTH) / 4; + int fy = (desc[i+1] * TILE_HEIGHT) / 4; + if (i == 4) { + poly.moveTo(fx, fy); + } else { + poly.lineTo(fx, fy); + } + } + poly.closePath(); + polys[NORTH] = poly; + } + + // now create the three other orientations + AffineTransform xform = new AffineTransform(); + for (int orient = 1; orient < 4; orient++) { + // rotate the xform into the next orientation + xform.translate(TILE_WIDTH, 0); + xform.rotate(Math.PI/2); + + // transform the polygon + polys[orient] = (GeneralPath)polys[NORTH].clone(); + polys[orient].transform(xform); + + // transform the piecen spot + piecenSpots[orient] = new Point2D.Float(); + xform.transform(piecenSpots[NORTH], piecenSpots[orient]); + } + } + + /** + * Returns true if the feature contains the supplied mouse coordinates + * (which should be relative to the tile origin) given the supplied + * orientation information. + */ + public boolean contains (int mouseX, int mouseY, int orientation) + { + return polys[orientation].contains(mouseX, mouseY); + } + + /** + * Paints this feature to the specified graphics context with the + * specified orientation, accounting for the supplied x and y offsets + * of the origin. The graphics context is assumed to be appropriately + * translated so that we may render this feature relative to the + * origin and have it painted in the proper place. + * + * @param g the graphics context to use when painting the feature. + * @param orientation the orientation at which to paint this feature. + * @param claimGroup the claim group to which this feature belongs (or + * zero if it belongs to no claim group). + */ + public void paint (Graphics2D g, int orientation, int claimGroup) + { + // set the color according to the claim group + g.setColor(claimGroup > 0 ? CLAIMED_COLOR_MAP[type] : + COLOR_MAP[type]); + + // now render the appropriate polygon for this orientation + g.fill(polys[orientation]); + +// // paint our features for debugging (they aren't rotated and thus +// // are only valid in the natural orientation) +// int w = TILE_WIDTH, hw = w/2, qw = hw/2, tqw = 3*qw; +// int h = TILE_HEIGHT, hh = h/2, qh = hh/2, tqh = 3*qh; +// g.setColor(Color.gray); + +// if ((edgeMask & FeatureUtil.NORTH_F) != 0) { +// g.drawLine(hw, 0, hw, qh); +// } +// if ((edgeMask & FeatureUtil.EAST_F) != 0) { +// g.drawLine(tqw, hh, w, hh); +// } +// if ((edgeMask & FeatureUtil.SOUTH_F) != 0) { +// g.drawLine(hw, tqh, hw, h); +// } +// if ((edgeMask & FeatureUtil.WEST_F) != 0) { +// g.drawLine(0, hh, qw, hh); +// } + +// if ((edgeMask & FeatureUtil.NNE_F) != 0) { +// g.drawLine(tqw, 0, tqw, qh); +// } +// if ((edgeMask & FeatureUtil.NNW_F) != 0) { +// g.drawLine(qw, 0, qw, qh); +// } +// if ((edgeMask & FeatureUtil.SSE_F) != 0) { +// g.drawLine(tqw, tqh, tqw, h); +// } +// if ((edgeMask & FeatureUtil.SSW_F) != 0) { +// g.drawLine(qw, tqh, qw, h); +// } + +// if ((edgeMask & FeatureUtil.ENE_F) != 0) { +// g.drawLine(tqw, qh, w, qh); +// } +// if ((edgeMask & FeatureUtil.ESE_F) != 0) { +// g.drawLine(tqw, tqh, w, tqh); +// } +// if ((edgeMask & FeatureUtil.WNW_F) != 0) { +// g.drawLine(0, qh, qw, qh); +// } +// if ((edgeMask & FeatureUtil.WSW_F) != 0) { +// g.drawLine(0, tqh, qw, tqh); +// } + } + + /** + * Paints a piecen in a position indicating that it is placed on this + * feature. The graphics context is assumed to be appropriately + * translated so that we may render this feature relative to the + * origin and have it painted in the proper place. + * + * @param g the graphics context to use when painting the feature. + * @param orientation the orientation at which to paint this feature. + * @param color the piecen color to be painted. + * @param claimGroup the claim group to which this piecen belongs (or + * zero if it belongs to no claim group). + */ + public void paintPiecen (Graphics2D g, int orientation, int color, + int claimGroup) + { + // paint a colored circle around the proper piecen spot + Point2D point = piecenSpots[orientation]; + double swidth = TILE_WIDTH/5; + double sheight = TILE_HEIGHT/5; + + Shape spot = new RoundRectangle2D.Double( + point.getX()-swidth/2, point.getY()-sheight/2, + swidth, sheight, swidth/4, sheight/4); + g.setColor(PIECEN_COLOR_MAP[color]); + g.fill(spot); + + // fucking ridiculous: draw()ing the same shape that was fill()ed + // doesn't generate the same shape. a draw()n shape is one pixel + // larger than a fill()ed shape (probably to account for some + // backwards compatible bad decision), so we have to create a + // whole new shape object to generate the outline. thanks sun + spot = new RoundRectangle2D.Double( + point.getX()-swidth/2, point.getY()-sheight/2, + swidth-1, sheight-1, swidth/4, sheight/4); + g.setColor(Color.black); + g.draw(spot); + + // for now, draw the claim group next to the piecen + g.drawString(Integer.toString(claimGroup), + (float)(point.getX() - swidth/2 + 2), + (float)(point.getY() - sheight/2 - 2)); + } + + /** + * Massages a road segment (specified in tile feature coordinates) + * into a polygon (in screen coordinates) that can be used to render + * or hit test the road. The coordinates must obey the following + * constraints: (x1 < x2 and y1 == y2) or (x1 == x2 and y1 < y2) or + * (x1 < x2 and y1 > y2). + * + * @return a polygon representing the road segment (with origin at 0, + * 0). + */ + protected static GeneralPath roadSegmentToPolygon ( + int x1, int y1, int x2, int y2) + { + // first convert the coordinates into screen coordinates + x1 = (x1 * TILE_WIDTH) / 4; + y1 = (y1 * TILE_HEIGHT) / 4; + x2 = (x2 * TILE_WIDTH) / 4; + y2 = (y2 * TILE_HEIGHT) / 4; + + GeneralPath poly = new GeneralPath(); + int dx = 4, dy = 4; + + // figure out what sort of line segment it is + if (x1 == x2) { // vertical + // make adjustments to ensure that we stay inside the tile + // bounds + if (y1 == 0) { + y1 += dy; + } + if (y2 == TILE_HEIGHT) { + y2 -= dy; + } + poly.moveTo(x1 - dx, y1 - dy); + poly.lineTo(x1 + dx, y1 - dy); + poly.lineTo(x2 + dx, y2 + dy); + poly.lineTo(x2 - dx, y2 + dy); + + } else if (y1 == y2) { // horizontal + // make adjustments to ensure that we stay inside the tile + // bounds + if (x1 == 0) { + x1 += dx; + } + if (x2 == TILE_WIDTH) { + x2 -= dx; + } + poly.moveTo(x1 - dx, y1 - dy); + poly.lineTo(x1 - dx, y1 + dy); + poly.lineTo(x2 + dx, y2 + dy); + poly.lineTo(x2 + dx, y2 - dy); + + } else { // diagonal + poly.moveTo(x1 - dx, y1); + poly.lineTo(x1 + dx, y1); + poly.lineTo(x2, y2 + dy); + poly.lineTo(x2, y2 - dy); + } + + poly.closePath(); + return poly; + } + + /** + * Massages a road segment (specified in tile feature coordinates) + * into a polygon (in screen coordinates) that can be used to render + * or hit test the road. The coordinates must obey the following + * constraints: (y1 == y2) and (y2 > y3) and (x2 == x3). + * + * @return a polygon representing the road segment (with origin at 0, + * 0). + */ + protected static GeneralPath roadSegmentToPolygon ( + int x1, int y1, int x2, int y2, int x3, int y3) + { + // first convert the coordinates into screen coordinates + x1 = (x1 * TILE_WIDTH) / 4; + y1 = (y1 * TILE_HEIGHT) / 4; + x2 = (x2 * TILE_WIDTH) / 4; + y2 = (y2 * TILE_HEIGHT) / 4; + x3 = (x3 * TILE_WIDTH) / 4; + y3 = (y3 * TILE_HEIGHT) / 4; + + GeneralPath poly = new GeneralPath(); + int dx = 4, dy = 4; + + // figure out what sort of road segment it is + if (x1 < x2) { // left turn + poly.moveTo(x1, y1-dy); + poly.lineTo(x2+dx, y2-dy); + poly.lineTo(x3+dx, y3); + poly.lineTo(x3-dx, y3); + poly.lineTo(x2-dx, y2+dy); + poly.lineTo(x1, y1+dy); + + } else { // right turn + poly.moveTo(x1, y1-dy); + poly.lineTo(x2-dx, y2-dy); + poly.lineTo(x3-dx, y3); + poly.lineTo(x3+dx, y3); + poly.lineTo(x2+dx, y2+dy); + poly.lineTo(x1, y1+dy); + } + + poly.closePath(); + return poly; + } + + public String toString () + { + return "[type=" + type + ", edgeMask=" + edgeMask + "]"; + } + + /** Maps feature types to colors. */ + protected static Color[] COLOR_MAP = { + Color.red, // CITY + Color.green, // GRASS + Color.black, // ROAD + Color.yellow // CLOISTER + }; + + /** Maps feature types to colors for claimed features. */ + protected static Color[] CLAIMED_COLOR_MAP = { + Color.red.darker(), // CITY + Color.green.darker(), // GRASS + Color.darkGray, // ROAD + Color.yellow.darker(), // CLOISTER + }; + + /** Maps piecen color codes to colors. */ + protected static Color[] PIECEN_COLOR_MAP = { + Color.red, // RED + Color.darkGray, // BLACK + Color.blue, // BLUE + Color.yellow, // YELLOW + Color.green, // GREEN + }; +} diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/data/Piecen.java b/projects/atlanti/src/java/com/samskivert/atlanti/data/Piecen.java new file mode 100644 index 00000000..19c82704 --- /dev/null +++ b/projects/atlanti/src/java/com/samskivert/atlanti/data/Piecen.java @@ -0,0 +1,133 @@ +// +// $Id: Piecen.java,v 1.1 2001/10/17 02:19:54 mdb Exp $ + +package com.threerings.venison; + +import java.io.IOException; +import java.io.DataInputStream; +import java.io.DataOutputStream; + +import com.threerings.presents.dobj.DSet; + +/** + * A piecen is a person and a piece all rolled into one! Players can play + * a single piecen on a tile feature as a part of their turn and that + * piecen then claims that feature and all of the features that are + * connected to it forming a claim group. Once a group of features has + * been claimed, no further piecens can be placed on the group. Note, + * however, that two or more separately claimed feature groups can be + * joined by placing a tile that connects the previously disconnected + * groups together. In that case, the groups are merged into a single + * claim group and all the piecens that were part of the disparate groups + * become claimees in the new group. At scoring time, the player with the + * most piecens in a group gets the points for the group. If two or more + * players have equal numbers of piecens in a group, they each get the + * points for the group. + */ +public class Piecen + implements DSet.Element +{ + /** A color constant. */ + public static final int RED = 0; + + /** A color constant. */ + public static final int BLACK = 1; + + /** A color constant. */ + public static final int BLUE = 2; + + /** A color constant. */ + public static final int YELLOW = 3; + + /** A color constant. */ + public static final int GREEN = 4; + + /** The color of this piecen. */ + public int color; + + /** The x and y coordinates of the tile on which this piecen is + * placed. */ + public int x, y; + + /** The index in the tile's feature array of the feature on which this + * piecen is placed. */ + public int featureIndex; + + /** The claim group to which this piecen belongs. */ + public int claimGroup; + + /** + * Construts a piecen with the specified configuration. + */ + public Piecen (int color, int x, int y, int featureIndex) + { + this.color = color; + this.x = x; + this.y = y; + this.featureIndex = featureIndex; + } + + /** + * Constructs a blank piecen, suitable for unserialization. + */ + public Piecen () + { + } + + // documentation inherited + public Object getKey () + { + // our key is our coordinates conflated into one integer + return new Integer((x + 128) * 256 + y + 128); + } + + // documentation inherited + public void writeTo (DataOutputStream out) + throws IOException + { + out.writeInt(color); + out.writeInt(x); + out.writeInt(y); + out.writeInt(featureIndex); + } + + // documentation inherited + public void readFrom (DataInputStream in) + throws IOException + { + color = in.readInt(); + x = in.readInt(); + y = in.readInt(); + featureIndex = in.readInt(); + } + + // documentation inherited + public boolean equals (Object other) + { + // we will either be compared to a tile or to a piecen + if (other == null) { + return false; + + } else if (other instanceof VenisonTile) { + VenisonTile tile = (VenisonTile)other; + return (tile.x == x) ? (y == tile.y) : false; + + } else if (other instanceof Piecen) { + Piecen piecen = (Piecen)other; + return (piecen.x == x) ? (y == piecen.y) : false; + + } else { + // who knows... + return false; + } + } + + /** + * Generates a string representation of this piecen. + */ + public String toString () + { + return "[color=" + color + ", pos=" + x + "/" + y + + ", fidx=" + featureIndex + "]"; + } +} diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/data/TileCodes.java b/projects/atlanti/src/java/com/samskivert/atlanti/data/TileCodes.java index 906141ee..b6e7f71b 100644 --- a/projects/atlanti/src/java/com/samskivert/atlanti/data/TileCodes.java +++ b/projects/atlanti/src/java/com/samskivert/atlanti/data/TileCodes.java @@ -1,5 +1,5 @@ // -// $Id: TileCodes.java,v 1.4 2001/10/16 09:31:46 mdb Exp $ +// $Id: TileCodes.java,v 1.5 2001/10/17 02:19:54 mdb Exp $ package com.threerings.venison; @@ -32,11 +32,11 @@ public interface TileCodes /** A two-sided city tile with two separate city arcs adjacent to one * another and not connected to each other. */ - public static final int DISCONNECTED_CITY_TWO = 7; + public static final int TWO_CITY_TWO = 7; /** A two-sided city tile with two separate city arcs on opposite * sides of the tile. */ - public static final int DISCONNECTED_CITY_TWO_ACROSS = 8; + public static final int TWO_CITY_TWO_ACROSS = 8; /** A one-sided city tile. */ public static final int CITY_ONE = 9; @@ -118,41 +118,4 @@ public interface TileCodes /** A constant indicating a cloister. */ public static final int CLOISTER = 3; - - - /** Bit mask for a north connecting feature. */ - public static final int NORTH_F = 0x1 << 0; - - /** Bit mask for an east connecting feature. */ - public static final int EAST_F = 0x1 << 1; - - /** Bit mask for a south connecting feature. */ - public static final int SOUTH_F = 0x1 << 2; - - /** Bit mask for a west connecting feature. */ - public static final int WEST_F = 0x1 << 3; - - /** Bit mask for a north by northeast connecting feature. */ - public static final int NNE_F = 0x1 << 4; - - /** Bit mask for an east by northeast connecting feature. */ - public static final int ENE_F = 0x1 << 5; - - /** Bit mask for an east by southeast connecting feature. */ - public static final int ESE_F = 0x1 << 6; - - /** Bit mask for a south by southeast connecting feature. */ - public static final int SSE_F = 0x1 << 7; - - /** Bit mask for a south by southwest connecting feature. */ - public static final int SSW_F = 0x1 << 8; - - /** Bit mask for a west by southwest connecting feature. */ - public static final int WSW_F = 0x1 << 9; - - /** Bit mask for a west by northwest connecting feature. */ - public static final int WNW_F = 0x1 << 10; - - /** Bit mask for a north by northwest connecting feature. */ - public static final int NNW_F = 0x1 << 11; } diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/server/AtlantiManager.java b/projects/atlanti/src/java/com/samskivert/atlanti/server/AtlantiManager.java index 9ef5d4dc..caff9411 100644 --- a/projects/atlanti/src/java/com/samskivert/atlanti/server/AtlantiManager.java +++ b/projects/atlanti/src/java/com/samskivert/atlanti/server/AtlantiManager.java @@ -1,8 +1,9 @@ // -// $Id: AtlantiManager.java,v 1.7 2001/10/16 09:31:46 mdb Exp $ +// $Id: AtlantiManager.java,v 1.8 2001/10/17 02:19:54 mdb Exp $ package com.threerings.venison; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -32,6 +33,8 @@ public class VenisonManager registerMessageHandler(PLACE_TILE_REQUEST, new PlaceTileHandler()); registerMessageHandler( PLACE_PIECEN_REQUEST, new PlacePiecenHandler()); + registerMessageHandler( + PLACE_NOTHING_REQUEST, new PlaceNothingHandler()); } // documentation inherited @@ -52,12 +55,19 @@ public class VenisonManager super.gameWillStart(); // generate a shuffled tile list - _tiles = TileUtil.getStandardTileSet(); - Collections.shuffle(_tiles); + _tilesInBox = TileUtil.getStandardTileSet(); + Collections.shuffle(_tilesInBox); - // clear out the tile set + // clear out our board tiles + _tiles.clear(); + + // clear out the tile and piecen set _venobj.setTiles(new DSet(VenisonTile.class)); + _venobj.setPiecens(new DSet(Piecen.class)); + + // and add the starting tile _venobj.addToTiles(VenisonTile.STARTING_TILE); + _tiles.add(VenisonTile.STARTING_TILE); } protected void turnWillStart () @@ -66,7 +76,7 @@ public class VenisonManager // let the players know what the next tile is that should be // played - VenisonTile tile = (VenisonTile)_tiles.remove(0); + VenisonTile tile = (VenisonTile)_tilesInBox.remove(0); _venobj.setCurrentTile(tile); } @@ -75,7 +85,7 @@ public class VenisonManager super.turnDidEnd(); // if there are no tiles left, we end the game - if (_tiles.size() == 0) { + if (_tilesInBox.size() == 0) { endGame(); } } @@ -86,7 +96,7 @@ public class VenisonManager protected void setNextTurnHolder () { // if we have tiles left, we move to the next player as normal - if (_tiles.size() > 0) { + if (_tilesInBox.size() > 0) { super.setNextTurnHolder(); } else { // if we don't, we ensure that a new turn isn't started by @@ -103,16 +113,21 @@ public class VenisonManager VenisonTile tile = (VenisonTile)event.getArgs()[0]; // make sure this is a valid placement - if (TileUtil.isValidPlacement(_venobj.tiles.elements(), tile)) { + if (TileUtil.isValidPlacement(_tiles, tile)) { + // add the tile to the lsit + _tiles.add(tile); + + // inherit its claim groups + TileUtil.inheritClaims(_tiles, tile); + + Log.info("Placed tile " + tile + "."); + // add the tile to the tiles set _venobj.addToTiles(tile); } else { Log.warning("Received invalid placement " + event + "."); } - - // end the turn - endTurn(); } } @@ -121,6 +136,40 @@ public class VenisonManager { public void handleEvent (MessageEvent event) { + Piecen piecen = (Piecen)event.getArgs()[0]; + + // 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 requested piecen " + + "placement " + piecen + "."); + + } else if (tile.claims[piecen.featureIndex] != 0) { + Log.warning("Requested to place piecen on claimed feature " + + "[tile=" + tile + ", piecen=" + piecen + "]."); + + } else { + // otherwise stick the piece in the tile to update the + // claim groups + tile.setPiecen(piecen, _tiles); + + // and add the piecen to the game object + _venobj.addToPiecens(piecen); + } + + // end the turn + endTurn(); + } + } + + /** Handles place nothing requests. */ + protected class PlaceNothingHandler implements MessageHandler + { + public void handleEvent (MessageEvent event) + { + // player doesn't want to place anything, so we just end the + // turn + endTurn(); } } @@ -129,5 +178,8 @@ public class VenisonManager /** The (shuffled) list of tiles remaining to be played in this * game. */ - protected List _tiles; + protected List _tilesInBox; + + /** A sorted list of the tiles that have been placed on the board. */ + protected List _tiles = new ArrayList(); } diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/util/FeatureUtil.java b/projects/atlanti/src/java/com/samskivert/atlanti/util/FeatureUtil.java new file mode 100644 index 00000000..8867e948 --- /dev/null +++ b/projects/atlanti/src/java/com/samskivert/atlanti/util/FeatureUtil.java @@ -0,0 +1,313 @@ +// +// $Id: FeatureUtil.java,v 1.1 2001/10/17 02:19:54 mdb Exp $ + +package com.threerings.venison; + +/** + * Feature related constants and utility functions. + */ +public class FeatureUtil implements TileCodes +{ + /** Bit mask for a north connecting feature. */ + public static final int NORTH_F = 0x1 << 0; + + /** Bit mask for an east connecting feature. */ + public static final int EAST_F = 0x1 << 1; + + /** Bit mask for a south connecting feature. */ + public static final int SOUTH_F = 0x1 << 2; + + /** Bit mask for a west connecting feature. */ + public static final int WEST_F = 0x1 << 3; + + /** Bit mask for a north by northeast connecting feature. */ + public static final int NNE_F = 0x1 << 4; + + /** Bit mask for an east by northeast connecting feature. */ + public static final int ENE_F = 0x1 << 5; + + /** Bit mask for an east by southeast connecting feature. */ + public static final int ESE_F = 0x1 << 6; + + /** Bit mask for a south by southeast connecting feature. */ + public static final int SSE_F = 0x1 << 7; + + /** Bit mask for a south by southwest connecting feature. */ + public static final int SSW_F = 0x1 << 8; + + /** Bit mask for a west by southwest connecting feature. */ + public static final int WSW_F = 0x1 << 9; + + /** Bit mask for a west by northwest connecting feature. */ + public static final int WNW_F = 0x1 << 10; + + /** Bit mask for a north by northwest connecting feature. */ + public static final int NNW_F = 0x1 << 11; + + /** A mapping from feature edge masks to tile directions and + * corresponding feature edge masks. */ + public static final int[] ADJACENCY_MAP = new int[] { + NORTH_F, NORTH, SOUTH_F, + EAST_F, EAST, WEST_F, + SOUTH_F, SOUTH, NORTH_F, + WEST_F, WEST, EAST_F, + NNW_F, NORTH, SSW_F, + NNE_F, NORTH, SSE_F, + ENE_F, EAST, WNW_F, + ESE_F, EAST, WSW_F, + SSE_F, SOUTH, NNE_F, + SSW_F, SOUTH, NNW_F, + WSW_F, WEST, ESE_F, + WNW_F, WEST, ENE_F, + }; + + /** + * Returns the feature array for the tile of the specified type. + */ + public static Feature[] getTileFeatures (int type) + { + // create the features array + Feature[] features = new Feature[TILE_FEATURES[type-1].length]; + + // initialize it with features from the repeated feature table or + // with newly constructed features + for (int i = 0; i < features.length; i++) { + int[] desc = TILE_FEATURES[type-1][i]; + // if the description is a length one array, it is the index + // into the reused feature table of the desired feature + if (desc.length == 1) { + features[i] = _reusedFeatures[desc[0]]; + } else { + // otherwise it is the description of this unique feature + features[i] = new Feature(desc); + } + } + + return features; + } + + /** + * Translates the feature edge mask into the orientation specified. + * For a forward translation, provide a positive valued orientation + * constant. For a backward translation, provide a negative valued + * orientation constant. + * + * @return the translated feature mask. + */ + public static int translateMask (int featureMask, int orientation) + { + int[] map = FEATURE_ORIENT_MAP[0]; + if ((featureMask & (NNE_F|ESE_F|SSW_F|WNW_F)) != 0) { + map = FEATURE_ORIENT_MAP[1]; + } else if ((featureMask & (ENE_F|SSE_F|WSW_F|NNW_F)) != 0) { + map = FEATURE_ORIENT_MAP[2]; + } + return xlateMask(map, featureMask, orientation); + } + + /** {@link #translateMask} helper function. */ + protected static int xlateMask ( + int[] map, int featureMask, int orientation) + { + int index = 0; + for (int i = 0; i < map.length; i++) { + if (map[i] == featureMask) { + return map[(i + 4 + orientation) % 4]; + } + } + return featureMask; + } + + /** A reused feature identifier. */ + protected static final int NESW_CITY = 0; + + /** A reused feature identifier. */ + protected static final int NEW_CITY = 1; + + /** A reused feature identifier. */ + protected static final int EW_CITY = 2; + + /** A reused feature identifier. */ + protected static final int NW_CITY = 3; + + /** A reused feature identifier. */ + protected static final int N_CITY = 4; + + /** A reused feature identifier. */ + protected static final int E_CITY = 5; + + /** A reused feature identifier. */ + protected static final int W_CITY = 6; + + /** A reused feature identifier. */ + protected static final int E_ROAD = 7; + + /** A reused feature identifier. */ + protected static final int S_ROAD = 8; + + /** A reused feature identifier. */ + protected static final int W_ROAD = 9; + + /** A reused feature identifier. */ + protected static final int S_GRASS = 10; + + /** A reused feature identifier. */ + protected static final int SE_GRASS = 11; + + /** A reused feature identifier. */ + protected static final int SW_GRASS = 12; + + /** An array of features used more than once. */ + protected static final int[][] FEATURES = new int[][] { + { CITY, NORTH_F|EAST_F|SOUTH_F|WEST_F, // NESW_CITY + 2,2, 0,0, 4,0, 4,4, 0,4 }, + { CITY, NORTH_F|EAST_F|WEST_F, // NEW_CITY + 2,2, 0,0, 4,0, 4,4, 3,3, 1,3, 0,4 }, + { CITY, EAST_F|WEST_F, // EW_CITY + 2,2, 0,0, 1,1, 3,1, 4,0, 4,4, 3,3, 1,3, 0,4 }, + { CITY, NORTH_F|WEST_F, // NW_CITY + 1,1, 0,0, 4,0, 0,4 }, + { CITY, NORTH_F, // N_CITY + 2,-1, 0,0, 1,1, 3,1, 4,0 }, + { CITY, EAST_F, // E_CITY + -4,2, 4,0, 3,1, 3,3, 4,4 }, + { CITY, WEST_F, // W_CITY + -1,2, 0,0, 1,1, 1,3, 0,4 }, + { ROAD, EAST_F, // E_ROAD + 3,2, 2,2, 4,2 }, + { ROAD, SOUTH_F, // S_ROAD + 2,3, 2,2, 2,4 }, + { ROAD, WEST_F, // W_ROAD + 1,2, 0,2, 2,2 }, + { GRASS, SOUTH_F, // S_GRASS + 2,-4, 0,4, 1,3, 3,3, 4,4 }, + { GRASS, ESE_F|SSE_F, // SE_GRASS + 3,3, 2,2, 4,2, 4,4, 2,4 }, + { GRASS, WSW_F|SSW_F, // SW_GRASS + 1,3, 0,2, 2,2, 2,4, 0,4 }, + }; + + /** A table describing the features of each tile. */ + protected static final int[][][] TILE_FEATURES = new int[][][] { + // one must offset tile type by one when indexing into this array + + { { NESW_CITY } }, // CITY_FOUR + + { { NEW_CITY }, // CITY_THREE + { S_GRASS } }, + + { { NEW_CITY }, // CITY_THREE_ROAD + { GRASS, SSW_F, -2,-4, 0,4, 1,3, 2,3, 2,4 }, + { GRASS, SSE_F, -3,-4, 2,4, 2,3, 3,3, 4,4 }, + { ROAD, SOUTH_F, 2,-4, 2,3, 2,4 } }, + + { { NW_CITY }, // CITY_TWO + { GRASS, EAST_F|SOUTH_F, 3,3, 0,4, 4,0, 4,4 } }, + + { { NW_CITY }, // CITY_TWO_ROAD + { GRASS, ENE_F|SSW_F, -3,-3, 0,4, 4,0, 4,2, 2,4 }, + { GRASS, ESE_F|SSE_F, -4,-4, 2,4, 4,2, 4,4 }, + { ROAD, EAST_F|SOUTH_F, 3,3, 2,4, 4,2 } }, + + { { EW_CITY }, // CITY_TWO_ACROSS + { GRASS, NORTH_F, 2,-1, 0,0, 1,1, 3,1, 4,0 }, + { S_GRASS } }, + + { { GRASS, WEST_F|SOUTH_F, // TWO_CITY_TWO + 2,2, 0,0, 1,1, 3,1, 3,3, 4,4, 0,4 }, + { N_CITY }, + { E_CITY } }, + + { { GRASS, NORTH_F|SOUTH_F, // TWO_CITY_TWO_ACROSS + 2,2, 0,0, 4,0, 3,1, 3,3, 4,4, 0,4, 1,3, 1,1 }, + { W_CITY }, + { E_CITY } }, + + { { GRASS, EAST_F|SOUTH_F|WEST_F, // CITY_ONE + 2,2, 0,0, 1,1, 3,1, 4,0, 4,4, 0,4 }, + { N_CITY } }, + + { { GRASS, ENE_F|SSW_F|WEST_F, // CITY_ONE_ROAD_RIGHT + 1,2, 0,0, 1,1, 3,1, 4,0, 4,2, 2,2, 2,4, 0,4 }, + { SE_GRASS }, + { ROAD, EAST_F|SOUTH_F, 3,2, 4,2, 2,2, 2,4 }, + { N_CITY } }, + + { { GRASS, EAST_F|SSE_F|WNW_F, // CITY_ONE_ROAD_LEFT + 3,2, 0,0, 1,1, 3,1, 4,0, 4,4, 2,4, 2,2, 0,2 }, + { SW_GRASS }, + { ROAD, SOUTH_F|WEST_F, 1,2, 0,2, 2,2, 2,4 }, + { N_CITY } }, + + { { GRASS, ENE_F|WNW_F, // CITY_ONE_ROAD_TEE + 2,-2, 0,0, 1,1, 3,1, 4,0, 4,2, 0,2 }, + { SE_GRASS }, + { SW_GRASS }, + { E_ROAD }, + { S_ROAD }, + { W_ROAD }, + { N_CITY } }, + + { { GRASS, ENE_F|WNW_F, // CITY_ONE_ROAD_STRAIGHT + 2,-2, 0,0, 1,1, 3,1, 4,0, 4,2, 0,2 }, + { GRASS, ESE_F|SOUTH_F|WSW_F, 2,3, 0,2, 4,2, 4,4, 0,4 }, + { ROAD, EAST_F|WEST_F, 2,2, 0,2, 4,2 }, + { N_CITY } }, + + { { GRASS, NORTH_F|EAST_F|SOUTH_F|WEST_F, // CLOISTER_PLAIN + 1,1, 0,0, 4,0, 4,4, 0,4 }, + { CLOISTER, 0, 2,2, 1,1, 3,1, 3,3, 1,3 } }, + + { { GRASS, NORTH_F|EAST_F|WEST_F|SSE_F|SSW_F, // CLOISTER_ROAD + 1,1, 0,0, 4,0, 4,4, 0,4 }, + { CLOISTER, 0, 2,2, 1,1, 3,1, 3,3, 1,3 }, + { ROAD, SOUTH_F, 2,-4, 2,3, 2,4 } }, + + { { GRASS, WNW_F|NNW_F, // FOUR_WAY_ROAD + 1,1, 0,0, 2,0, 2,2, 0,2 }, + { GRASS, NNE_F|ENE_F, 3,1, 2,0, 4,0, 4,2, 2,2 }, + { SE_GRASS }, + { SW_GRASS }, + { ROAD, NORTH_F, 2,1, 2,0, 2,2 }, + { E_ROAD }, + { S_ROAD }, + { W_ROAD } }, + + { { GRASS, WNW_F|NORTH_F|ENE_F, // THREE_WAY_ROAD + 2,1, 0,0, 4,0, 4,2, 0,2 }, + { SE_GRASS }, + { SW_GRASS }, + { E_ROAD }, + { S_ROAD }, + { W_ROAD } }, + + { { GRASS, NNW_F|WEST_F|SSW_F, // STRAIGHT_ROAD + 1,2, 0,0, 2,0, 2,4, 0,4 }, + { GRASS, SSE_F|EAST_F|NNE_F, 3,2, 2,0, 4,0, 4,4, 2,4 }, + { ROAD, NORTH_F|SOUTH_F, 2,2, 2,0, 2,4 } }, + + { { GRASS, WNW_F|NORTH_F|EAST_F|SSE_F, // CURVED_ROAD + 3,1, 0,0, 4,0, 4,4, 2,4, 2,2, 0,2 }, + { SW_GRASS }, + { ROAD, SOUTH_F|WEST_F, 1,2, 0,2, 2,2, 2,4 } }, + }; + + /** Mapping table used to rotate feature facements. */ + protected static final int[][] FEATURE_ORIENT_MAP = new int[][] { + // orientations rotate through one of three four-cycles + { NORTH_F, EAST_F, SOUTH_F, WEST_F }, + { NNE_F, ESE_F, SSW_F, WNW_F }, + { ENE_F, SSE_F, WSW_F, NNW_F }, + }; + + /** A table of features that are used on more than one tile. */ + protected static Feature[] _reusedFeatures; + + // create our reused features table + static { + _reusedFeatures = new Feature[FEATURES.length]; + for (int i = 0; i < FEATURES.length; i++) { + _reusedFeatures[i] = new Feature(FEATURES[i]); + } + } +} diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/util/TileUtil.java b/projects/atlanti/src/java/com/samskivert/atlanti/util/TileUtil.java index 3d2dc216..26892e6f 100644 --- a/projects/atlanti/src/java/com/samskivert/atlanti/util/TileUtil.java +++ b/projects/atlanti/src/java/com/samskivert/atlanti/util/TileUtil.java @@ -1,14 +1,14 @@ // -// $Id: TileUtil.java,v 1.6 2001/10/16 17:11:07 mdb Exp $ +// $Id: TileUtil.java,v 1.7 2001/10/17 02:19:54 mdb Exp $ package com.threerings.venison; import java.awt.Polygon; -import java.util.Arrays; import java.util.ArrayList; -import java.util.List; +import java.util.Collections; import java.util.Iterator; +import java.util.List; import com.samskivert.util.IntTuple; @@ -36,8 +36,7 @@ public class TileUtil implements TileCodes * position of the target tile is assumed to be the desired placement * position and the current orientation of the target tile is ignored. * - * @param tiles an iterator that will iterate over all of the tiles - * currently placed on the board. + * @param tiles a list of the tiles on the board. * @param target the tile whose valid orientations we wish to compute. * * @return an array of boolean values indicating whether or not the @@ -45,14 +44,15 @@ public class TileUtil implements TileCodes * up with the direction constants specified in {@link TileCodes}. */ public static boolean[] computeValidOrients ( - Iterator tiles, VenisonTile target) + List tiles, VenisonTile target) { // this contains a count of tiles that match up with the candidate // tile in each of its four orientations int[] matches = new int[4]; - while (tiles.hasNext()) { - VenisonTile tile = (VenisonTile)tiles.next(); + int tsize = tiles.size(); + for (int i = 0; i < tsize; i++) { + VenisonTile tile = (VenisonTile)tiles.get(i); // figure out where this tile is in relation to the candidate int xdiff = tile.x - target.x; @@ -75,14 +75,14 @@ public class TileUtil implements TileCodes // we iterate over the four possible orientations of the // target tile - for (int i = 0; i < 4; i++) { + for (int o = 0; o < 4; o++) { // we compare the edge of the placed tile (which never // changes) with the edge of the target tile which is // adjusted based on the target tile's orientation if (getEdge(tile.type, tileEdge) == - getEdge(target.type, (targetEdge+(4-i)) % 4)) { + getEdge(target.type, (targetEdge+(4-o)) % 4)) { // increment the edge matches - matches[i]++; + matches[o]++; } else { // if we have a mismatch, we want to ensure that @@ -91,7 +91,7 @@ public class TileUtil implements TileCodes // that it will remain less than zero regardless // of which of the other three tiles match in this // orientation - matches[i] -= 10; + matches[o] -= 10; } } } @@ -110,20 +110,19 @@ public class TileUtil implements TileCodes * Returns true if the position and orientation of the target tile is * legal given the placement of all of the existing tiles. * - * @param tiles an iterator that enumerates all of the tile already on - * the board. + * @param tiles a list of the tiles already on the board. * @param target the tile whose validity we want to determine. * * @return true if the target tile is configured with a valid position * and orientation, false if it is not. */ - public static boolean isValidPlacement ( - Iterator tiles, VenisonTile target) + public static boolean isValidPlacement (List tiles, VenisonTile target) { boolean matchedAnEdge = false; - while (tiles.hasNext()) { - VenisonTile tile = (VenisonTile)tiles.next(); + int tsize = tiles.size(); + for (int i = 0; i < tsize; i++) { + VenisonTile tile = (VenisonTile)tiles.get(i); // figure out where this tile is in relation to the candidate int xdiff = tile.x - target.x; @@ -184,11 +183,11 @@ public class TileUtil implements TileCodes * claims, those claims will be joined. The affected tiles and piecens * will have their claim groups updated. * - * @param tiles a sorted array of the tiles on the board (which need + * @param tiles a sorted list of the tiles on the board (which need * not include the tile whose features are being configured). * @param tile the tile whose features should be configured. */ - public static void inheritClaims (VenisonTile[] tiles, VenisonTile tile) + public static void inheritClaims (List tiles, VenisonTile tile) { // obtain our neighboring tiles VenisonTile[] neighbors = new VenisonTile[4]; @@ -199,16 +198,17 @@ public class TileUtil implements TileCodes // for each feature in the tile, determine whether or not the // neighboring tile's matching feature is claimed - for (int i = 0; i < tile.features.length; i += 2) { - int ftype = tile.features[i]; - int fmask = tile.features[i+1]; + for (int i = 0; i < tile.features.length; i ++) { + int ftype = tile.features[i].type; + int fmask = tile.features[i].edgeMask; int cgroup = 0; - // iterate over all of the possible adjacency possibilities - for (int c = 0; c < ADJADCENCY_MAP.length; c += 3) { - int mask = ADJADCENCY_MAP[c]; - int dir = ADJADCENCY_MAP[c+1]; - int opp_mask = ADJADCENCY_MAP[c+2]; + // iterate over all of the possible adjacency possibilities, + // first looking for a claim group to inherit + 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 if ((fmask & mask) == 0) { @@ -226,34 +226,84 @@ public class TileUtil implements TileCodes // it looks like we have a match, so translate the target // stuff into our orientation - mask = translateMask(mask, tile.orientation); - opp_mask = translateMask(opp_mask, tile.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) { - // 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) { - 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 + "]."); - } - - } else { - // otherwise, we inherit the group of the opposing - // feature - cgroup = neighbors[dir].getFeatureGroup(opp_mask); + Log.info("Inherited claim [tile=" + tile + + ", fidx=" + i + ", cgroup=" + cgroup + + ", source=" + neighbors[dir] + "]."); + break; } } - // finally initialize the feature's claim group - tile.claims[i/2] = cgroup; + // if we didn't inherit a claim group, skip to the next + // feature + if (cgroup == 0) { + continue; + } + + // initialize the feature's claim group + tile.claims[i] = cgroup; + + // otherwise, iterate over all of the possible adjacency + // possibilities, propagating our group to connected features + 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 + if ((fmask & mask) == 0) { + 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 + "]."); + } + } } } @@ -261,7 +311,7 @@ public class TileUtil implements TileCodes * Sets the claim group for the specified feature in this tile and * propagates that claim group to all connected features. * - * @param tiles a sorted array of the tiles on the board. + * @param tiles a sorted list of the tiles on the board. * @param tile the tile that contains the feature whose claim group is * being set. * @param featureIndex the index of the feature. @@ -271,21 +321,21 @@ public class TileUtil implements TileCodes * propagating the group further). */ public static void setFeatureGroup ( - VenisonTile[] tiles, VenisonTile tile, int featureIndex, + List tiles, VenisonTile tile, int featureIndex, int claimGroup, int entryEdgeMask) { // set the claim group for this feature on this tile tile.setFeatureGroup(featureIndex, claimGroup); // now propagate this feature to connected features - int ftype = tile.features[featureIndex*2]; - int fmask = tile.features[featureIndex*2+1]; + 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 < ADJADCENCY_MAP.length; c += 3) { - int mask = ADJADCENCY_MAP[c]; - int dir = ADJADCENCY_MAP[c+1]; - int opp_mask = ADJADCENCY_MAP[c+2]; + 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 @@ -295,7 +345,7 @@ public class TileUtil implements TileCodes // figure out if this would be the tile from which we // propagated into our current tile and skip it if so - opp_mask = translateMask(opp_mask, tile.orientation); + opp_mask = FeatureUtil.translateMask(opp_mask, tile.orientation); if ((opp_mask & entryEdgeMask) != 0) { continue; } @@ -314,12 +364,16 @@ public class TileUtil implements TileCodes // it looks like we have a match, so translate the target mask // into our orientation - mask = translateMask(mask, tile.orientation); + mask = FeatureUtil.translateMask(mask, tile.orientation); // propagate, propagate, propagate int fidx = neighbor.getFeatureIndex(opp_mask); if (fidx >= 0) { - setFeatureGroup(tiles, neighbor, fidx, claimGroup, mask); + // 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 + @@ -330,51 +384,19 @@ public class TileUtil implements TileCodes } } - /** - * Translates the feature edge mask into the orientation specified. - * For a forward translation, provide a positive valued orientation - * constant. For a backward translation, provide a negative valued - * orientation constant. - * - * @return the translated feature mask. - */ - public static int translateMask (int featureMask, int orientation) - { - int[] map = FEATURE_ORIENT_MAP[0]; - if ((featureMask & (NNE_F|ESE_F|SSW_F|WNW_F)) != 0) { - map = FEATURE_ORIENT_MAP[1]; - } else if ((featureMask & (ENE_F|SSE_F|WSW_F|NNW_F)) != 0) { - map = FEATURE_ORIENT_MAP[2]; - } - return xlateMask(map, featureMask, orientation); - } - - /** {@link #translateMask} helper function. */ - protected static int xlateMask ( - int[] map, int featureMask, int orientation) - { - int index = 0; - for (int i = 0; i < map.length; i++) { - if (map[i] == featureMask) { - return map[(i + 4 + orientation) % 4]; - } - } - return featureMask; - } - /** * Locates and returns the tile with the specified coordinates. * - * @param tiles a sorted tiles array. + * @param tiles a sorted list of tiles. * * @return the tile with the requested coordinates or null if no tile * exists at those coordinates. */ - protected static VenisonTile findTile (VenisonTile[] tiles, int x, int y) + protected static VenisonTile findTile (List tiles, int x, int y) { IntTuple coord = new IntTuple(x, y); - int tidx = Arrays.binarySearch(tiles, coord); - return (tidx >= 0) ? tiles[tidx] : null; + int tidx = Collections.binarySearch(tiles, coord); + return (tidx >= 0) ? (VenisonTile)tiles.get(tidx) : null; } /** @@ -393,64 +415,11 @@ public class TileUtil implements TileCodes } /** - * Massages a road segment (specified in tile feature coordinates) - * into a polygon (in screen coordinates) that can be used to render - * or hit test the road. The coordinates must obey the following - * constraints: (x1 < x2 and y1 == y2) or (x1 == x2 and y1 < y2) or - * (x1 < x2 and y1 > y2). - * - * @return a polygon representing the road segment (with origin at 0, - * 0). + * Returns the next unused claim group value. */ - public static Polygon roadSegmentToPolygon ( - int x1, int y1, int x2, int y2) + public static int nextClaimGroup () { - // first convert the coordinates into screen coordinates - x1 = (x1 * TILE_WIDTH) / 4; - y1 = (y1 * TILE_HEIGHT) / 4; - x2 = (x2 * TILE_WIDTH) / 4; - y2 = (y2 * TILE_HEIGHT) / 4; - - Polygon poly = new Polygon(); - int dx = 4, dy = 4; - - // figure out what sort of line segment it is - if (x1 == x2) { // vertical - // make adjustments to ensure that we stay inside the tile - // bounds - if (y1 == 0) { - y1 += dy; - } else if (y2 == TILE_HEIGHT) { - y2 -= dy; - } - poly.addPoint(x1 - dx, y1 - dy); - poly.addPoint(x1 + dx, y1 - dy); - poly.addPoint(x2 + dx, y2 + dy); - poly.addPoint(x2 - dx, y2 + dy); - - } else if (y1 == y2) { // horizontal - // make adjustments to ensure that we stay inside the tile - // bounds - if (x1 == 0) { - x1 += dx; - } else if (x2 == TILE_WIDTH) { - x2 -= dx; - } - poly.addPoint(x1 - dx, y1 - dy); - poly.addPoint(x1 - dx, y1 + dy); - poly.addPoint(x2 + dx, y2 + dy); - poly.addPoint(x2 + dx, y2 - dy); - - } else { // diagonal - dx = 6; // widen these a bit so that the diagonal line appears - dy = 6; // to have the same weight as the straight ones - poly.addPoint(x1 - dx, y1); - poly.addPoint(x1 + dx, y1); - poly.addPoint(x2, y2 + dy); - poly.addPoint(x2, y2 - dy); - } - - return poly; + return ++_claimGroupCounter; } /** Used to generate our standard tile set. */ @@ -462,6 +431,9 @@ public class TileUtil implements TileCodes list.add(tile); } + /** Used to generate claim group values. */ + protected static int _claimGroupCounter; + /** Used to figure out which edges match up to which when comparing * adjacent tiles. */ protected static final int[] EDGE_MAP = new int[] { @@ -470,31 +442,6 @@ public class TileUtil implements TileCodes -1, SOUTH, -1 }; - /** A mapping from feature edge masks to tile directions and - * corresponding feature edge masks. */ - protected static final int[] ADJADCENCY_MAP = new int[] { - NORTH_F, NORTH, SOUTH_F, - EAST_F, EAST, WEST_F, - SOUTH_F, SOUTH, NORTH_F, - WEST_F, WEST, EAST_F, - NNW_F, NORTH, SSW_F, - NNE_F, NORTH, SSE_F, - ENE_F, EAST, WNW_F, - ESE_F, EAST, WSW_F, - SSE_F, SOUTH, NNE_F, - SSW_F, SOUTH, NNW_F, - WSW_F, WEST, ESE_F, - WNW_F, WEST, ENE_F, - }; - - /** Mapping table used to rotate feature facements. */ - public static final int[][] FEATURE_ORIENT_MAP = new int[][] { - // orientations rotate through one of three four-cycles - { NORTH_F, EAST_F, SOUTH_F, WEST_F }, - { NNE_F, ESE_F, SSW_F, WNW_F }, - { ENE_F, SSE_F, WSW_F, NNW_F }, - }; - /** A table indicating which tiles have which edges. */ protected static final int[] TILE_EDGES = new int[] { -1, -1, -1, -1, // null tile @@ -504,8 +451,8 @@ public class TileUtil implements TileCodes CITY, GRASS, GRASS, CITY, // CITY_TWO CITY, ROAD, ROAD, CITY, // CITY_TWO_ROAD GRASS, CITY, GRASS, CITY, // CITY_TWO_ACROSS - CITY, CITY, GRASS, GRASS, // DISCONNECTED_CITY_TWO - GRASS, CITY, GRASS, CITY, // DISCONNECTED_CITY_TWO_ACROSS + CITY, CITY, GRASS, GRASS, // TWO_CITY_TWO + GRASS, CITY, GRASS, CITY, // TWO_CITY_TWO_ACROSS CITY, GRASS, GRASS, GRASS, // CITY_ONE CITY, ROAD, ROAD, GRASS, // CITY_ONE_ROAD_RIGHT CITY, GRASS, ROAD, ROAD, // CITY_ONE_ROAD_LEFT @@ -519,247 +466,6 @@ public class TileUtil implements TileCodes GRASS, GRASS, ROAD, ROAD, // CURVED_ROAD }; - /** A table describing the features of each tile and their edge - * connectedness. */ - protected static final int[][] TILE_FEATURES = new int[][] { - // one must offset tile type by one when indexing into this array - - { CITY, NORTH_F|EAST_F|SOUTH_F|WEST_F }, // CITY_FOUR - - { CITY, NORTH_F|EAST_F|WEST_F, // CITY_THREE - GRASS, SOUTH_F }, - - { CITY, NORTH_F|EAST_F|WEST_F, // CITY_THREE_ROAD - GRASS, SSW_F, - GRASS, SSE_F, - ROAD, SOUTH_F }, - - { CITY, NORTH_F|WEST_F, // CITY_TWO - GRASS, EAST_F|SOUTH_F }, - - { CITY, NORTH_F|WEST_F, // CITY_TWO_ROAD - GRASS, ENE_F|SSW_F, - GRASS, ESE_F|SSE_F, - ROAD, EAST_F|SOUTH_F }, - - { CITY, WEST_F|EAST_F, // CITY_TWO_ACROSS - GRASS, NORTH_F, - GRASS, SOUTH_F }, - - { GRASS, WEST_F|SOUTH_F, // DISCONNECTED_CITY_TWO - CITY, NORTH_F, - CITY, EAST_F }, - - { GRASS, NORTH_F|SOUTH_F, // DISCONNECTED_CITY_TWO_ACROSS - CITY, WEST_F, - CITY, EAST_F }, - - { GRASS, EAST_F|SOUTH_F|WEST_F, // CITY_ONE - CITY, NORTH_F }, - - { GRASS, ENE_F|SSW_F|WEST_F, // CITY_ONE_ROAD_RIGHT - GRASS, ESE_F|SSE_F, - ROAD, EAST_F|SOUTH_F, - CITY, NORTH_F }, - - { GRASS, EAST_F|SSE_F, WNW_F, // CITY_ONE_ROAD_LEFT - GRASS, SSW_F|WSW_F, - ROAD, SOUTH_F|WEST_F, - CITY, NORTH_F }, - - { GRASS, ENE_F|WNW_F, // CITY_ONE_ROAD_TEE - GRASS, ESE_F|SSE_F, - GRASS, SSW_F|WSW_F, - ROAD, EAST_F, - ROAD, SOUTH_F, - ROAD, WEST_F, - CITY, NORTH_F }, - - { GRASS, ENE_F|WNW_F, // CITY_ONE_ROAD_STRAIGHT - GRASS, ESE_F, SOUTH_F, WSW_F, - ROAD, EAST_F|WEST_F, - CITY, NORTH_F }, - - { GRASS, NORTH_F|EAST_F|SOUTH_F|WEST_F, // CLOISTER_PLAIN - CLOISTER, 0}, - - { GRASS, NORTH_F|EAST_F|WEST_F, // CLOISTER_ROAD - CLOISTER, 0, - ROAD, SOUTH_F }, - - { GRASS, WNW_F|NNW_F, // FOUR_WAY_ROAD - GRASS, NNE_F|ENE_F, - GRASS, ESE_F|SSE_F, - GRASS, SSW_F|WSW_F, - ROAD, NORTH_F, - ROAD, EAST_F, - ROAD, SOUTH_F, - ROAD, WEST_F }, - - { GRASS, WNW_F|NORTH_F|ENE_F, // THREE_WAY_ROAD - GRASS, ESE_F|SSE_F, - GRASS, SSW_F|WSW_F, - ROAD, EAST_F, - ROAD, SOUTH_F, - ROAD, WEST_F }, - - { GRASS, NNE_F|EAST_F|SSE_F, // STRAIGHT_ROAD - GRASS, SSW_F|WEST_F|NNW_F, - ROAD, NORTH_F|SOUTH_F }, - - { GRASS, WNW_F|NORTH_F|EAST_F|SSE_F, // CURVED_ROAD - GRASS, SSW_F|WSW_F, - ROAD, SOUTH_F|WEST_F }, - }; - - /** A table describing the geometry of the features (cities, roads, - * etc.) of each tile. */ - protected static final Object[] TILE_FEATURE_GEOMS = new Object[] { - // one must offset tile type by one when indexing into this array - - new Object[] { new IntTuple(CITY, 0), }, // CITY_FOUR - - new Object[] { new IntTuple(CITY, 0), // CITY_THREE - new IntTuple(GRASS, 1), - new int[] { 0, 4, 1, 3, 3, 3, 4, 4 }}, - - new Object[] { new IntTuple(CITY, 0), // CITY_THREE_ROAD - new IntTuple(GRASS, 1), - new int[] { 0, 4, 1, 3, 2, 3, 2, 4 }, - new IntTuple(GRASS, 2), - new int[] { 2, 4, 2, 3, 3, 3, 4, 4 }, - new IntTuple(ROAD, 3), - new int[] { 2, 3, 2, 4 }}, - - new Object[] { new IntTuple(CITY, 0), // CITY_TWO - new IntTuple(GRASS, 1), - new int[] { 0, 4, 4, 0, 4, 4 }}, - - new Object[] { new IntTuple(CITY, 0), // CITY_TWO_ROAD - new IntTuple(GRASS, 1), - new int[] { 0, 4, 4, 0, 4, 2, 2, 4 }, - new IntTuple(GRASS, 2), - new int[] { 2, 4, 4, 2, 4, 4 }, - new IntTuple(ROAD, 3), - new int[] { 2, 4, 4, 2 }}, - - new Object[] { new IntTuple(CITY, 0), // CITY_TWO_ACROSS - new IntTuple(GRASS, 1), - new int[] { 0, 0, 1, 1, 3, 1, 4, 0 }, - new IntTuple(GRASS, 2), - new int[] { 0, 4, 1, 3, 3, 3, 4, 4 }}, - - new Object[] { new IntTuple(GRASS, 0), // DISCONNECTED_CITY_TWO - new IntTuple(CITY, 1), - new int[] { 0, 0, 1, 1, 3, 1, 4, 0 }, - new IntTuple(CITY, 2), - new int[] { 4, 0, 3, 1, 3, 3, 4, 4 }}, - - new Object[] { new IntTuple(GRASS, 0), // DISCONNECTED_CITY_TWO_ACROSS - new IntTuple(CITY, 1), - new int[] { 0, 0, 1, 1, 1, 3, 0, 4 }, - new IntTuple(CITY, 2), - new int[] { 4, 0, 3, 1, 3, 3, 4, 4 }}, - - new Object[] { new IntTuple(GRASS, 0), // CITY_ONE - new IntTuple(CITY, 1), - new int[] { 0, 0, 1, 1, 3, 1, 4, 0 }}, - - new Object[] { new IntTuple(GRASS, 0), // CITY_ONE_ROAD_RIGHT - new IntTuple(GRASS, 1), - new int[] { 2, 2, 4, 2, 4, 4, 2, 4 }, - new IntTuple(ROAD, 2), - new int[] { 2, 2, 2, 4 }, - new IntTuple(ROAD, 2), - new int[] { 2, 2, 4, 2 }, - new IntTuple(CITY, 3), - new int[] { 0, 0, 1, 1, 3, 1, 4, 0 }}, - - new Object[] { new IntTuple(GRASS, 0), // CITY_ONE_ROAD_LEFT - new IntTuple(GRASS, 1), - new int[] { 0, 2, 2, 2, 2, 4, 0, 4 }, - new IntTuple(ROAD, 2), - new int[] { 2, 2, 2, 4 }, - new IntTuple(ROAD, 2), - new int[] { 0, 2, 2, 2 }, - new IntTuple(CITY, 3), - new int[] { 0, 0, 1, 1, 3, 1, 4, 0 }}, - - new Object[] { new IntTuple(GRASS, 0), // CITY_ONE_ROAD_TEE - new IntTuple(GRASS, 1), - new int[] { 2, 2, 4, 2, 4, 4, 2, 4 }, - new IntTuple(GRASS, 2), - new int[] { 0, 2, 2, 2, 2, 4, 0, 4 }, - new IntTuple(ROAD, 3), - new int[] { 0, 2, 2, 2 }, - new IntTuple(ROAD, 4), - new int[] { 2, 2, 4, 2 }, - new IntTuple(ROAD, 5), - new int[] { 2, 2, 2, 4 }, - new IntTuple(CITY, 6), - new int[] { 0, 0, 1, 1, 3, 1, 4, 0 }}, - - new Object[] { new IntTuple(GRASS, 0), // CITY_ONE_ROAD_STRAIGHT - new IntTuple(GRASS, 1), - new int[] { 0, 2, 4, 2, 4, 4, 0, 4 }, - new IntTuple(ROAD, 2), - new int[] { 0, 2, 4, 2 }, - new IntTuple(CITY, 3), - new int[] { 0, 0, 1, 1, 3, 1, 4, 0 }}, - - new Object[] { new IntTuple(GRASS, 0), // CLOISTER_PLAIN - new IntTuple(CLOISTER, 1), - new int[] { 1, 1, 3, 1, 3, 3, 1, 3 }}, - - new Object[] { new IntTuple(GRASS, 0), // CLOISTER_ROAD - new IntTuple(CLOISTER, 1), - new int[] { 1, 1, 3, 1, 3, 3, 1, 3 }, - new IntTuple(ROAD, 2), - new int[] { 2, 3, 2, 4 }}, - - new Object[] { new IntTuple(GRASS, 0), // FOUR_WAY_ROAD - new IntTuple(GRASS, 1), - new int[] { 2, 0, 4, 0, 4, 2, 2, 2 }, - new IntTuple(GRASS, 2), - new int[] { 2, 2, 4, 2, 4, 4, 2, 4 }, - new IntTuple(GRASS, 3), - new int[] { 0, 2, 2, 2, 2, 4, 0, 4 }, - new IntTuple(ROAD, 4), - new int[] { 2, 0, 2, 2 }, - new IntTuple(ROAD, 5), - new int[] { 2, 2, 4, 2 }, - new IntTuple(ROAD, 6), - new int[] { 2, 2, 2, 4 }, - new IntTuple(ROAD, 7), - new int[] { 0, 2, 2, 2 }}, - - new Object[] { new IntTuple(GRASS, 0), // THREE_WAY_ROAD - new IntTuple(GRASS, 1), - new int[] { 2, 2, 4, 2, 4, 4, 2, 4 }, - new IntTuple(GRASS, 2), - new int[] { 0, 2, 2, 2, 2, 4, 0, 4 }, - new IntTuple(ROAD, 3), - new int[] { 2, 2, 4, 2 }, - new IntTuple(ROAD, 4), - new int[] { 2, 2, 2, 4 }, - new IntTuple(ROAD, 5), - new int[] { 0, 2, 2, 2 }}, - - new Object[] { new IntTuple(GRASS, 0), // STRAIGHT_ROAD - new IntTuple(GRASS, 1), - new int[] { 0, 2, 4, 2, 4, 4, 0, 4 }, - new IntTuple(ROAD, 2), - new int[] { 2, 0, 2, 4 }}, - - new Object[] { new IntTuple(GRASS, 0), // CURVED_ROAD - new IntTuple(GRASS, 1), - new int[] { 0, 2, 2, 2, 2, 4, 0, 4 }, - new IntTuple(ROAD, 2), - new int[] { 2, 2, 2, 4 }, - new IntTuple(ROAD, 2), - new int[] { 0, 2, 2, 2 }}, - }; - /** The standard tile set for a game of Venison. */ protected static ArrayList TILE_SET = new ArrayList(); @@ -779,9 +485,8 @@ public class TileUtil implements TileCodes addTiles(1, TILE_SET, new VenisonTile(CITY_TWO_ACROSS, false)); addTiles(2, TILE_SET, new VenisonTile(CITY_TWO_ACROSS, true)); - addTiles(2, TILE_SET, new VenisonTile(DISCONNECTED_CITY_TWO, false)); - addTiles(3, TILE_SET, - new VenisonTile(DISCONNECTED_CITY_TWO_ACROSS, false)); + addTiles(2, TILE_SET, new VenisonTile(TWO_CITY_TWO, false)); + addTiles(3, TILE_SET, new VenisonTile(TWO_CITY_TWO_ACROSS, false)); addTiles(5, TILE_SET, new VenisonTile(CITY_ONE, false)); addTiles(3, TILE_SET, new VenisonTile(CITY_ONE_ROAD_RIGHT, false));