Java 多米諾骨牌記憶游戲:揭示機制與游戲結束邏輯修復教程

Java 多米諾骨牌記憶游戲:揭示機制與游戲結束邏輯修復教程

本教程旨在解決Java多米諾骨牌記憶游戲中,匹配成功的多米諾骨牌無法保持揭示狀態,且游戲無法正常結束的問題。核心解決方案包括:正確重寫 Domino 類的 equals() 和 hashCode() 方法以實現基于內容的比較,而非內存地址比較;以及在 MemoryLane 類的 guess() 方法中調用 setRevealed() 方法來更新多米諾骨牌的顯示狀態。通過這些修正,游戲將能夠正確識別匹配,并按照預期邏輯結束。

問題分析:多米諾骨牌未正確揭示與游戲未結束的原因

在提供的多米諾骨牌記憶游戲代碼中,存在兩個主要問題導致游戲功能不完整:

  1. 多米諾骨牌匹配判斷不準確: MemoryLane 類中的 guess 方法使用 board[i] == board[k] 來判斷兩個多米諾骨牌是否匹配。在 Java 中,對于對象類型,== 運算符比較的是兩個引用是否指向內存中的同一個對象實例,而不是它們的內容是否相等。因此,即使兩個多米諾骨牌具有相同的 top 和 bottom 值,如果它們是不同的對象實例,== 也會返回 false。這導致了即使玩家猜對了,多米諾骨牌也無法被正確識別為匹配。
  2. 多米諾骨牌狀態未更新: Domino 類中包含 setRevealed(Boolean revealed) 方法用于設置多米諾骨牌的揭示狀態,但在 MemoryLane 類的 guess 方法中,當匹配成功時,并未調用此方法來將匹配的多米諾骨牌設置為已揭示。這使得 isRevealed() 方法始終返回 false(因為 revealed 成員變量的默認值為 false),從而導致 MemoryLane 的 gameOver() 方法無法正確判斷所有多米諾骨牌是否都被揭示,進而游戲無法結束。

解決方案一:正確實現對象比較——重寫 equals() 和 hashCode()

為了使 Domino 對象能夠基于其內容(即 top 和 bottom 值)進行比較,我們需要在 Domino 類中重寫 equals() 方法。同時,根據 Java 規范,如果重寫了 equals() 方法,也必須重寫 hashCode() 方法,以維護它們之間的契約:如果兩個對象通過 equals() 方法比較是相等的,那么它們的 hashCode() 方法必須產生相同的整數結果。

以下是 Domino 類中 equals() 和 hashCode() 方法的正確實現:

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() {         return revealed; // 簡化了原有的 if-else 結構     }      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) {         if (this == obj) { // 檢查是否是同一個對象實例             return true;         }         if (obj == NULL || getClass() != obj.getClass()) { // 檢查是否為null或類型不匹配             return false;         }         final Domino other = (Domino) obj; // 類型轉換         if (this.getTop() != other.getTop()) { // 比較 top 值             return false;         }         if (this.getBottom() != other.getBottom()) { // 比較 bottom 值             return false;         }         return true; // 如果 top 和 bottom 都相等,則認為對象相等     } }

說明:

  • equals(Object obj):
    • 首先檢查是否為同一個對象引用(this == obj),如果是則直接返回 true。
    • 然后檢查傳入對象是否為 null 或類型不匹配(getClass() != obj.getClass()),如果是則返回 false。
    • 將 obj 強制轉換為 Domino 類型。
    • 最后,比較 top 和 bottom 屬性是否相等。
  • hashCode():
    • 使用 top 和 bottom 屬性計算哈希值。一種常見的做法是使用一個質數(如59)乘以當前哈希值并加上字段值,以分散哈希碼并減少碰撞。

解決方案二:確保匹配多米諾骨牌保持揭示狀態

在 MemoryLane 類的 guess 方法中,當兩個多米諾骨牌被正確匹配時,需要調用它們的 setRevealed(true) 方法,將它們的狀態設置為已揭示。

立即學習Java免費學習筆記(深入)”;

以下是 MemoryLane 類中 guess 方法的修正:

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 方法進行內容比較         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 = ""; // 使用空字符串初始化,避免 String 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); // 當所有多米諾骨牌都已揭示時,游戲結束     }      // 調試方法,顯示所有多米諾骨牌的真實值(可選,原答案中包含)     public String debug() {         String text = "";         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 = "";         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;     } }

說明:

  • guess(int i, int k): 現在使用 board[i].equals(board[k]) 進行比較,這將正確判斷兩個多米諾骨牌的內容是否相等。
  • 如果判斷為匹配成功,則 board[i].setRevealed(true) 和 board[k].setRevealed(true) 將被調用,從而更新這些多米諾骨牌的 revealed 狀態。
  • gameOver() 方法現在能夠正確統計已揭示的多米諾骨牌數量,并在所有多米諾骨牌都被揭示時返回 true,從而使游戲正常結束。

注意事項與總結

  • equals() 與 hashCode() 契約: 再次強調,當您在 Java 中重寫 equals() 方法時,務必同時重寫 hashCode() 方法。這是 Java 集合框架(如 HashMap、HashSet)正確工作的基礎,否則可能導致對象在集合中行為異常。
  • 對象比較的正確性: 理解 == 運算符和 equals() 方法在對象比較上的區別至關重要。== 比較引用地址,而 equals() 比較對象內容(如果被正確重寫)。
  • 代碼可讀性與維護: 在 Domino 類的 isRevealed() 方法中,將 if (revealed) return true; return false; 簡化為 return revealed; 可以提高代碼的簡潔性。同樣,在 String text = new String(); 處,直接使用 String text = “”; 更為簡潔高效。
  • 驅動類不變性: 由于 MemoryLaneDriver 類被設定為不可修改,所有的修正都必須集中在 Domino 和 MemoryLane 這兩個核心業務邏輯類中,這正是本教程所遵循的原則。

通過以上修正,您的多米諾骨牌記憶游戲將能夠正確地識別匹配、揭示多米諾骨牌,并最終判斷游戲勝利,提供完整的游戲體驗。

? 版權聲明
THE END
喜歡就支持一下吧
點贊12 分享