
本文介绍一种高效、可扩展的无重复二维坐标对(x, y)随机生成方案,避免传统“重试法”性能退化问题,适用于战舰类游戏中船体自动布放场景。
本文介绍一种高效、可扩展的无重复二维坐标对(x, y)随机生成方案,避免传统“重试法”性能退化问题,适用于战舰类游戏中船体自动布放场景。
在开发类似《BattleShip》的网格类游戏时,一个核心挑战是:如何在有限棋盘(如10×10)上,批量、高效、无冲突地生成多个不重叠的船体位置? 每艘船由连续的单元格组成(如长度为3的横向或纵向序列),而所有船的单元格坐标(x, y)全局唯一——即任意两个单元格不能拥有完全相同的坐标对。
直接使用 Random.nextInt() 配合循环重试(“generate-then-check-if-used-else-retry”)看似简单,但在棋盘填充率升高时,碰撞概率急剧上升,最坏情况下可能陷入长时阻塞或无限循环,不符合生产级游戏逻辑的确定性与响应性要求。
更优解是采用确定性伪随机映射 + 全局状态管理策略。其核心思想不是“随机选点再验证”,而是将所有合法坐标对线性编号 → 通过可逆/遍历式伪随机置换 → 映射回二维坐标,从而保证:
- 每次调用获取新坐标对,天然不重复;
- 时间复杂度 O(1) 均摊,无重试开销;
- 支持提前预分配全部位置,便于碰撞预检。
以下是一个轻量、实用的实现方案(不依赖外部库,兼容 Java 8+):
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
import java.util.*;
public class NonRepeatingCoordinateGenerator {
private final int width;
private final int height;
private final int totalCells;
private final Random globalRandom;
private final Set<string> used = new HashSet(); // 用于快速查重(可选,仅调试/兜底)
// 使用线性同余生成器(LCG)模拟独立随机流 —— 关键优化点
private static final long A = 25214903917L; // JDK Random 的 multiplier
private static final long C = 11L; // JDK Random 的 addend
private static final long M = 1L nextUniqueCells(int n) {
List<cell> result = new ArrayList(n);
for (int i = 0; i <p>⚠️ <strong>关键注意事项:</strong> </p>
<ul>
<li>
<strong>不要在每艘船内单独调用 <code>nextUniqueCell()</code> 生成首格后“沿方向延伸”</strong>——这仍可能导致船体之间重叠(例如船A占(2,3)-(2,4)-(2,5),船B首格恰好生成(2,4))。正确做法是:<strong>对整艘船所需的所有单元格(如3个连续坐标),统一从 <code>nextUniqueCells(3)</code> 获取,再校验是否共线且方向一致</strong>;或更稳妥地——先随机选首格和方向,再检查该整条路径是否全部未被占用(使用 <code>Set<cell></cell></code> 快速判断)。 </li>
<li>上述 LCG 实现虽非密码学安全,但对游戏逻辑已足够:周期极长、分布均匀、无明显模式。若需更强随机性,可替换为 <code>ThreadLocalRandom.current().nextLong()</code> 驱动的 Fisher-Yates 洗牌(需预先构建全坐标列表,内存 O(W×H))。 </li>
<li>实际部署时,建议将所有已放置的 <code>Cell</code> 存入 <code>HashSet<cell></cell></code>(需确保 <code>Cell</code> 正确重写 <code>equals()</code> 和 <code>hashCode()</code>),每次生成新船前执行 <code>Collections.disjoint(newShipCells, occupiedCells)</code> 快速校验。</li>
</ul>
<p>总结:解决“无重复随机坐标对”问题,本质是<strong>将随机采样转化为确定性遍历</strong>。通过 LCG 映射或预洗牌,我们摆脱了不可控的重试循环,使布船逻辑兼具高性能、可预测性与可维护性——这才是面向真实游戏场景的工程化答案。</p></cell></string>Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










