战舰游戏舰船随机放置:子类实例化与间距控制技术问题
解决方案:战舰游戏舰船放置优化与碰撞校验
一、消除代码冗余:通用舰船放置方法
当前每个舰船单独写放置方法的问题在于重复了随机坐标、边界检查、网格更新的核心逻辑。可以抽象出通用方法,通过传入舰船类型、长度、数组索引等参数,复用所有放置逻辑:
通用放置方法实现
// 通用舰船放置方法,支持所有Ship子类 private <T extends Ship> void placeShip(Class<T> shipClass, int shipLength, int shipIndex, char[][] selfGrid) { int xCor, yCor; boolean isShipPlaced = false; // 随机生成舰船方向 int shipOrientation = Math.random() > 0.5 ? VERTICAL : HORIZONTAL; do { xCor = (int) (Math.random() * 9) + 1; yCor = (int) (Math.random() * 9) + 1; // 先判断边界,再校验位置合法性 if (shipOrientation == VERTICAL && yCor + shipLength <= BattleshipSystem.GRID_LENGTH) { if (isPositionValid(selfGrid, xCor, yCor, shipLength, VERTICAL)) { playerShips[shipIndex] = createShipInstance(shipClass, xCor, yCor, xCor, yCor + shipLength); updateGrid(selfGrid, xCor, yCor, shipLength, VERTICAL); isShipPlaced = true; } } else if (shipOrientation == HORIZONTAL && xCor + shipLength <= BattleshipSystem.GRID_LENGTH) { if (isPositionValid(selfGrid, xCor, yCor, shipLength, HORIZONTAL)) { playerShips[shipIndex] = createShipInstance(shipClass, xCor, yCor, xCor + shipLength, yCor); updateGrid(selfGrid, xCor, yCor, shipLength, HORIZONTAL); isShipPlaced = true; } } } while (!isShipPlaced); } // 单独抽出生成Ship实例的方法,避免反射逻辑混杂 private <T extends Ship> T createShipInstance(Class<T> shipClass, int x1, int y1, int x2, int y2) { try { return shipClass.getConstructor(int.class, int.class, int.class, int.class) .newInstance(x1, y1, x2, y2); } catch (Exception e) { throw new RuntimeException("Failed to create ship instance", e); } } // 单独抽出网格更新方法 private void updateGrid(char[][] grid, int x, int y, int length, int orientation) { if (orientation == VERTICAL) { for (int dy = y; dy < y + length; dy++) { grid[dy][x] = SHIP; } } else { for (int dx = x; dx < x + length; dx++) { grid[y][dx] = SHIP; } } }
调用示例
// 放置航母 placeShip(Carrier.class, Carrier.CARRIER_LENGTH, 0, selfGrid); // 放置战列舰 placeShip(Battleship.class, Battleship.BATTLESHIP_LENGTH, 1, selfGrid); // 放置潜艇 placeShip(Submarine.class, Submarine.SUBMARINE_LENGTH, 2, selfGrid); // 放置巡逻艇 placeShip(PatrolBoat.class, PatrolBoat.PATROL_BOAT_LENGTH, 3, selfGrid);
如果不想用反射,也可以用工厂模式替代createShipInstance方法,通过类型判断生成对应舰船实例,避免反射异常风险。
二、实现舰船不重叠且不相邻的校验
核心是检查舰船占据的所有格子,以及这些格子周围8个方向的相邻格子是否已存在SHIP。编写如下校验方法:
private boolean isPositionValid(char[][] grid, int x, int y, int length, int orientation) { if (orientation == VERTICAL) { // 覆盖舰船本身及上下左右斜向的所有相邻区域 for (int dy = y - 1; dy <= y + length; dy++) { for (int dx = x - 1; dx <= x + 1; dx++) { // 过滤网格外的坐标 if (dx >= 0 && dx < BattleshipSystem.GRID_LENGTH && dy >= 0 && dy < BattleshipSystem.GRID_LENGTH) { if (grid[dy][dx] == SHIP) { return false; } } } } } else { // 水平方向 for (int dx = x - 1; dx <= x + length; dx++) { for (int dy = y - 1; dy <= y + 1; dy++) { if (dx >= 0 && dx < BattleshipSystem.GRID_LENGTH && dy >= 0 && dy < BattleshipSystem.GRID_LENGTH) { if (grid[dy][dx] == SHIP) { return false; } } } } } return true; }
注意事项
- 原代码中的边界判断
yCor + Carrier.CARRIER_LENGTH < BattleshipSystem.GRID_LENGTH需修正为<=,避免舰船超出网格索引范围(比如网格长度为10时,索引0-9,yCor=5+长度5=10会越界) - 校验范围包含了舰船周围一圈的所有格子,确保满足“至少一格间距”的要求
额外优化建议
- 给抽象父类
Ship添加getLength()抽象方法,每个子类实现该方法返回自身长度,这样通用放置方法可直接调用ship.getLength(),无需手动传入长度参数 - 若随机放置多次仍无法找到有效位置,可添加重试次数限制,避免极端情况下的死循环
内容的提问来源于stack exchange,提问作者okaycomputer
相关产品推荐
相关产品推荐

