[end] CS61B Java 03: Proj 0

文章介绍了在IntelliJ中设置项目SDK和语言级别的过程,并详细讲解了开发字母猜测游戏的算法,包括基于频率的猜字策略和考虑模式的猜字策略。此外,还讨论了如何编写和验证辅助方法,以及实现随机选择单词的游戏策略,同时涵盖了单元测试和集成测试的实践应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Assignment workflow guide

Opening in IntelliJ

  • Navigate to the “File -> Project Structure” menu, and make sure you are in the Project tab. Set your project SDK to your installed Java version. 设置你的项目SDK为你下载的Java版本。 If 17 or higher isn’s available in the dropdown, make sure you sownloaded and installed Java completely.
  • Set the Project Language Level to “17 - Sealed types, always-strict floating-point semantics”.
    在这里插入图片描述
  • Still in Project Structure, go to the Libraties tab. Click the “**+ -> Java” button, then navigate to library-sp23, select the folder, and click “Ok”. 导入本课程的辅助项目库
    在这里插入图片描述

Guessers

NaiveLetterFreqGuesser

Task: Fill in the functions: getFrequencyMap and getGuess.
1 getFrequencyMap:得出单词列表涉及的所有字符的出现频率
2 getGuess.:根据字符出现频率和已猜测字符列表guesses得出下一个最可能出现的字符

PatternAwareLetterFreqGuesser

1 根据猜测的字符得出最新的单词模式“pattern”
2 根据模式得出符合该模式的单词列表 ※
3 根据单词列表得出字符出新的频率字典 ※
4 根据字符出现频率得出下一个最可能出现的字符
We know:
1 Fill in the getGuess(String pattern, List<Character> guesses) method. 根据模式和已猜测字符列表得出下一个最可能出现的字符
2 Write helper methods. For example, getFreqMapThatMatchesPattern which returns a frequency map, 辅助方法:根据符单词列表得出新的字符出现频率字典。 keepOnlyWordsThatMatchPattern that takes as input a list of words and a pattern, and only returns words that match. 辅助方法:根据模式和单词列表得出符合模式的新的单词列表
3 Write code to verify the helper methods work. We can do this by adding function calls to our main, e.g. System.out.println(getFreqMapThatMatchesPattern(pattern)) or System.out.println(keepOnlyWordsThatMatchPattern(pattern). 通过打印验证辅助方法。
Then,
1 The getGuess(String pattern, List<Character> guesses) method takes as input a list of words were guess and a pattern, and returns the next character.
2.1 keepOnlyWordsThatMatchPattern(String pattern, List<string>) returns a list of words that match.
2.2 getFreqMapThatMatchesPattern()returns a frequency map, which like NaiveLetterFreqGuesser()that takes as input a list of words that match pattern and return the frequency character.

Q:如果字符串中每个字符都满足一定的条件,则语句

    if (pattern == "----") { 
        map = getFreqMapThatMatchesPattern(words);
    }
    else {
        List <String> list = KeepOnlyWordsThatMatchPattern(pattern, words);
        map = getFreqMapThatMatchesPattern(list);
    }

PAGALetterFreqGuesser

PAGA stands for “Pattern And Guesses Aware”.
1 根据猜测的字符得出最新的单词模式“pattern”
2 根据模式得出符合该模式的单词列表
3 若猜测的字符未出现在模式中,单词列表需去除包含该猜测字符的单词,进而得出新的单词列表 ※
4 根据单词列表得出字符出新的频率字典
5 根据字符出现频率得出下一个最可能出现的字符
Task: Implement the methods in PAGALetterFreqGuesser, including any helper methods. 完成PAGALetterGuesser方法,包括辅助方法。Don’t add any additional instance variables. 不要添加任何实例变量。
Verify Your Code: Pass all the tests in the two test class.
0 Run PGALetterFreq...main()
Pattern: “----”; Guess List: [‘e’]; Expected: ‘o’
创建一个辅助方法,根据符合模式的单词列表、模式、已猜测字符,得出既符合模式又排除了包含未出现在模式中字符的单词列表中,进而得出最可能出现的字母。
1 PAGALetterFreqGuesserTest
(1)testGetGuessBlankThen_o__Pttern()
3) Pattern: “-oo-”; Guess List: [‘e’, ‘o’]; Expected: ‘c’
If the freqiency map is {a=3, b=2, d=2, e=7, l=6, o=5} and guesses is[‘e’,‘l’], then the next guess should be ‘o’. guess不是简单取字符出现频率列表中最高的字符,而是列表中除了已猜测字符之外的最高频率的字符。
(2)testGetGuess_o__Pattern
Pattern: “-o_-”; Guess List: [‘o’]; Expected: ‘e’
Warning: Another implication of taking guesses into account can be seen in the pattern be-. This pattern does not match bee, because all the e’s hae been revealed.
符合模式的列表方法,需要去除已猜测字符未完全体现的单词
Note: The easiest way to resolve ties is by using a TreeMap instead of a HashMap, sin a TreeMap stores its keys in sorted order.
当字符出现频次一样时,按字母表顺序取值

Q:List和Map如何进行深拷贝

目前是新建,遍历原数据,添加至新建的对象

Quick Note on Code Reuse

Avoid redundancy by putting commom helper methods into a single helper class with a suitable. 将共同的辅助方法放在一个class中避免冗余
Note that in this case, all such methods should be static (since the `LFGHelper`` class is never instantiated and carries no data of its own).注意本例中,所有方法都应是静态的,因为辅助类不会实例化或懈怠任何数据)

Q(已知待实现):创建HelperMethodClass

使用PALALetter…中的辅助方法

Choosers

RandomChoosers

We know:
Implement RandomChooser:
1 The RandomChosser select a word in its constructor
2 The guesser piks a character using makeGuess('o') function
3 The next call getPattern returns the pattern

RandomChooser Constructors and Methods

1 方法 RandomChooser()

Q:如何通过根据特定条件throw抛出特定异常描述?

如何通过文件名字符串读取文件内容?
Here, words should be a list of words of the desired length in sorted order, i.e. the output of calling readWordOfLength from FileUtils.java in utils.

Q(待修正):如何调用另一个java文件中的方法?

1)类中包含以类名命名的属性,先实例化类,再调用
2)类中只包含方法,直接通过类名.方法调用;或import类,直接使用方法

// 《FileUtils.java》
public class FileUtils {
    public static List<String> readWords(String filename) {...} // 
    public static List<String> readWordsOfLength(String filename, int length) {...}
}

// 《PAGALetterFreqGuesser.java》
public class PAGALetterFreqGuesser implements Guesser {
    ...
    public PAGALetterFreqGuesser(String dictionary) {words = FileUtils.readWords(dictionaryFile); }; // 直接调用
    ...
    public char getGuess(String pattern, List<Character guesses) {...} /
    ...
}
public static void main(String[] args) {
    PAGALetterFreqGuesser pagalfg = new PAGALetterFreqGuesser("data/example.txt"); // 先实例化
    System.out.println(pagalfg.getGuess("----", List.of('e'))); // 然后调用
}

2 方法 makeGuess()
遍历字符串中的每个字符
为字符串添加字符
3 方法 getPattern()
一个方法可以调用另一个中的参数吗?不可以,方法内的参数是局部变量,不能被方法本身之外调用
4 方法 getWord()
返回秘密单词

Testing RandomChooser

RandomChooserTest.java
testCorrectGusses()

测试:pattern能正确显示猜测到的第一个字符

public void testCorrectGuesses() {
	...
assertThat(pattern.charAt(i)).isEqualTo(firstGuess);
	...
}

value of: charAt(…)
expected: h
but was : -

问题:getpattern()无参方法中如何调用其他参数传入的方法
解答:设置成员变量,在传参方法中将局部变量的值传入成员变量,在无参方法中使用成员变量

xxxx(){ // 类
	private char letter; // 定义成员变量

   	xxxx(letter){ // 传参方法
		this.letter = letter; // 将传递的参数赋值给成员变量
	}

	xxxx(){ // 无参方法
		letter; //访问成员变量
	}
}
testReturnedOccurrences()

测试:pattern能基于已有pattern更新

value of: getPattern()
expected: -ea-
but was : --a-

testRCLargeLength()

测试:如果给到的单词长度值非常大,期望某种报错类型

@Order(7)
@DisplayName("RandomChooser throws exception for max int word length")
@Test
@Timeout(1)
public void testRCLargeLength() {
    assertThrows(IllegalStateException.class, () -> new RandomChooser(Integer.MAX_VALUE, DICTIONARY_FILE));
}

org.opentest4j.AssertionFailedError: Unexpected exception type thrown,
Expected :class java.lang.IllegalStateException 期望非法状态异常
Actual :class java.lang.IllegalArgumentException 代码触发非法参数异常
at aoa.choosers.RandomChooserTest.testRCLargeLength(RandomChooserTest.java:128) // 定位test报错代码行
Caused by: java.lang.IllegalArgumentException: argument must be positive: 0 // 定位报错原因:参数必须大于0
at aoa.choosers.RandomChooser.(RandomChooser.java:26) //定位代码报错行
打断点

Press Ctrl+Button1 on uniform --> External Libraries > library-sp23 > algs4.jar >


public static int uniform(int n) {
        if (n <= 0) throw new IllegalArgumentException("argument must be positive: " + n);
        return random.nextInt(n);
}

错误的思路:根据未通过testcase的报错提示,错误类型的异常抛出,超导抛出异常的地方,是某方法,该方法的代码中对应做了异常抛出的设计。误以为应当找个抛正确类型异常的方法,或修改该方法抛出的异常类型。
正确的思路:实际只需要参照方法中设计的异常抛出代码,对构造方法的参数、其中定义的参数,做异常抛出代码设计即可。即该testcase是为了验证该构造方法正确处理了异常。

RandomChooserIntegrationTest.java

testPlayGameWithRandomChooser()
debug

java.util.NoSuchElementException // 没找到类
at java.base/java.util.Scanner.throwFor(Scanner.java:937) // Scan没读取到信息
at aoa.choosers.RandomChooserIntegrationTest.testPlayGameWithRandomChooser(RandomChooserIntegrationTest.java:91) // 代码报错行,进入代码runTestGame查看

解决:根据choseword的长度在构造函数中得出pattern的初始值

private String pattern = "-";

public RandomChooser(int wordLength, String dictionaryFile) { 
	...
	for (int i = 1; i < chosenWord.length(); i ++) {
    	pattern = pattern + "-";
        }
    }

RandomChooserTest问题:

java.util.concurrent.TimeoutException: testRCLargeLength() timed out after 1 second

失败case

public void testRCLargeLength() {
        assertThrows(IllegalStateException.class, () -> new RandomChooser(Integer.MAX_VALUE, DICTIONARY_FILE));
    }

解决:RandomChooser构造函数中加入异常处理

if (wordLength == Integer.MAX_VALUE) throw new IllegalStateException();

testPlayGameWithRandomChooser() pass

Q:第几次猜测guesses计数好像有些问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值