Food Chain Magnate uygulaması için pano üreteci
Kodlama becerilerimi geliştirmek için Java'da bir tahta oyununun mantığını uyguluyorum. Food Chain Magnate'i seçtim çünkü farklı veri yapıları gerektiren oldukça karmaşık bir oyun. İlk görevlerimden biri, oyun tahtası için veri yapısını oluşturmaktır. Oyunda, tahta mevcut 20 karonun bir kısmı (oyuncu sayısına bağlı olarak) kullanılarak oluşturulmuştur. Her karo 5 x 5 kareden oluşan bir ızgaradır. Oyun sırasında her bir karonun kimliği önemli değildir, sadece bir kare diğerinden farklı bir taşta ise.
Board
Temelde hesaplama için ek yöntemler içeren bir 2D Nesne dizisi üzerinde bir sarmalayıcı olan bir sınıf oluşturdum ve bu BoardGenerator
, Board
s oluşturup onu farklı döşemelerin içeriğiyle başlatır.
Board.java
package com.lartkma.fcm.model.board;
public class Board {
public static final int TILE_SIZE = 5;
public static final Object OFF_LIMIT = new Object();
private Object[][] boardSquares;
public Board(int widthTiles, int heightTiles) {
this.boardSquares = new Object[widthTiles * TILE_SIZE][heightTiles * TILE_SIZE];
}
public Object get(int x, int y) {
if (x >= 0 && x < this.boardSquares.length && y >= 0 && y < this.boardSquares[0].length) {
return this.boardSquares[x][y];
} else {
return OFF_LIMIT;
}
}
public Object get(Point p) {
return get(p.x(), p.y());
}
public void set(int x, int y, Object obj) {
if (x >= 0 && x < this.boardSquares.length && y >= 0 && y < this.boardSquares[0].length) {
this.boardSquares[x][y] = obj;
} else {
throw new IndexOutOfBoundsException("Point " + new Point(x, y) + " is out of the board");
}
}
public void set(Point p, Object obj) {
set(p.x(), p.y(), obj);
}
public int getWidth() {
return this.boardSquares.length;
}
public int getHeight() {
return this.boardSquares[0].length;
}
/**
* Returns the tile where the square belongs, relative to this board. The value
* is not related to the original tile used to build the board, only allows to
* differentiate one tile from another.
* @param p
* @return
*/
public int getTileNumber(Point p) {
return (p.y() / TILE_SIZE) * (this.boardSquares.length / TILE_SIZE) + (p.x() / TILE_SIZE);
}
}
Point
Sınıf, bir yapılandırıcı ile, basit, değişmez 2B noktası sınıf Point(int x, int y)
, yöntem x()
, y()
erişim için bir add(int dx, int dy)
yöntem bu döner noktası (x + dx, dy y +). Diğer derslere odaklanmak için buraya yazmıyorum.
BoardGenerator.java
package com.lartkma.fcm.model.board;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Stream;
public class BoardGenerator {
public static Board fromRandom(int widthTiles, int heightTiles) {
Random rnd = new Random();
Map<Character, Object[][]> tileList = getTileList();
List<Character> randomTiles = new LinkedList<>(tileList.keySet());
Collections.shuffle(randomTiles);
Board board = new Board(widthTiles, heightTiles);
for (int i = 0; i < widthTiles; i++) {
for (int j = 0; j < heightTiles; j++) {
fillWithTile(board, tileList.get(randomTiles.get(i * heightTiles + j)), i * Board.TILE_SIZE,
j * Board.TILE_SIZE, rnd.nextInt(4));
}
}
return board;
}
/**
* Generates a board using the tiles and rotations indicated in the expression.
*
* The expression is composed of (# tiles tall) subexpressions separated by
* newlines or spaces, each subexpression made of (# tiles wide x 2) characters.
*
* Each 2 characters of a subexpression describe a tile and the rotation of such
* tile. The tile is indicated with one of the upper-case characters used in
* <a href="https://boardgamehelpers.com/FoodChainMagnate/MapTileKey.aspx">this page</a>.
* The rotation is described as a digit from 1 to 4, where 1 is the orientation shown in
* the page mentioned above (with the identified in the bottom left), 2 rotates the
* reference orientation rotated 90 degrees clockwise, and so on.
*
* @param expression
* @return
*/
public static Board fromExpression(String expression) {
String[] rows = expression.split("\n|\r\n| ");
int heightTiles = rows.length;
int widthTiles = Stream.of(rows).mapToInt(s -> s.length() / 2).max().orElse(0);
Board board = new Board(widthTiles, heightTiles);
Map<Character, Object[][]> tileList = getTileList();
for (int i = 0; i < widthTiles; i++) {
for (int j = 0; j < heightTiles; j++) {
if (2 * i + 1 < rows[rows.length - 1 - j].length()) {
char tileId = rows[rows.length - 1 - j].charAt(2 * i);
char tileRotationFactor = rows[rows.length - 1 - j].charAt(2 * i + 1);
if (tileList.containsKey(tileId) && tileRotationFactor >= '1' && tileRotationFactor <= '4') {
// Number of rotations goes from 0 to 3
fillWithTile(board, tileList.get(tileId), i * Board.TILE_SIZE, j * Board.TILE_SIZE,
tileRotationFactor - '1');
} else {
throw new IllegalArgumentException(
"Board tile expression \"" + tileId + tileRotationFactor + "\" cannot be read");
}
}
}
}
return board;
}
private static Map<Character, Object[][]> getTileList() {
Map<Character, Object[][]> outputMap = new HashMap<>();
try (BufferedReader stream = new BufferedReader(
new InputStreamReader(BoardGenerator.class.getResourceAsStream("tiles.txt")))) {
int lineCount = 1;
Object[][] currentTileContent = new Object[Board.TILE_SIZE][Board.TILE_SIZE];
char currentTileIdentifier = 'A';
String currentLine;
while ((currentLine = stream.readLine()) != null) {
for (int i = 0; i < Board.TILE_SIZE; i++) {
char lineChar = currentLine.charAt(i);
if (lineChar == 'O') {
currentTileContent[i][Board.TILE_SIZE - lineCount] = null;
} else if (lineChar == '-') {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(false, true, false, true);
} else if (lineChar == '|') {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(true, false, true, false);
} else if (lineChar == '/') {
// check the previous and next squares in the same line to check if this is
// a up-to-right turn or a right-to-up turn
char previous = (i == 0 ? 'O' : currentLine.charAt(i - 1));
char next = (i == Board.TILE_SIZE - 1 ? 'O' : currentLine.charAt(i + 1));
if ((isHorizontalRoad(previous) || i == 0) && !isHorizontalRoad(next)) {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(true, false, false,
true);
} else if (!isHorizontalRoad(previous)
&& (isHorizontalRoad(next) || i == Board.TILE_SIZE - 1)) {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(false, true, true,
false);
} else {
throw new IllegalStateException("Unknown combination on ( " + currentLine + ")");
}
} else if (lineChar == '\\') {
// check the previous and next squares in the same line to check if this is
// a up-to-left turn or a left-to-up turn
char previous = (i == 0 ? 'O' : currentLine.charAt(i - 1));
char next = (i == Board.TILE_SIZE - 1 ? 'O' : currentLine.charAt(i + 1));
if ((isHorizontalRoad(previous) || i == 0) && !isHorizontalRoad(next)) {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(false, false, true,
true);
} else if (!isHorizontalRoad(previous)
&& (isHorizontalRoad(next) || i == Board.TILE_SIZE - 1)) {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(true, true, false,
false);
} else {
throw new IllegalStateException("Unknown combination on ( " + currentLine + ")");
}
} else if (lineChar == '^') {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(true, true, false, true);
} else if (lineChar == '>') {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(true, true, true, false);
} else if (lineChar == 'V') {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(false, true, true, true);
} else if (lineChar == '<') {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(true, false, true, true);
} else if (lineChar == '+') {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(true, true, true, true);
} else if (lineChar == 'S') {
currentTileContent[i][Board.TILE_SIZE - lineCount] = GoodsSource.SODA;
} else if (lineChar == 'B') {
currentTileContent[i][Board.TILE_SIZE - lineCount] = GoodsSource.BEER;
} else if (lineChar == 'L') {
currentTileContent[i][Board.TILE_SIZE - lineCount] = GoodsSource.LEMONADE;
} else if (lineChar >= '0' && lineChar <= '9') {
Object previous = (i == 0 ? null : currentTileContent[i - 1][Board.TILE_SIZE - lineCount]);
if (previous instanceof House) {
// part of a two-digit house, same entity as left
currentTileContent[i][Board.TILE_SIZE
- lineCount] = currentTileContent[i - 1][Board.TILE_SIZE - lineCount];
} else {
int houseOrder = (lineChar - '0'); // classic
char next = (i == Board.TILE_SIZE - 1 ? 'O' : currentLine.charAt(i + 1));
if (next >= '0' && next <= '9') { // two digit id
houseOrder = houseOrder * 10 + (next - '0');
}
currentTileContent[i][Board.TILE_SIZE - lineCount] = new House(houseOrder);
}
} else if (lineChar == 'H') {
Object previous = (i == 0 ? null : currentTileContent[i - 1][Board.TILE_SIZE - lineCount]);
if (previous instanceof House) {
// same entity as left
currentTileContent[i][Board.TILE_SIZE - lineCount] = previous;
} else {
previous = (lineCount == 1 ? null
: currentTileContent[i][Board.TILE_SIZE - lineCount + 1]);
if (previous instanceof House) {
// same entity as up
currentTileContent[i][Board.TILE_SIZE - lineCount] = previous;
} else {
throw new IllegalStateException(
"Unknown combination on ( " + currentLine + "): no house defined near H");
}
}
} else {
throw new IllegalStateException("Unknown symbol: " + lineChar);
}
}
lineCount += 1;
if (lineCount > Board.TILE_SIZE) {
outputMap.put(currentTileIdentifier, currentTileContent);
lineCount = 1;
currentTileContent = new Object[Board.TILE_SIZE][Board.TILE_SIZE];
currentTileIdentifier += 1;
}
}
return outputMap;
} catch (IOException e) {
throw new Error("tiles.txt not available", e);
}
}
private static boolean isHorizontalRoad(char c) {
return c == '-' || c == '/' || c == '\\' || c == '^' || c == 'V' || c == '+';
}
private static void fillWithTile(Board board, Object[][] tileArray, int xStart, int yStart, int numRotations) {
for (int i = 0; i < Board.TILE_SIZE; i++) {
for (int j = 0; j < Board.TILE_SIZE; j++) {
Point boardPoint = new Point(xStart + i, yStart + j);
Point tileCoords = toTileCoords(i, j, numRotations);
Object inTile = tileArray[tileCoords.x()][tileCoords.y()];
if (inTile instanceof House) {
Object prevHouse;
if ((prevHouse = board.get(boardPoint.add(-1, 0))) instanceof House
&& ((House) prevHouse).getOrder() == ((House) inTile).getOrder()) {
// check house at the left
board.set(boardPoint, prevHouse);
} else if ((prevHouse = board.get(boardPoint.add(0, -1))) instanceof House
&& ((House) prevHouse).getOrder() == ((House) inTile).getOrder()) {
// check house below
board.set(boardPoint, prevHouse);
} else {
board.set(boardPoint, new House(((House) inTile).getOrder()));
}
} else if (inTile instanceof Road) {
board.set(boardPoint, ((Road) inTile).rotate(numRotations));
} else if (inTile instanceof GoodsSource || inTile == null) {
board.set(boardPoint, inTile);
} else {
throw new IllegalStateException("Unknown object: " + inTile.getClass());
}
}
}
}
private static Point toTileCoords(int x, int y, int rotations) {
switch (rotations) {
case 0:
return new Point(x, y);
case 1:
return new Point(Board.TILE_SIZE - 1 - y, x);
case 2:
return new Point(Board.TILE_SIZE - 1 - x, Board.TILE_SIZE - 1 - y);
case 3:
return new Point(y, Board.TILE_SIZE - 1 - x);
default:
throw new IllegalArgumentException("Should not happen");
}
}
}
tiles.txt
Dosya 20 karoların açıklamasını içerir. Her bir döşemenin içeriği burada görülebilir:https://boardgamehelpers.com/FoodChainMagnate/MapTileKey.aspx(genişletme kutucuklarını içermez). Her biri 5 karakterden oluşan satırlardan oluşan düz metin dosyasıdır. Her 5 satır bir döşemeyi (5 x 5) tanımlar. Her karoya, referans bağlantısında gösterildiği gibi, ilk 5 satır A karesi, sonraki 5 karo B vb. Olmak üzere bir karakter atanır. Her karakter (veya karakter grubu) bir nesneyi temsil eder. Örneğin, E karosu şu şekilde tanımlanır:
/-/OO
|BOOO
/O8H/
OOHH|
OO/-/
(karakterler /
ve \
bağlama bağlı olarak iki olası dönüş türünden birini temsil edebilir)
Road.java
package com.lartkma.fcm.model.board;
import java.util.Arrays;
import java.util.StringJoiner;
public class Road {
private boolean[] canMove;
public Road(boolean canGoUp, boolean canGoRight, boolean canGoDown, boolean canGoLeft) {
this.canMove = new boolean[] { canGoUp, canGoRight, canGoDown, canGoLeft };
}
public boolean canMove(Direction inDirection) {
return this.canMove[inDirection.ordinal()];
}
public Road rotate(int amountRotations) {
Road rotated = new Road(this.canMove[0], this.canMove[1], this.canMove[2], this.canMove[3]);
if (amountRotations < 0) {
// Java operator % returns a remainder, that is different from a mathematical
// modulus
// https://stackoverflow.com/questions/5385024/mod-in-java-produces-negative-numbers
// https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.17.3
amountRotations = amountRotations % rotated.canMove.length + rotated.canMove.length;
} else {
amountRotations = amountRotations % rotated.canMove.length;
}
boolean swapTemp;
for (int k = 0; k < amountRotations; k++) {
for (int i = 1; i < rotated.canMove.length; i++) { // it makes no sense for the first element
swapTemp = rotated.canMove[0];
rotated.canMove[0] = rotated.canMove[i];
rotated.canMove[i] = swapTemp;
}
}
return rotated;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Road) {
return Arrays.equals(this.canMove, ((Road) obj).canMove);
} else {
return false;
}
}
@Override
public String toString() {
StringJoiner name = new StringJoiner("-", "Road[", "]");
for (Direction d : Direction.values()) {
if (canMove(d)) {
name.add(d.name());
}
}
return name.toString();
}
}
Direction
değerleri içeren bir enumdur UP, RIGHT, DOWN, LEFT
(bu sırayla). House
özelliği olan basit bir veri sınıfıdır order
, ancak oyun sırasında diğer özellikleri değiştirecektir. GoodsSource
sadece 3 olası örneğe sahip olabilen basit, değişmez bir sınıftır.
BoardGeneratorTest.java (nasıl kullanıldığına dair bir örnek için)
package com.lartkma.fcm.model.board;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.junit.Assert.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import com.lartkma.fcm.model.board.Board;
import com.lartkma.fcm.model.board.GoodsSource;
import com.lartkma.fcm.model.board.House;
import com.lartkma.fcm.model.board.BoardGenerator;
import com.lartkma.fcm.model.board.Road;
public class BoardGeneratorTest {
@Test
@DisplayName("The board generator can receive specific tiles as parameters and should generate the correct amount of squares")
public void testBoardGeneratorSizeFromExpression() {
Board board = BoardGenerator.fromExpression("G1E2\nI3M4");
assertAll("The board should be of 2 x 2 tiles (10 x 10 squares)",
() -> assertEquals(10, board.getWidth(), "Board width"),
() -> assertEquals(10, board.getHeight(), "Board height"));
}
@Test
@DisplayName("The board generator can generate a random board and should generate the correct amount of squares")
public void testBoardGeneratorSizeFromRandom() {
Board board = BoardGenerator.fromRandom(3, 2);
assertAll("The board should be of 3 x 2 tiles (15 x 10 squares)",
() -> assertEquals(15, board.getWidth(), "Board width"),
() -> assertEquals(10, board.getHeight(), "Board height"));
}
@Test
@DisplayName("The board generator can create a 1-tile board with the correct contents")
public void testBoardGeneratorContent() {
Board board = BoardGenerator.fromExpression("E1");
assertAll("The board should have the following contents",
() -> assertThat("In 0, 0", board.get(0, 0), is(nullValue())),
() -> assertThat("In 1, 0", board.get(1, 0), is(nullValue())),
() -> assertThat("In 2, 0", board.get(2, 0), is(equalTo(new Road(false, true, true, false)))),
() -> assertThat("In 3, 0", board.get(3, 0), is(equalTo(new Road(false, true, false, true)))),
() -> assertThat("In 4, 0", board.get(4, 0), is(equalTo(new Road(true, false, false, true)))),
() -> assertThat("In 0, 1", board.get(0, 1), is(nullValue())),
() -> assertThat("In 1, 1", board.get(1, 1), is(nullValue())),
() -> assertThat("In 2, 1", board.get(2, 1), is(equalTo(new House(8)))),
() -> assertThat("In 3, 1", board.get(3, 1), is(sameInstance(board.get(2, 1)))),
() -> assertThat("In 4, 1", board.get(4, 1), is(equalTo(new Road(true, false, true, false)))),
() -> assertThat("In 0, 2", board.get(0, 2), is(equalTo(new Road(true, false, false, true)))),
() -> assertThat("In 1, 2", board.get(1, 2), is(nullValue())),
() -> assertThat("In 2, 2", board.get(2, 2), is(sameInstance(board.get(2, 1)))),
() -> assertThat("In 3, 2", board.get(3, 2), is(sameInstance(board.get(2, 1)))),
() -> assertThat("In 4, 2", board.get(4, 2), is(equalTo(new Road(false, true, true, false)))),
() -> assertThat("In 0, 3", board.get(0, 3), is(equalTo(new Road(true, false, true, false)))),
() -> assertThat("In 1, 3", board.get(1, 3), is(equalTo(GoodsSource.BEER))),
() -> assertThat("In 2, 3", board.get(2, 3), is(nullValue())),
() -> assertThat("In 3, 3", board.get(3, 3), is(nullValue())),
() -> assertThat("In 4, 3", board.get(4, 3), is(nullValue())),
() -> assertThat("In 0, 4", board.get(0, 4), is(equalTo(new Road(false, true, true, false)))),
() -> assertThat("In 1, 4", board.get(1, 4), is(equalTo(new Road(false, true, false, true)))),
() -> assertThat("In 2, 4", board.get(2, 4), is(equalTo(new Road(true, false, false, true)))),
() -> assertThat("In 3, 4", board.get(3, 4), is(nullValue())),
() -> assertThat("In 4, 4", board.get(4, 4), is(nullValue())));
}
@Test
@DisplayName("The board generator can create a rotated 1-tile board with the correct contents")
public void testBoardGeneratorContentRotated() {
Board board = BoardGenerator.fromExpression("E2");
assertAll("The board should have the following contents",
() -> assertThat("In 0, 0", board.get(0, 0), is(equalTo(new Road(true, true, false, false)))),
() -> assertThat("In 1, 0", board.get(1, 0), is(equalTo(new Road(false, true, false, true)))),
() -> assertThat("In 2, 0", board.get(2, 0), is(equalTo(new Road(false, false, true, true)))),
() -> assertThat("In 3, 0", board.get(3, 0), is(nullValue())),
() -> assertThat("In 4, 0", board.get(4, 0), is(nullValue())),
() -> assertThat("In 0, 1", board.get(0, 1), is(equalTo(new Road(true, false, true, false)))),
() -> assertThat("In 1, 1", board.get(1, 1), is(equalTo(new House(8)))),
() -> assertThat("In 2, 1", board.get(2, 1), is(sameInstance(board.get(1, 1)))),
() -> assertThat("In 3, 1", board.get(3, 1), is(nullValue())),
() -> assertThat("In 4, 1", board.get(4, 1), is(nullValue())),
() -> assertThat("In 0, 2", board.get(0, 2), is(equalTo(new Road(false, false, true, true)))),
() -> assertThat("In 1, 2", board.get(1, 2), is(sameInstance(board.get(1, 1)))),
() -> assertThat("In 2, 2", board.get(2, 2), is(sameInstance(board.get(1, 1)))),
() -> assertThat("In 3, 2", board.get(3, 2), is(nullValue())),
() -> assertThat("In 4, 2", board.get(4, 2), is(equalTo(new Road(true, true, false, false)))),
() -> assertThat("In 0, 3", board.get(0, 3), is(nullValue())),
() -> assertThat("In 1, 3", board.get(1, 3), is(nullValue())),
() -> assertThat("In 2, 3", board.get(2, 3), is(nullValue())),
() -> assertThat("In 3, 3", board.get(3, 3), is(equalTo(GoodsSource.BEER))),
() -> assertThat("In 4, 3", board.get(4, 3), is(equalTo(new Road(true, false, true, false)))),
() -> assertThat("In 0, 4", board.get(0, 4), is(nullValue())),
() -> assertThat("In 1, 4", board.get(1, 4), is(nullValue())),
() -> assertThat("In 2, 4", board.get(2, 4), is(equalTo(new Road(true, true, false, false)))),
() -> assertThat("In 3, 4", board.get(3, 4), is(equalTo(new Road(false, true, false, true)))),
() -> assertThat("In 4, 4", board.get(4, 4), is(equalTo(new Road(false, false, true, true)))));
}
}
Yanıtlar
public static final int TILE_SIZE = 5;
TILE_EDGE_SIZE
Bir karo 5 kare içermediğinden, buna veya benzeri bir şey diyebilirsiniz .
public Object get(int x, int y) {
Bunu kaldır, sadece kullan get(Point p)
, o kadar fazla iş değil.
return OFF_LIMIT;
Bu tek sihirli değer , sizi Dizi için doğru sınıfı kullanmaktan alıkoyuyor. Sihirli değerler, kaçınmak isteyebileceğiniz şeylerdir. Bir istisnadan gerçekten kaçınmak istiyorsanız, kullanın Optional<Square>
. Ama şahsen ben bir istisna atar ve Optional
geri dönmek yerine kullanırdım null
.
throw new IndexOutOfBoundsException("Point " + new Point(x, y) + " is out of the board");
Bunu yapabileceğinizi biliyordum, şimdi yöntemleri sınırların dışında nasıl idare edildiklerine göre simetrik yapın .
public int getWidth() {
...
public int getHeight() {
Mükemmel, OFF_LIMIT
o zaman uygulamanın geri dönmesi için hiçbir neden yok .
Map<Character, Object[][]> tileList = getTileList();
Koleksiyonları ve dizileri karıştırmak iyi bir fikir değildir; sadece koleksiyonları kullanın.
List<Character> randomTiles = new LinkedList<>(tileList.keySet());
Kesin olmak gerekirse, ArrayList
bağlantılı bir liste değil, burayı kullanırsınız .
Collections.shuffle(randomTiles);
Çünkü rastgele dizinlere bakmak ve ardından bunları bağlantılı bir listede hareket ettirmek iyi bir fikir değildir.
fillWithTile(board, tileList.get(randomTiles.get(i * heightTiles + j)), i * Board.TILE_SIZE, j * Board.TILE_SIZE, rnd.nextInt(4));
Bu yöntemde çok şey oluyor, bölün. Bu neden oluyor ve ne yapıyor? Neden orada sihir 4
var, neyin 4'ü?
if (2 * i + 1 < rows[rows.length - 1 - j].length()) {
Yine buradan işlerin nasıl yapıldığını görebiliriz ama neyi veya niçin olduğunu göremeyiz. Yararlı (eksikse) JavaDoc biraz yardımcı olur, ancak bir yorum memnuniyetle karşılanacaktır.
private static Map<Character, Object[][]> getTileList() {
Bu yöntemde çok fazla şey yapılıyor, karmaşıklık miktarı hayret verici.
if (lineChar == 'O') { // ... endless else if's
Burada bir geçiş harikalar yaratabilir, ancak break;
ifadeleri unutmayın .
currentTileContent[i][Board.TILE_SIZE - lineCount] = null;
Object tileContent;
Beyanname ne olacak , sonra onu ayarlayın switch
ve sonunda atayın currentTileContent[i][Board.TILE_SIZE - lineCount]
. Bana sorarsan çok fazla kopyala / yapıştır.
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(false, true, false, true);
Ah, giden bir yol false, true, false, true
. Bu bir true
yol enum
değil, boole parametreleri yerine kullanın . Etkili Java'da okumanız gerekir.
EnumSet<Direction> possibleDirections = EnumSet.of(Direction.RIGHT, Direction.LEFT);
sadece çok daha güzel, katılmıyor musun?
char previous = (i == 0 ? 'O' : currentLine.charAt(i - 1));
char next = (i == Board.TILE_SIZE - 1 ? 'O' : currentLine.charAt(i + 1));
if ((isHorizontalRoad(previous) || i == 0) && !isHorizontalRoad(next)) {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(true, false, false,
true);
} else if (!isHorizontalRoad(previous)
&& (isHorizontalRoad(next) || i == Board.TILE_SIZE - 1)) {
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(false, true, true,
false);
} else {
throw new IllegalStateException("Unknown combination on ( " + currentLine + ")");
}
Bir değer döndürülür: a Road
. Yöntem belki? Ayırt etmek çok kolay.
currentTileContent[i][Board.TILE_SIZE - lineCount] = GoodsSource.SODA;
Ah, şimdi anladım. A Road
, GoodsSource
a House
veya hiçbir şey beklenmiyor. Yine de, TileContent
en azından gibi bir işaretleme arayüzü oluşturun ve buna sahip olun Road
ve GoodsSource
uygulayın, böylece ihtiyacınız olmaz Object
, çünkü bu çok çirkin.
throw new Error("tiles.txt not available", e);
Tam olarak okunamamak belki daha iyi bir istisnadır. Genelde sistem genelinde kurtarılamayan RuntimeException
yerine tercih edilmelidir .Error
} else if ((prevHouse = board.get(boardPoint.add(0, -1))) instanceof House
&& ((House) prevHouse).getOrder() == ((House) inTile).getOrder()) {
// check house below
board.set(boardPoint, prevHouse);
Tamam, yani daha büyük evler yaratıyorsun. Sanırım kaba olabilirim ve ayrı parçalardan oluşan bir ev yaratabilirim. Umarım evleriniz kare şeklindedir :) Ama gerçekten, yine yöntemler sağlayın.
Enum değerleri doğrudan karşılaştırılabilir, order
eşitlik için karşılaştırmaya gerek yoktur .
throw new IllegalArgumentException("Should not happen");
Orada katılıyorum, böyle bir istisna kabul edilemez.
return this.canMove[inDirection.ordinal()];
Veya possibleDirections.contains(inDirection)
(yukarıya bakın?)