เครื่องกำเนิดบอร์ดสำหรับการใช้งาน Food Chain Magnate
ฉันใช้ตรรกะของเกมกระดานใน Java เพื่อฝึกฝนทักษะการเขียนโค้ด ฉันเลือกFood Chain Magnateเพราะเป็นเกมที่ค่อนข้างซับซ้อนซึ่งต้องใช้โครงสร้างข้อมูลที่แตกต่างกัน งานแรกของฉันอย่างหนึ่งคือการสร้างโครงสร้างข้อมูลสำหรับบอร์ดเกม ในเกมกระดานถูกสร้างขึ้นโดยใช้บางส่วน (ทั้งหมดขึ้นอยู่กับจำนวนผู้เล่น) จาก 20 แผ่นที่มี กระเบื้องแต่ละแผ่นมีขนาด 5 x 5 สี่เหลี่ยม เอกลักษณ์ของแต่ละไทล์ไม่สำคัญในระหว่างเกมเฉพาะในกรณีที่บางตารางอยู่ในไทล์อื่น
ฉันสร้างBoard
คลาสที่โดยพื้นฐานแล้วเป็น wrapper บนอาร์เรย์วัตถุ 2 มิติด้วยวิธีการเพิ่มเติมสำหรับการคำนวณและBoardGenerator
ที่สร้างBoard
s และเริ่มต้นด้วยเนื้อหาของไทล์ต่างๆ
บอร์ด 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
ระดับเป็นง่ายไม่เปลี่ยนรูประดับจุด 2D กับผู้สร้างPoint(int x, int y)
วิธีการx()
, y()
สำหรับการเรียกใช้และadd(int dx, int dy)
วิธีการที่ผลตอบแทนจุด (x + DX, y + DY) ฉันไม่ได้เขียนที่นี่เพื่อมุ่งเน้นในชั้นเรียนอื่น ๆ
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
ไฟล์มีคำอธิบายของ 20 กระเบื้อง สามารถดูเนื้อหาของแต่ละกระเบื้องได้ที่นี่:https://boardgamehelpers.com/FoodChainMagnate/MapTileKey.aspx(ไม่รวมไทล์ส่วนขยาย) เป็นไฟล์ข้อความธรรมดาที่มีบรรทัดละ 5 อักขระ แต่ละบรรทัดอธิบายถึงไทล์ (5 x 5) แต่ละไทล์จะถูกกำหนดอักขระตามที่แสดงในลิงก์อ้างอิงโดยเป็นไทล์ 5 บรรทัดแรก A, 5 ไทล์ B ถัดไปและอื่น ๆ อักขระแต่ละตัว (หรือกลุ่มอักขระ) แสดงถึงวัตถุ ตัวอย่างเช่นกระเบื้อง E อธิบายว่า
/-/OO
|BOOO
/O8H/
OOHH|
OO/-/
(อักขระ/
และ\
สามารถแสดงถึงหนึ่งในสองประเภทที่เป็นไปได้ของการเลี้ยวขึ้นอยู่กับบริบท)
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
คือ enum ที่มีค่าUP, RIGHT, DOWN, LEFT
(ตามลำดับ) House
เป็นคลาสข้อมูลธรรมดาที่มีorder
คุณสมบัติ แต่จะกลายพันธุ์คุณสมบัติอื่น ๆ ในระหว่างเกม GoodsSource
เป็นคลาสที่เรียบง่ายไม่เปลี่ยนรูปซึ่งสามารถมีได้ 3 อินสแตนซ์เท่านั้น
BoardGeneratorTest.java (สำหรับตัวอย่างวิธีการใช้งาน)
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)))));
}
}
คำตอบ
public static final int TILE_SIZE = 5;
อาจเรียกสิ่งนี้TILE_EDGE_SIZE
หรือสิ่งที่คล้ายกันเนื่องจากไทล์ไม่มี 5 เหลี่ยม
public Object get(int x, int y) {
เอาออกแค่get(Point p)
นี้ก็ใช้ได้ไม่เวิร์กกว่านี้
return OFF_LIMIT;
ค่าเวทมนตร์เดียวนี้ป้องกันไม่ให้คุณใช้คลาสที่ถูกต้องสำหรับ Array ค่าเวทมนตร์เป็นสิ่งที่คุณอาจต้องการหลีกเลี่ยง Optional<Square>
ถ้าคุณอยากจะหลีกเลี่ยงข้อยกเว้นการใช้งาน แต่ส่วนตัวผมจะโยนข้อยกเว้นและใช้แทนที่จะกลับOptional
null
throw new IndexOutOfBoundsException("Point " + new Point(x, y) + " is out of the board");
ฉันรู้ว่าคุณทำได้ตอนนี้ทำให้วิธีการแบบสมมาตรกับวิธีจัดการนอกขอบเขต
public int getWidth() {
...
public int getHeight() {
สมบูรณ์แบบไม่มีเหตุผลว่าทำไมแอปพลิเคชันจะกลับมาในOFF_LIMIT
ตอนนั้น
Map<Character, Object[][]> tileList = getTileList();
การผสมคอลเลกชันและอาร์เรย์ไม่ใช่ความคิดที่ดี เพียงใช้คอลเลกชัน
List<Character> randomTiles = new LinkedList<>(tileList.keySet());
เพื่อความชัดเจนคุณจะใช้ArrayList
ที่นี่ไม่ใช่รายการที่เชื่อมโยง
Collections.shuffle(randomTiles);
เนื่องจากการค้นหาดัชนีแบบสุ่มแล้วย้ายไปรอบ ๆ ในรายการที่เชื่อมโยงนั้นไม่ใช่ความคิดที่ดี
fillWithTile(board, tileList.get(randomTiles.get(i * heightTiles + j)), i * Board.TILE_SIZE, j * Board.TILE_SIZE, rnd.nextInt(4));
วิธีนี้เกิดขึ้นมากเกินไปให้แยกออก เหตุใดจึงเกิดขึ้นและกำลังทำอะไรอยู่ ทำไมถึงมีเวทย์มนตร์4
4 อย่าง?
if (2 * i + 1 < rows[rows.length - 1 - j].length()) {
อีกครั้งจากที่นี่เราจะเห็นว่าสิ่งต่าง ๆ กำลังดำเนินการอย่างไร แต่ไม่ใช่อะไรหรือทำไม JavaDoc ที่เป็นประโยชน์ (ถ้าไม่สมบูรณ์) ช่วยได้บ้าง แต่ความคิดเห็นจะได้รับการชื่นชม
private static Map<Character, Object[][]> getTileList() {
วิธีนี้มีการดำเนินการมากเกินไปปริมาณความซับซ้อนนั้นน่าตกใจ
if (lineChar == 'O') { // ... endless else if's
สวิตช์จะทำสิ่งมหัศจรรย์ แต่อย่าลืมbreak;
งบ
currentTileContent[i][Board.TILE_SIZE - lineCount] = null;
สิ่งที่เกี่ยวกับObject tileContent;
การประกาศแล้วตั้งในและในท้ายกำหนดให้switch
currentTileContent[i][Board.TILE_SIZE - lineCount]
คัดลอก / วางมากเกินไปถ้าคุณถามฉัน
currentTileContent[i][Board.TILE_SIZE - lineCount] = new Road(false, true, false, true);
false, true, false, true
อาถนนที่จะไป นั่นไม่ใช่true
ถนนใช้enum
แทนพารามิเตอร์บูลีน มันอยู่ใน Effective Java ซึ่งคุณควรอ่าน
EnumSet<Direction> possibleDirections = EnumSet.of(Direction.RIGHT, Direction.LEFT);
ดีกว่ามากคุณไม่เห็นด้วยเหรอ?
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 + ")");
}
หนึ่งค่าจะถูกส่งกลับ: a Road
. วิธีการอาจ? ง่ายมากที่จะแยกแยะ
currentTileContent[i][Board.TILE_SIZE - lineCount] = GoodsSource.SODA;
ตอนนี้ฉันเข้าใจแล้ว Road
, หรือไม่มีอะไรเป็นที่คาดหวัง ยังคงสร้างอินเทอร์เฟซเครื่องหมายเช่นอย่างน้อยที่สุดและมีและใช้งานได้โดยที่คุณไม่จำเป็นต้องใช้เพราะมันน่าเกลียดเกินไปGoodsSource
House
TileContent
Road
GoodsSource
Object
throw new Error("tiles.txt not available", e);
ไม่สามารถอ่านได้ทั้งหมดอาจเป็นข้อยกเว้นที่ดีกว่า RuntimeException
ควรเป็นที่ต้องการมากกว่า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);
ตกลงคุณกำลังสร้างบ้านหลังใหญ่ขึ้น ฉันคิดว่าฉันเป็นคนใจร้ายและสร้างบ้านที่ประกอบด้วยส่วนแยกต่างหาก ฉันหวังว่าบ้านของคุณจะเป็นสี่เหลี่ยม :) แต่จริงๆแล้วให้วิธีการอีกครั้ง
ค่า Enum สามารถเปรียบเทียบได้โดยตรงไม่จำเป็นต้องเปรียบเทียบorder
เพื่อความเท่าเทียมกัน
throw new IllegalArgumentException("Should not happen");
ฉันยอมรับข้อยกเว้นดังกล่าวไม่สามารถยอมรับได้
return this.canMove[inDirection.ordinal()];
หรือpossibleDirections.contains(inDirection)
(ดูด้านบน?)