本教程旨在解決Java記憶游戲項(xiàng)目中多米諾牌無(wú)法正確顯示已猜中牌面及游戲無(wú)法正常結(jié)束的問(wèn)題。核心解決方案包括重寫(xiě)Domino類(lèi)的equals和hashCode方法以實(shí)現(xiàn)對(duì)象內(nèi)容的正確比較,以及在MemoryLane類(lèi)的guess方法中調(diào)用setRevealed方法來(lái)更新多米諾牌的顯示狀態(tài),從而確保游戲邏輯的完整性和正確性。
問(wèn)題分析
在提供的java記憶游戲代碼中,存在兩個(gè)主要問(wèn)題導(dǎo)致游戲無(wú)法按預(yù)期運(yùn)行:
- 多米諾牌匹配判斷不準(zhǔn)確: MemoryLane類(lèi)中的guess方法使用board[i] == board[k]來(lái)判斷兩個(gè)多米諾牌是否匹配。在Java中,==運(yùn)算符對(duì)于對(duì)象類(lèi)型而言,比較的是兩個(gè)引用是否指向內(nèi)存中的同一個(gè)對(duì)象實(shí)例,而不是它們所代表的邏輯內(nèi)容是否相等。因此,即使兩張多米諾牌(即兩個(gè)Domino對(duì)象)的top和bottom值相同,只要它們是不同的對(duì)象實(shí)例,==比較就會(huì)返回false。
- 多米諾牌狀態(tài)未更新: 當(dāng)玩家成功猜對(duì)一對(duì)多米諾牌時(shí),程序并未調(diào)用Domino對(duì)象的setRevealed(true)方法來(lái)將其標(biāo)記為已揭示。這導(dǎo)致以下連鎖問(wèn)題:
- Domino類(lèi)的isRevealed()方法始終返回false(因?yàn)閞evealed成員變量從未被設(shè)置為true)。
- MemoryLane類(lèi)的toString()方法無(wú)法正確顯示已匹配的多米諾牌(因?yàn)樗蕾?lài)isRevealed()的狀態(tài))。
- MemoryLane類(lèi)的gameOver()方法始終返回false(因?yàn)樗y(tǒng)計(jì)的是已揭示的多米諾牌數(shù)量,而這個(gè)數(shù)量始終為零),導(dǎo)致游戲無(wú)法結(jié)束。
此外,Domino類(lèi)中原有的equals方法也存在邏輯錯(cuò)誤,它比較的是top == bottom,這實(shí)際上是檢查多米諾牌是否為雙牌(如[1|1]),而不是比較兩個(gè)不同的Domino對(duì)象是否相等。
解決方案
針對(duì)上述問(wèn)題,我們需要對(duì)Domino類(lèi)和MemoryLane類(lèi)進(jìn)行修改。
1. 修復(fù) Domino 類(lèi):重寫(xiě) equals() 和 hashCode() 方法
為了使MemoryLane類(lèi)中的guess方法能夠正確地比較兩個(gè)Domino對(duì)象的內(nèi)容(即它們的top和bottom值),我們需要在Domino類(lèi)中重寫(xiě)equals(Object obj)方法。同時(shí),根據(jù)Java的約定,如果重寫(xiě)了equals()方法,就必須同時(shí)重寫(xiě)hashCode()方法,以確保對(duì)象在哈希表(如HashMap、HashSet)中能正確工作,并維護(hù)equals和hashCode之間的契約:如果兩個(gè)對(duì)象通過(guò)equals()方法比較為相等,那么它們的hashCode()方法必須返回相同的值。
Domino 類(lèi)修改示例:
立即學(xué)習(xí)“Java免費(fèi)學(xué)習(xí)筆記(深入)”;
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() { // 簡(jiǎn)化原始邏輯:直接返回revealed狀態(tài) 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. 檢查是否為同一個(gè)對(duì)象引用 if (this == obj) { return true; } // 2. 檢查傳入對(duì)象是否為NULL或類(lèi)型不匹配 if (obj == null || !(obj instanceof Domino)) { return false; } // 3. 將傳入對(duì)象向下轉(zhuǎn)型 final Domino other = (Domino) obj; // 4. 比較關(guān)鍵屬性(top和bottom) if (this.getTop() != other.getTop()) { return false; } if (this.getBottom() != other.getBottom()) { return false; } return true; // 所有屬性都匹配,則認(rèn)為對(duì)象相等 } }
注意事項(xiàng):
- equals方法的實(shí)現(xiàn)遵循了標(biāo)準(zhǔn)的約定:自反性、對(duì)稱(chēng)性、傳遞性、一致性以及對(duì)null的判斷。
- hashCode方法的實(shí)現(xiàn)確保了與equals方法的一致性,即相等的對(duì)象具有相同的哈希碼。
2. 修復(fù) MemoryLane 類(lèi):更新 guess() 方法
在MemoryLane類(lèi)的guess方法中,我們需要將對(duì)象引用比較==替換為內(nèi)容比較equals(),并在匹配成功時(shí)調(diào)用Domino對(duì)象的setRevealed(true)方法。
MemoryLane 類(lèi)修改示例:
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對(duì)象的內(nèi)容 if (board[i].equals(board[k])) { // 如果匹配成功,則設(shè)置這兩個(gè)多米諾牌為已揭示 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); } // 可選:添加一個(gè)debug方法用于查看所有牌面,便于測(cè)試 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 類(lèi):
MemoryLaneDriver 類(lèi)無(wú)需任何修改,因?yàn)樗ㄟ^(guò)MemoryLane類(lèi)的公共接口進(jìn)行交互,而我們對(duì)MemoryLane和Domino類(lèi)的修改都保持了其公共接口不變(或僅在內(nèi)部邏輯上進(jìn)行了優(yōu)化)。
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()) { // 可選:在實(shí)際游戲中可以移除或注釋掉此行,它用于調(diào)試時(shí)顯示所有牌面 // 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"); } }
總結(jié)
通過(guò)以上修改,我們解決了記憶游戲中的核心邏輯問(wèn)題:
- 正確識(shí)別匹配: 重寫(xiě)Domino類(lèi)的equals方法,使得MemoryLane.guess方法能夠基于多米諾牌的實(shí)際數(shù)值(top和bottom)進(jìn)行準(zhǔn)確比較。
- 正確更新?tīng)顟B(tài): 在guess方法中,當(dāng)檢測(cè)到匹配時(shí),調(diào)用setRevealed(true)將匹配的多米諾牌狀態(tài)更新為已揭示。
- 正確顯示與游戲結(jié)束: Domino.isRevealed()現(xiàn)在能返回正確狀態(tài),進(jìn)而MemoryLane.toString()能夠正確顯示已揭示的牌面,并且MemoryLane.gameOver()能夠準(zhǔn)確判斷游戲是否所有牌都已揭示,從而使游戲能夠正常結(jié)束。
這個(gè)案例強(qiáng)調(diào)了在Java中處理自定義對(duì)象比較時(shí),正確重寫(xiě)equals()和hashCode()方法的重要性,以及理解對(duì)象狀態(tài)管理在游戲邏輯中的關(guān)鍵作用。