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 cf0b5c40..7aa0157a 100644
--- a/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiBoard.java
+++ b/projects/atlanti/src/java/com/samskivert/atlanti/client/AtlantiBoard.java
@@ -1,9 +1,10 @@
//
-// $Id: AtlantiBoard.java,v 1.1 2001/10/10 03:35:02 mdb Exp $
+// $Id: AtlantiBoard.java,v 1.2 2001/10/10 06:14:57 mdb Exp $
package com.threerings.venison;
import java.awt.*;
+import java.awt.event.*;
import javax.swing.*;
import java.util.Iterator;
@@ -15,8 +16,31 @@ import com.threerings.cocktail.cher.dobj.DSet;
* Displays the tiles that make up the Venison board.
*/
public class VenisonBoard
- extends JPanel implements VenisonTileCodes
+ extends JPanel implements TileCodes
{
+ /** The command posted when a tile is placed by the user on the
+ * board. */
+ public static final String TILE_PLACED_CMD = "tile_placed";
+
+ /**
+ * Constructs a Venison board.
+ */
+ public VenisonBoard ()
+ {
+ // create mouse adapters that will let us know when interesting
+ // mouse events happen
+ addMouseListener(new MouseAdapter() {
+ public void mouseClicked (MouseEvent evt) {
+ VenisonBoard.this.mouseClicked(evt);
+ }
+ });
+ addMouseMotionListener(new MouseMotionAdapter() {
+ public void mouseMoved (MouseEvent evt) {
+ VenisonBoard.this.mouseMoved(evt);
+ }
+ });
+ }
+
/**
* Sets the tiles that are displayed by this board. Causes the board
* to recompute its size based on the new tile layout.
@@ -34,15 +58,41 @@ public class VenisonBoard
revalidate();
}
+ /**
+ * Sets the tile to be placed on the board. The tile will be displayed
+ * in the square under the mouse cursor where it can be legally placed
+ * and its orientation will be determined based on the pointer's
+ * proximity to the edges of the target square. When the user clicks
+ * the mouse while the tile is in a placeable position, a
+ * TILE_PLACED command will be dispatched to the
+ * controller in scope. The coordinates and orientation of the tile
+ * will have been set to the values specified by the user.
+ */
+ public void setTileToBePlaced (VenisonTile tile)
+ {
+ _placingTile = tile;
+ // update our internal state based on this new placing tile
+ updatePlacingInfo(true);
+ repaint();
+ }
+
+ // documentation inherited
+ public void layout ()
+ {
+ super.layout();
+
+ // compute our translation coordinates based on our size
+ _tx = (getWidth() - TILE_WIDTH * _width)/2;
+ _ty = (getHeight() - TILE_HEIGHT * _height)/2;
+ }
+
// documentation inherited
public void paintComponent (Graphics g)
{
super.paintComponent(g);
// center the tile display if we are bigger than we need to be
- int tx = (getWidth() - TILE_WIDTH * _width)/2;
- int ty = (getHeight() - TILE_HEIGHT * _height)/2;
- g.translate(tx, ty);
+ g.translate(_tx, _ty);
// iterate over our tiles, painting each of them
Iterator iter = _tiles.elements();
@@ -51,8 +101,138 @@ public class VenisonBoard
tile.paint(g, _origX, _origY);
}
+ // if we have a placing tile, draw that one as well
+ if (_placingTile != null && _validPlacement) {
+ // if the current position is valid, draw the placing tile
+ _placingTile.paint(g, _origX, _origY);
+
+ // draw a green rectangle around the placing tile
+ g.setColor(Color.green);
+ int sx = (_placingTile.x + _origX) * TILE_WIDTH;
+ int sy = (_placingTile.y + _origY) * TILE_HEIGHT;
+ g.drawRect(sx, sy, TILE_WIDTH, TILE_HEIGHT);
+ }
+
// undo our translations
- g.translate(-tx, -ty);
+ g.translate(-_tx, -_ty);
+ }
+
+ /** Called by our adapter when the mouse moves. */
+ protected void mouseMoved (MouseEvent evt)
+ {
+ // we always want to know about our last mouse coordinates
+ _mouseX = evt.getX() - _tx;
+ _mouseY = evt.getY() - _ty;
+
+ // if we have a tile to be placed, update its coordinates
+ if (_placingTile != null) {
+ if (updatePlacingInfo(false)) {
+ repaint();
+ }
+ }
+ }
+
+ /** Called by our adapter when the mouse is clicked. */
+ protected void mouseClicked (MouseEvent evt)
+ {
+ // if we have a placing tile, we want to dispatch a command
+ // letting the controller know that the user placed it
+ if (_placingTile != null) {
+ }
+ }
+
+ /**
+ * Updates the coordinates and orientation of the placing tile based
+ * on the last known coordinates of the mouse and returns true if the
+ * coordinates or orientation changed from their previous values.
+ */
+ protected boolean updatePlacingInfo (boolean force)
+ {
+ boolean changed = false;
+
+ // convert mouse coordinates into tile coordinates and offset them
+ // by the origin
+ int x = divFloor(_mouseX, TILE_WIDTH) - _origX;
+ int y = divFloor(_mouseY, TILE_HEIGHT) - _origY;
+
+ // if these are different than the values currently in the placing
+ // tile, update the tile coordinates
+ if (_placingTile.x != x || _placingTile.y != y || force) {
+ // update the coordinates of the tile
+ _placingTile.x = x;
+ _placingTile.y = y;
+
+ // we've changed the display, so make a note of it
+ changed = true;
+
+ // we also need to recompute the valid orientations for the
+ // tile in this new position
+ _validOrients = TileUtil.computeValidOrients(
+ _tiles.elements(), _placingTile);
+
+ // if we've changed positions, clear out our valid placement
+ // flag
+ _validPlacement = false;
+ }
+
+ // determine if we should change the orientation based on the
+ // position of the mouse within the tile boundaries
+ int rx = _mouseX % TILE_WIDTH;
+ int ry = _mouseY % TILE_HEIGHT;
+ int orient = coordToOrient(rx, ry);
+
+ // scan for a legal orientation that is closest to our desired
+ // orientation
+ for (int i = 0; i < 4; i++) {
+ int candOrient = (orient+i)%4;
+ if (_validOrients[candOrient]) {
+ if (_placingTile.orientation != candOrient) {
+ _placingTile.orientation = candOrient;
+ changed = true;
+ }
+ _validPlacement = true;
+ break;
+ }
+ }
+
+ return changed;
+ }
+
+ /**
+ * Converts mouse coordinates which are relative to a particular tile,
+ * into an orientation based on the position within that tile. A tile
+ * is divided up into four quadrants by lines connecting its four
+ * corners. If the tile is in a quadrant closes to an edge, it is
+ * converted to the orientation corresponding with that edge.
+ *
+ * @param rx the mouse coordinates modulo tile width.
+ * @param ry the mouse coordinates modulo tile height.
+ *
+ * @return the orientation desired for the tile in which the mouse
+ * resides.
+ */
+ protected int coordToOrient (int rx, int ry)
+ {
+ if (rx > ry) {
+ if (rx > (TILE_HEIGHT - ry)) {
+ return EAST;
+ } else {
+ return NORTH;
+ }
+ } else {
+ if (rx > (TILE_HEIGHT - ry)) {
+ return SOUTH;
+ } else {
+ return WEST;
+ }
+ }
+ }
+
+ /** Divides the two integers returning the floor of the divided value
+ * rather than its truncation. */
+ protected static int divFloor (int value, int divisor)
+ {
+ return (int)Math.floor((double)value/divisor);
}
// documentation inherited
@@ -91,6 +271,10 @@ public class VenisonBoard
}
}
+ // spread our bounds out by one
+ minX -= 1; minY -= 1;
+ maxX += 1; maxY += 1;
+
// now we can compute our width and the origin offset
_origX = -minX;
_origY = -minY;
@@ -106,6 +290,7 @@ public class VenisonBoard
VenisonBoard board = new VenisonBoard();
TestDSet set = new TestDSet();
+ set.addTile(new VenisonTile(CITY_TWO, false, WEST, 0, 0));
set.addTile(new VenisonTile(CITY_FOUR, false, NORTH, 0, 1));
set.addTile(new VenisonTile(CITY_THREE, false, NORTH, 1, 1));
set.addTile(new VenisonTile(CITY_THREE_ROAD, false, NORTH, 1, 2));
@@ -113,6 +298,9 @@ public class VenisonBoard
set.addTile(new VenisonTile(CITY_FOUR, false, NORTH, -2, 0));
board.setTiles(set);
+ VenisonTile placing = new VenisonTile(CITY_TWO, false, NORTH, 0, 0);
+ board.setTileToBePlaced(placing);
+
frame.getContentPane().add(board, BorderLayout.CENTER);
frame.pack();
SwingUtil.centerWindow(frame);
@@ -135,9 +323,26 @@ public class VenisonBoard
/** A reference to our tile set. */
protected DSet _tiles;
+ /** The tile currently being placed by the user. */
+ protected VenisonTile _placingTile;
+
+ /** Whether or not the current position and orientation of the placing
+ * tile is valid. */
+ protected boolean _validPlacement = false;
+
+ /** An array indicating which of the four directions are valid
+ * placements based on the current position of the placing tile. */
+ protected boolean[] _validOrients;
+
+ /** Our render offset in pixels. */
+ protected int _tx, _ty;
+
/** The offset in tile coordinates of the origin. */
protected int _origX, _origY;
/** The width and height of the board in tile coordinates. */
protected int _width, _height;
+
+ /** The last known position of the mouse. */
+ protected int _mouseX, _mouseY;
}
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 42e982c8..39190c94 100644
--- a/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiTile.java
+++ b/projects/atlanti/src/java/com/samskivert/atlanti/data/AtlantiTile.java
@@ -1,8 +1,9 @@
//
-// $Id: AtlantiTile.java,v 1.1 2001/10/10 03:35:02 mdb Exp $
+// $Id: AtlantiTile.java,v 1.2 2001/10/10 06:14:57 mdb Exp $
package com.threerings.venison;
+import java.awt.Color;
import java.awt.Graphics;
import java.io.IOException;
@@ -15,12 +16,8 @@ import com.threerings.cocktail.cher.dobj.DSet;
* Represents a single tile in play on the Venison game board.
*/
public class VenisonTile
- implements DSet.Element, VenisonTileCodes
+ implements DSet.Element, TileCodes, Cloneable
{
- /** The starting tile. */
- public static final VenisonTile STARTING_TILE =
- new VenisonTile(CITY_ONE_ROAD_STRAIGHT, false, NORTH, 0, 0);
-
/** The tile type. */
public int type;
@@ -46,6 +43,15 @@ public class VenisonTile
this.y = y;
}
+ /**
+ * Constructs a tile with the type information set, but in the default
+ * NORTH orientation and with no position.
+ */
+ public VenisonTile (int type, boolean hasShield)
+ {
+ this(type, hasShield, NORTH, 0, 0);
+ }
+
/**
* Constructs a blank tile, suitable for unserialization.
*/
@@ -72,11 +78,52 @@ public class VenisonTile
int sy = (y + yoff) * TILE_HEIGHT;
// draw a rectangle
+ g.setColor(Color.black);
g.drawRect(sx, sy, TILE_WIDTH, TILE_HEIGHT);
// and draw our tile id in the middle for now (we'll eventually
// draw tile images)
- g.drawString(type + "/" + x + "/" + y, sx + 20, sy + 20);
+ String txt = type + "/" + x + "/" + y + "/" +
+ ORIENT_NAMES[orientation];
+ g.drawString(txt, sx + 20, sy + 20);
+
+ // draw little dots indicating the color of our sides
+ for (int i = 0; i < 4; i++) {
+ // adjust for our orientation
+ switch (TileUtil.getEdge(type, (i+(4-orientation))%4)) {
+ case GRASS:
+ g.setColor(Color.green);
+ break;
+ case ROAD:
+ g.setColor(Color.white);
+ break;
+ case CITY:
+ g.setColor(Color.red);
+ break;
+ }
+ switch (i) {
+ case NORTH:
+ g.fillOval(sx + TILE_WIDTH/2 - 2, sy + 2, 4, 4);
+ break;
+ case EAST:
+ g.fillOval(sx + TILE_WIDTH - 6, sy + TILE_HEIGHT/2 - 2, 4, 4);
+ break;
+ case SOUTH:
+ g.fillOval(sx + TILE_WIDTH/2 - 2, sy + TILE_HEIGHT - 6, 4, 4);
+ break;
+ case WEST:
+ g.fillOval(sx + 2, sy + TILE_HEIGHT/2 - 2, 4, 4);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Returns a copy of this Venison tile object.
+ */
+ public Object clone ()
+ {
+ return new VenisonTile(type, hasShield, orientation, x, y);
}
// documentation inherited
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 6bab75a1..d87aa602 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.1 2001/10/10 03:35:02 mdb Exp $
+// $Id: TileCodes.java,v 1.2 2001/10/10 06:14:57 mdb Exp $
package com.threerings.venison;
@@ -7,7 +7,7 @@ package com.threerings.venison;
* A repository for constants related to the tiles that are used in the
* game of Venison.
*/
-public interface VenisonTileCodes
+public interface TileCodes
{
/** A four-sided city tile. */
public static final int CITY_FOUR = 1;
@@ -75,6 +75,7 @@ public interface VenisonTileCodes
/** A curved road segment. */
public static final int CURVED_ROAD = 19;
+
/** A tile orientation constant indicating the tile is in its default
* orientation. */
public static final int NORTH = 0;
@@ -91,9 +92,23 @@ public interface VenisonTileCodes
* degrees clockwise from its default orientation. */
public static final int WEST = 3;
+ /** A mapping from orientation codes to a string representation. */
+ public static final String[] ORIENT_NAMES =
+ new String[] { "N", "E", "S", "W" };
+
/** The tile image width in pixels. */
public static int TILE_WIDTH = 90;
/** The tile image height in pixels. */
public static int TILE_HEIGHT = 90;
+
+
+ /** A tile edge constant indicating a city edge. */
+ public static final int CITY = 0;
+
+ /** A tile edge constant indicating a grass edge. */
+ public static final int GRASS = 1;
+
+ /** A tile edge constant indicating a road edge. */
+ public static final int ROAD = 2;
}
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 bce70922..971b782c 100644
--- a/projects/atlanti/src/java/com/samskivert/atlanti/server/AtlantiManager.java
+++ b/projects/atlanti/src/java/com/samskivert/atlanti/server/AtlantiManager.java
@@ -1,5 +1,5 @@
//
-// $Id: AtlantiManager.java,v 1.2 2001/10/10 03:35:02 mdb Exp $
+// $Id: AtlantiManager.java,v 1.3 2001/10/10 06:14:57 mdb Exp $
package com.threerings.venison;
@@ -37,7 +37,7 @@ public class VenisonManager
// clear out the tile set
_venobj.setTiles(new DSet(VenisonTile.class));
- _venobj.addToTiles(VenisonTile.STARTING_TILE);
+ _venobj.addToTiles(TileUtil.STARTING_TILE);
}
protected VenisonObject _venobj;
diff --git a/projects/atlanti/src/java/com/samskivert/atlanti/util/TileUtil.java b/projects/atlanti/src/java/com/samskivert/atlanti/util/TileUtil.java
new file mode 100644
index 00000000..f7c1ebc9
--- /dev/null
+++ b/projects/atlanti/src/java/com/samskivert/atlanti/util/TileUtil.java
@@ -0,0 +1,200 @@
+//
+// $Id: TileUtil.java,v 1.1 2001/10/10 06:14:57 mdb Exp $
+
+package com.threerings.venison;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * Utility functions relating to the Venison tiles.
+ */
+public class TileUtil implements TileCodes
+{
+ /** The starting tile. */
+ public static final VenisonTile STARTING_TILE =
+ new VenisonTile(CITY_ONE_ROAD_STRAIGHT, false, NORTH, 0, 0);
+
+ /**
+ * Returns a list containing the standard tile set for the Venison
+ * game. The list is a clone, so it can be bent, folded and modified
+ * by the caller.
+ */
+ public static List getStandardTileSet ()
+ {
+ return (List)TILE_SET.clone();
+ }
+
+ /**
+ * Scans the supplied tile set to determine which of the four
+ * orientations of the supplied target tile would result in a valid
+ * placement of that tile (valid placement meaning that all of its
+ * edges match up with neighboring tiles, it abuts at least one tile
+ * and it does not occupy the same space as any existing tile). The
+ * 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 target the tile whose valid orientations we wish to compute.
+ *
+ * @return an array of boolean values indicating whether or not the
+ * tile can be placed in each of the cardinal directions (which match
+ * up with the direction constants specified in {@link TileCodes}.
+ */
+ public static boolean[] computeValidOrients (
+ Iterator 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();
+
+ // figure out where this tile is in relation to the candidate
+ int xdiff = tile.x - target.x;
+ int ydiff = tile.y - target.y;
+ int sum = Math.abs(xdiff) + Math.abs(ydiff);
+
+ if (sum == 0) {
+ // they overlap, nothing doing
+ return new boolean[4];
+
+ } else if (sum == 1) {
+ // they're neighbors, we may have a match
+ int targetEdge = EDGE_MAP[(ydiff+1)*3 + xdiff+1];
+ // we want the edge of the placed tile that matches up
+ // with the tile in the candidate location, but we also
+ // need to take into account the orientation of the placed
+ // tile
+ int tileEdge = (targetEdge+(4-tile.orientation)+2) % 4;
+
+ // we iterate over the four possible orientations of the
+ // target tile
+ for (int i = 0; i < 4; i++) {
+ // 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)) {
+ // increment the edge matches
+ matches[i]++;
+
+ } else {
+ // if we have a mismatch, we want to ensure that
+ // we screw this orientation up for good, so we
+ // deduct a large value from the array to ensure
+ // that it will remain less than zero regardless
+ // of which of the other three tiles match in this
+ // orientation
+ matches[i] -= 10;
+ }
+ }
+ }
+ }
+
+ // for every orientation that we have a positive number of edge
+ // matches, we have a valid orientation
+ boolean[] orients = new boolean[4];
+ for (int i = 0; i < matches.length; i++) {
+ orients[i] = (matches[i] > 0);
+ }
+ return orients;
+ }
+
+ /**
+ * Returns the edge type for specified edge of the specified tile
+ * type.
+ *
+ * @param tileType the type of the tile in question.
+ * @param edge the direction constant indicating the edge in which we
+ * are interested.
+ *
+ * @return the edge constant for the edge in question.
+ */
+ public static int getEdge (int tileType, int edge)
+ {
+ return TILE_EDGES[4*tileType + edge];
+ }
+
+ /** Used to generate our standard tile set. */
+ protected static void addTiles (int count, List list, VenisonTile tile)
+ {
+ for (int i = 0; i < count-1; i++) {
+ list.add(tile.clone());
+ }
+ list.add(tile);
+ }
+
+ /** The standard tile set for a game of Venison. */
+ protected static ArrayList TILE_SET = new ArrayList();
+
+ // create our standard tile set
+ static {
+ addTiles(1, TILE_SET, new VenisonTile(CITY_FOUR, true));
+
+ addTiles(3, TILE_SET, new VenisonTile(CITY_THREE, false));
+ addTiles(1, TILE_SET, new VenisonTile(CITY_THREE, true));
+ addTiles(1, TILE_SET, new VenisonTile(CITY_THREE_ROAD, false));
+ addTiles(2, TILE_SET, new VenisonTile(CITY_THREE_ROAD, true));
+
+ addTiles(3, TILE_SET, new VenisonTile(CITY_TWO, false));
+ addTiles(2, TILE_SET, new VenisonTile(CITY_TWO, true));
+ addTiles(3, TILE_SET, new VenisonTile(CITY_TWO_ROAD, false));
+ addTiles(2, TILE_SET, new VenisonTile(CITY_TWO_ROAD, true));
+ 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(5, TILE_SET, new VenisonTile(CITY_ONE, false));
+ addTiles(3, TILE_SET, new VenisonTile(CITY_ONE_ROAD_RIGHT, false));
+ addTiles(3, TILE_SET, new VenisonTile(CITY_ONE_ROAD_LEFT, false));
+ addTiles(3, TILE_SET, new VenisonTile(CITY_ONE_ROAD_TEE, false));
+ addTiles(3, TILE_SET, new VenisonTile(CITY_ONE_ROAD_STRAIGHT, false));
+
+ addTiles(4, TILE_SET, new VenisonTile(CLOISTER, false));
+ addTiles(2, TILE_SET, new VenisonTile(CLOISTER_ROAD, false));
+
+ addTiles(1, TILE_SET, new VenisonTile(FOUR_WAY_ROAD, false));
+ addTiles(4, TILE_SET, new VenisonTile(THREE_WAY_ROAD, false));
+ addTiles(8, TILE_SET, new VenisonTile(STRAIGHT_ROAD, false));
+ addTiles(9, TILE_SET, new VenisonTile(CURVED_ROAD, false));
+ }
+
+ /** Used to figure out which edges match up to which when comparing
+ * adjacent tiles. */
+ protected static final int[] EDGE_MAP = new int[] {
+ -1, NORTH, -1,
+ WEST, -1, EAST,
+ -1, SOUTH, -1
+ };
+
+ /** A table indicating which tiles have which edges. */
+ protected static final int[] TILE_EDGES = new int[] {
+ -1, -1, -1, -1, // null tile
+ CITY, CITY, CITY, CITY, // CITY_FOUR
+ CITY, CITY, GRASS, CITY, // CITY_THREE
+ CITY, CITY, ROAD, CITY, // CITY_THREE_ROAD
+ 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, GRASS, GRASS, GRASS, // CITY_ONE
+ CITY, ROAD, ROAD, GRASS, // CITY_ONE_ROAD_RIGHT
+ CITY, GRASS, ROAD, ROAD, // CITY_ONE_ROAD_LEFT
+ CITY, ROAD, ROAD, ROAD, // CITY_ONE_ROAD_TEE
+ CITY, ROAD, GRASS, ROAD, // CITY_ONE_ROAD_STRAIGHT
+ GRASS, GRASS, GRASS, GRASS, // CLOISTER
+ GRASS, GRASS, ROAD, GRASS, // CLOISTER_ROAD
+ ROAD, ROAD, ROAD, ROAD, // FOUR_WAY_ROAD
+ GRASS, ROAD, ROAD, ROAD, // THREE_WAY_ROAD
+ ROAD, GRASS, ROAD, GRASS, // STRAIGHT_ROAD
+ GRASS, GRASS, ROAD, ROAD, // CURVED_ROAD
+ };
+}