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));