Java 记忆游戏多米诺牌显示与游戏结束逻辑修复教程

Java 记忆游戏多米诺牌显示与游戏结束逻辑修复教程

本教程旨在解决Java记忆游戏项目中多米诺牌无法正确显示已猜中牌面及游戏无法正常结束的问题。核心解决方案包括重写Domino类的equals和hashCode方法以实现对象内容的正确比较,以及在MemoryLane类的guess方法中调用setRevealed方法来更新多米诺牌的显示状态,从而确保游戏逻辑的完整性和正确性。

问题分析

在提供的java记忆游戏代码中,存在两个主要问题导致游戏无法按预期运行:

  1. 多米诺牌匹配判断不准确: MemoryLane类中的guess方法使用board[i] == board[k]来判断两个多米诺牌是否匹配。在Java中,==运算符对于对象类型而言,比较的是两个引用是否指向内存中的同一个对象实例,而不是它们所代表的逻辑内容是否相等。因此,即使两张多米诺牌(即两个Domino对象)的top和bottom值相同,只要它们是不同的对象实例,==比较就会返回false。
  2. 多米诺牌状态未更新: 当玩家成功猜对一对多米诺牌时,程序并未调用Domino对象的setRevealed(true)方法来将其标记为已揭示。这导致以下连锁问题:
    • Domino类的isRevealed()方法始终返回false(因为revealed成员变量从未被设置为true)。
    • MemoryLane类的toString()方法无法正确显示已匹配的多米诺牌(因为它依赖isRevealed()的状态)。
    • MemoryLane类的gameOver()方法始终返回false(因为它统计的是已揭示的多米诺牌数量,而这个数量始终为零),导致游戏无法结束。

此外,Domino类中原有的equals方法也存在逻辑错误,它比较的是top == bottom,这实际上是检查多米诺牌是否为双牌(如[1|1]),而不是比较两个不同的Domino对象是否相等。

解决方案

针对上述问题,我们需要对Domino类和MemoryLane类进行修改。

1. 修复 Domino 类:重写 equals() 和 hashCode() 方法

为了使MemoryLane类中的guess方法能够正确地比较两个Domino对象的内容(即它们的top和bottom值),我们需要在Domino类中重写equals(Object obj)方法。同时,根据Java的约定,如果重写了equals()方法,就必须同时重写hashCode()方法,以确保对象在哈希表(如HashMap、HashSet)中能正确工作,并维护equals和hashCode之间的契约:如果两个对象通过equals()方法比较为相等,那么它们的hashCode()方法必须返回相同的值。

Domino 类修改示例:

立即学习Java免费学习笔记(深入)”;

public class Domino {     private int top, bottom;     private boolean revealed;      public Domino(int x, int y) {         if (x > y) {             top = y;             bottom = x;         } else {             top = x;             bottom = y;         }     }      public int getTop() {         return top;     }      public int getBottom() {         return bottom;     }      public boolean isRevealed() {         // 简化原始逻辑:直接返回revealed状态         return revealed;     }      public void setRevealed(boolean revealed) {         this.revealed = revealed;     }      @Override     public int hashCode() {         int hash = 7;         hash = 59 * hash + this.getTop();         hash = 59 * hash + this.getBottom();         return hash;     }      @Override     public boolean equals(Object obj) {         // 1. 检查是否为同一个对象引用         if (this == obj) {             return true;         }         // 2. 检查传入对象是否为NULL或类型不匹配         if (obj == null || !(obj instanceof Domino)) {             return false;         }         // 3. 将传入对象向下转型         final Domino other = (Domino) obj;         // 4. 比较关键属性(top和bottom)         if (this.getTop() != other.getTop()) {             return false;         }         if (this.getBottom() != other.getBottom()) {             return false;         }         return true; // 所有属性都匹配,则认为对象相等     } }

注意事项:

  • equals方法的实现遵循了标准的约定:自反性、对称性、传递性、一致性以及对null的判断。
  • hashCode方法的实现确保了与equals方法的一致性,即相等的对象具有相同的哈希码。

2. 修复 MemoryLane 类:更新 guess() 方法

在MemoryLane类的guess方法中,我们需要将对象引用比较==替换为内容比较equals(),并在匹配成功时调用Domino对象的setRevealed(true)方法。

MemoryLane 类修改示例:

import java.util.Arrays; import java.util.Random;  public class MemoryLane {     private Domino[] board;      public MemoryLane(int max) {         board = new Domino[(max * max) + max];          int i = 0;         for (int top = 1; top <= max; top++) {             for (int bot = 1; bot <= max; bot++) {                 if (top <= bot) {                     board[i] = new Domino(top, bot);                     i++;                     board[i] = new Domino(top, bot);                     i++;                 }             }         }         shuffle();     }      private void shuffle() {         int index;         Random random = new Random();         for (int i = board.length - 1; i > 0; i--) {             index = random.nextInt(i + 1);             if (index != i) {                 Domino temp = board[index];                 board[index] = board[i];                 board[i] = temp;             }         }     }      public boolean guess(int i, int k) {         // 使用equals方法比较Domino对象的内容         if (board[i].equals(board[k])) {             // 如果匹配成功,则设置这两个多米诺牌为已揭示             board[i].setRevealed(true);             board[k].setRevealed(true);             return true;         }         return false;     }      public String peek(int a, int b) {         String text = new String();         text += ("[" + board[a].getTop() + "] [" + board[b].getTop() + "]n");         text += ("[" + board[a].getBottom() + "] [" + board[b].getBottom() + "]n");         return text;     }      public boolean gameOver() {         int count = 0;         for (int i = 0; i < board.length; i++) {             if (board[i].isRevealed()) {                 count++;             }         }         return (count == board.length);     }      // 可选:添加一个debug方法用于查看所有牌面,便于测试     public String debug() {         String text = new String();         for (int i = 0; i < board.length; i++) {             text += ("[" + board[i].getTop() + "] ");         }         text += ('n');         for (int i = 0; i < board.length; i++) {             text += ("[" + board[i].getBottom() + "] ");         }         return text;     }      @Override     public String toString() {         String text = new String();         for (int i = 0; i < board.length; i++) {             if (board[i].isRevealed()) {                 text += ("[" + board[i].getTop() + "] ");             } else {                 text += ("[ ] ");             }         }         text += ('n');         for (int i = 0; i < board.length; i++) {             if (board[i].isRevealed()) {                 text += ("[" + board[i].getBottom() + "] ");             } else {                 text += ("[ ] ");             }         }         return text;     } }

MemoryLaneDriver 类:

MemoryLaneDriver 类无需任何修改,因为它通过MemoryLane类的公共接口进行交互,而我们对MemoryLane和Domino类的修改都保持了其公共接口不变(或仅在内部逻辑上进行了优化)。

import java.util.Scanner;  public class MemoryLaneDriver {     public static void main(String[] args) {         String message = "Welcome to Memory Lane!" + "n" +                 "Choose two indexes to reveal the corresponding dominoes." + "n" +                 "If the dominoes match, they stay revealed." + "n" +                 "Reveal all the dominoes to win the game!" + "n";          System.out.println(message);          Scanner input = new Scanner(System.in);          MemoryLane game = new MemoryLane(2);          long start = System.currentTimeMillis();          while (!game.gameOver()) {             // 可选:在实际游戏中可以移除或注释掉此行,它用于调试时显示所有牌面             // System.out.println(game.debug());             System.out.println(game);              System.out.print("First:  ");             int first = input.nextInt();              System.out.print("Second: ");             int second = input.nextInt();              game.guess(first, second);             System.out.println(game.peek(first, second) + "n");         }          long stop = System.currentTimeMillis();         long elapsed = (stop - start) / 1000;          System.out.println(game);          System.out.println("nYou win!");         System.out.println("Total time: " + elapsed + "s");     } }

总结

通过以上修改,我们解决了记忆游戏中的核心逻辑问题:

  1. 正确识别匹配: 重写Domino类的equals方法,使得MemoryLane.guess方法能够基于多米诺牌的实际数值(top和bottom)进行准确比较。
  2. 正确更新状态: 在guess方法中,当检测到匹配时,调用setRevealed(true)将匹配的多米诺牌状态更新为已揭示。
  3. 正确显示与游戏结束: Domino.isRevealed()现在能返回正确状态,进而MemoryLane.toString()能够正确显示已揭示的牌面,并且MemoryLane.gameOver()能够准确判断游戏是否所有牌都已揭示,从而使游戏能够正常结束。

这个案例强调了在Java中处理自定义对象比较时,正确重写equals()和hashCode()方法的重要性,以及理解对象状态管理在游戏逻辑中的关键作用。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享