在Java骨牌游戏开发中,玩家与CPU轮流操作机制的核心是通过状态管理明确当前操作主体,配合循环逻辑实现回合自动切换,同时保证操作合法性校验与流程可控性。

核心设计思路
首先需要定义游戏状态枚举,区分当前是玩家回合还是CPU回合,同时维护骨牌牌堆、玩家手牌、桌面骨牌序列等核心数据。回合切换时只需要修改状态枚举值,配合主循环判断当前操作主体即可。
状态枚举定义
通过枚举明确所有可能的游戏状态,避免硬编码字符串带来的维护问题:
// 定义游戏状态枚举
public enum GameState {
PLAYER_TURN, // 玩家回合
CPU_TURN, // CPU回合
GAME_OVER // 游戏结束
}
核心数据类设计
封装骨牌、玩家、游戏管理器三个核心类,分别管理单张骨牌信息、玩家手牌、整体游戏流程:
// 骨牌类,存储骨牌两端的数字
public class Domino {
private int left;
private int right;
public Domino(int left, int right) {
this.left = left;
this.right = right;
}
// 判断骨牌是否能接在桌面序列的左侧
public boolean canConnectLeft(int target) {
return left == target || right == target;
}
// 判断骨牌是否能接在桌面序列的右侧
public boolean canConnectRight(int target) {
return left == target || right == target;
}
// 获取骨牌左侧数字
public int getLeft() {
return left;
}
// 获取骨牌右侧数字
public int getRight() {
return right;
}
}
// 玩家类,管理玩家手牌
public class Player {
private List<Domino> handCards;
public Player() {
handCards = new ArrayList<>();
}
// 添加手牌
public void addCard(Domino domino) {
handCards.add(domino);
}
// 移除指定手牌
public boolean removeCard(Domino domino) {
return handCards.remove(domino);
}
// 获取当前手牌
public List<Domino> getHandCards() {
return handCards;
}
}
// 游戏管理器类,控制整体流程
public class DominoGameManager {
private GameState currentState;
private Player humanPlayer;
private Player cpuPlayer;
private List<Domino> desktopDominos; // 桌面骨牌序列
private List<Domino> dominoPool; // 剩余骨牌池
public DominoGameManager() {
currentState = GameState.PLAYER_TURN;
humanPlayer = new Player();
cpuPlayer = new Player();
desktopDominos = new ArrayList<>();
dominoPool = new ArrayList<>();
initDominoPool();
dealCards();
}
// 初始化骨牌池,生成所有0-6的骨牌组合
private void initDominoPool() {
for (int i = 0; i <= 6; i++) {
for (int j = i; j <= 6; j++) {
dominoPool.add(new Domino(i, j));
}
}
// 打乱骨牌顺序
Collections.shuffle(dominoPool);
}
// 发牌,玩家和CPU各拿7张
private void dealCards() {
for (int i = 0; i < 7; i++) {
humanPlayer.addCard(dominoPool.remove(0));
cpuPlayer.addCard(dominoPool.remove(0));
}
// 第一张骨牌放到桌面
desktopDominos.add(dominoPool.remove(0));
}
}
玩家回合实现逻辑
玩家回合需要等待用户输入操作,校验操作合法性后更新桌面骨牌序列,再切换到CPU回合。这里通过控制台输入模拟玩家操作,实际GUI开发可替换为事件监听逻辑。
// 游戏管理器中添加玩家回合处理方法
public void handlePlayerTurn() {
if (currentState != GameState.PLAYER_TURN) {
return;
}
System.out.println("当前是你的回合,你的手牌为:");
List<Domino> playerCards = humanPlayer.getHandCards();
for (int i = 0; i < playerCards.size(); i++) {
Domino d = playerCards.get(i);
System.out.println(i + ": [" + d.getLeft() + "|" + d.getRight() + "]");
}
System.out.println("请输入要出的骨牌序号,以及要接在左侧(l)还是右侧(r),格式如 0 l:");
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
String[] parts = input.split(" ");
if (parts.length != 2) {
System.out.println("输入格式错误,请重新输入");
return;
}
int cardIndex = Integer.parseInt(parts[0]);
String position = parts[1];
if (cardIndex < 0 || cardIndex >= playerCards.size()) {
System.out.println("骨牌序号错误,请重新输入");
return;
}
Domino selectedDomino = playerCards.get(cardIndex);
// 校验是否能接在指定位置
boolean isValid = false;
if ("l".equals(position)) {
int leftTarget = desktopDominos.get(0).getLeft();
isValid = selectedDomino.canConnectLeft(leftTarget);
if (isValid) {
// 调整骨牌方向后添加到左侧
if (selectedDomino.getRight() == leftTarget) {
desktopDominos.add(0, selectedDomino);
} else {
desktopDominos.add(0, new Domino(selectedDomino.getRight(), selectedDomino.getLeft()));
}
humanPlayer.removeCard(selectedDomino);
}
} else if ("r".equals(position)) {
int rightTarget = desktopDominos.get(desktopDominos.size() - 1).getRight();
isValid = selectedDomino.canConnectRight(rightTarget);
if (isValid) {
// 调整骨牌方向后添加到右侧
if (selectedDomino.getLeft() == rightTarget) {
desktopDominos.add(selectedDomino);
} else {
desktopDominos.add(new Domino(selectedDomino.getRight(), selectedDomino.getLeft()));
}
humanPlayer.removeCard(selectedDomino);
}
}
if (!isValid) {
System.out.println("该骨牌无法接在指定位置,请重新选择");
return;
}
// 检查玩家是否出完手牌,出完则游戏结束
if (humanPlayer.getHandCards().isEmpty()) {
currentState = GameState.GAME_OVER;
System.out.println("恭喜你,你赢了!");
return;
}
// 切换到CPU回合
currentState = GameState.CPU_TURN;
}
CPU回合实现逻辑
CPU回合需要实现自动决策逻辑,优先遍历手牌找到可出的骨牌,没有可出骨牌时从骨牌池抽牌,抽牌后仍无法出牌则跳过回合,最后切换到玩家回合。
// 游戏管理器中添加CPU回合处理方法
public void handleCPUTurn() {
if (currentState != GameState.CPU_TURN) {
return;
}
System.out.println("CPU的回合,正在思考...");
List<Domino> cpuCards = cpuPlayer.getHandCards();
Domino selectedDomino = null;
String position = null;
int leftTarget = desktopDominos.get(0).getLeft();
int rightTarget = desktopDominos.get(desktopDominos.size() - 1).getRight();
// 遍历CPU手牌,找可出的骨牌
for (Domino d : cpuCards) {
if (d.canConnectLeft(leftTarget)) {
selectedDomino = d;
position = "l";
break;
}
if (d.canConnectRight(rightTarget)) {
selectedDomino = d;
position = "r";
break;
}
}
// 没有可出的骨牌,从池里抽牌
if (selectedDomino == null) {
if (!dominoPool.isEmpty()) {
Domino drawDomino = dominoPool.remove(0);
cpuPlayer.addCard(drawDomino);
System.out.println("CPU从牌池抽了一张牌");
// 抽牌后再次检查是否能出
if (drawDomino.canConnectLeft(leftTarget)) {
selectedDomino = drawDomino;
position = "l";
} else if (drawDomino.canConnectRight(rightTarget)) {
selectedDomino = drawDomino;
position = "r";
}
}
}
// 仍然无法出牌,跳过回合
if (selectedDomino == null) {
System.out.println("CPU无法出牌,跳过回合");
currentState = GameState.PLAYER_TURN;
return;
}
// 执行出牌操作
if ("l".equals(position)) {
if (selectedDomino.getRight() == leftTarget) {
desktopDominos.add(0, selectedDomino);
} else {
desktopDominos.add(0, new Domino(selectedDomino.getRight(), selectedDomino.getLeft()));
}
cpuPlayer.removeCard(selectedDomino);
System.out.println("CPU出了骨牌 [" + selectedDomino.getLeft() + "|" + selectedDomino.getRight() + "] 接在左侧");
} else {
if (selectedDomino.getLeft() == rightTarget) {
desktopDominos.add(selectedDomino);
} else {
desktopDominos.add(new Domino(selectedDomino.getRight(), selectedDomino.getLeft()));
}
cpuPlayer.removeCard(selectedDomino);
System.out.println("CPU出了骨牌 [" + selectedDomino.getLeft() + "|" + selectedDomino.getRight() + "] 接在右侧");
}
// 检查CPU是否出完手牌
if (cpuPlayer.getHandCards().isEmpty()) {
currentState = GameState.GAME_OVER;
System.out.println("CPU出完了所有手牌,你输了!");
return;
}
// 切换到玩家回合
currentState = GameState.PLAYER_TURN;
}
主循环整合
最后通过主循环驱动整个游戏流程,根据当前状态调用对应的回合处理方法,直到游戏结束。
// 游戏管理器添加启动方法
public void startGame() {
while (currentState != GameState.GAME_OVER) {
switch (currentState) {
case PLAYER_TURN:
handlePlayerTurn();
break;
case CPU_TURN:
handleCPUTurn();
break;
}
}
}
// 主函数入口
public static void main(String[] args) {
DominoGameManager gameManager = new DominoGameManager();
gameManager.startGame();
}
注意事项
- 操作合法性校验需要覆盖所有边界情况,比如骨牌池为空时无法抽牌、输入序号越界等,避免程序异常崩溃。
- CPU决策逻辑可以根据需求优化,比如优先出数字大的骨牌、优先接右侧等,提升游戏趣味性。
- 如果是GUI版本开发,需要将控制台输入替换为按钮点击、拖拽等事件监听,回合切换时更新界面显示即可,核心状态逻辑不需要修改。