做完了Breakout之后,感觉做Hangman的速度明显快了不少。估计也是和用了三个类,加上stub implement有关系吧!这个程序我并没有使用太多的Decomposition,原因是整个游戏比较直观,虽然步骤多,但是一个接一个的步骤写代码基本不会错。其实写代码的风格还可以简洁一些的,这一点还要多加练习。下面贴上代码:
以下是第一部分(Hangman.java)的代码:
import acm.program.*;
import acm.util.*;
public class Hangman extends ConsoleProgram {
private static final int GUESS_NUM = 8;
public void init() {
canvas = new HangmanCanvas();
add(canvas);
}
public void run() {
/**========================================
* @author Andrew Garcia
* 1、从列表中随机选择一个单词
* 2、让用户输入猜测的字母并进行跟踪
* 3、根据游戏规则进行游戏
* ========================================*/
setFont("serif-20");
canvas.reset();
//以随机方式选取一个单词
int x = rgen.nextInt(0,hl.getWordCount());
ans = hl.getWord(x);
//提示用户单词的形式,并进行猜测
hintStr = "";
for (int i=0;i<ans.length();i++){
hintStr += "-";
}
println("The word now looks like this: "+hintStr);
//猜多个字母
for (int i=0;i<GUESS_NUM;i++){
if ((GUESS_NUM-i)!=1){
println("You have " + (GUESS_NUM-i) + " guesses left.");
} else println("You have only one guess left.");
//检查并反馈猜测的结果
/********************************************************/
while (true){
guess();
canvas.displayWord(check); //输出图像的单词形式
if (done){
println("That guess is correct!");
check(); //检查是否已经猜到完整的单词
if (!flag){
println("The word now looks like: "+check);
if ((GUESS_NUM-i)!=1){
println("You have " + (GUESS_NUM-i) + " guesses left.");
} else println("You have only one guess left.");
} else break;
} else {
println("There are no "+ guess +"'s in the word.");
println("The word now looks like: "+check);
canvas.noteIncorrectGuess(guess.charAt(0));
}
if (done==false) break;
}
/***********************************************************/
if (flag) break;
}
/*=========End: For Block=====================================================*/
if (!flag) {
println("You lose.");
println("The word is "+ans);
}
}
private void check() {
if (check.equals(ans)){
println("You guessed the word: "+ans);
println("You win!");
flag = true;
}
}
private void guess(){
//追踪猜测的字符
guess = readLine("Your guess is: ");
while (guess.length()!= 1 || Character.isLetter(guess.charAt(0))!= true){
println("Illegal guess!");
guess = readLine("Your guess is: ");
}
guess = guess.toUpperCase();
//检查并反馈猜测的结果
check = hintStr;
done = false;
char ch = guess.charAt(0);
for (int j=0;j<ans.length();j++){
if (ch == ans.charAt(j)){
check = hintStr.substring(0,j) + ch;
if (j != ans.length()-1){
check = check + hintStr.substring(j+1);
}
hintStr = check;
done = true;
}
}
}
private HangmanLexicon hl = new HangmanLexicon();
private RandomGenerator rgen = new RandomGenerator();
private HangmanCanvas canvas;
private String ans;
private String guess;
private String hintStr;
private boolean done;
private String check;
private boolean flag;
}
以下是第二部分(HangmanCanvas.java)的代码:
import acm.graphics.*;
public class HangmanCanvas extends GCanvas {
/** Resets the display so that only the scaffold appears */
public void reset() {
removeAll();
x_beam = getWidth()/2 - BEAM_LENGTH;
y_beam = 5;
GLine beam = new GLine(x_beam,y_beam,getWidth()/2,y_beam);
GLine scaffold = new GLine(x_beam,y_beam,x_beam,y_beam+SCAFFOLD_HEIGHT);
GLine rope = new GLine(getWidth()/2,y_beam,getWidth()/2,y_beam+ROPE_LENGTH);
add(beam);
add(scaffold);
add(rope);
}
/**
* Updates the word on the screen to correspond to the current
* state of the game. The argument string shows what letters have
* been guessed so far; unguessed letters are indicated by hyphens.
*/
public void displayWord(String word) {
if (label == null){
label = new GLabel(word);
label.setFont("serif-24");
add(label,x_beam,y_beam+SCAFFOLD_HEIGHT+label.getAscent()+5);
} else {
remove(label);
label = new GLabel(word);
label.setFont("serif-24");
add(label,x_beam,y_beam+SCAFFOLD_HEIGHT+label.getAscent()+5);
}
}
/**
* Updates the display to correspond to an incorrect guess by the
* user. Calling this method causes the next body part to appear
* on the scaffold and adds the letter to the list of incorrect
* guesses that appears at the bottom of the window.
*/
public void noteIncorrectGuess(char letter) {
str += letter;
int x1 = getWidth()/2 - UPPER_ARM_LENGTH;
int x2 = getWidth()/2;
int x3 = getWidth()/2 + UPPER_ARM_LENGTH;
int x4 = getWidth()/2 - HIP_WIDTH;
int x5 = getWidth()/2 + HIP_WIDTH;
int x6 = x4 - FOOT_LENGTH;
int x7 = x5 + FOOT_LENGTH;
int y1 = y_beam+ROPE_LENGTH+2*HEAD_RADIUS+ARM_OFFSET_FROM_HEAD;
int y2 = y1 + LOWER_ARM_LENGTH;
int y3 = y_beam+ROPE_LENGTH+2*HEAD_RADIUS+BODY_LENGTH;
int y4 = y3 + LEG_LENGTH;
switch (str.length()){
case 1:
GOval head = new GOval(2*HEAD_RADIUS,2*HEAD_RADIUS);
add(head,getWidth()/2-HEAD_RADIUS,y_beam+ROPE_LENGTH);
break;
case 2:
GLine body = new GLine(getWidth()/2,y_beam+ROPE_LENGTH+2*HEAD_RADIUS,
getWidth()/2,y_beam+ROPE_LENGTH+2*HEAD_RADIUS+BODY_LENGTH);
add(body);
break;
case 3:
GLine leftArm1 = new GLine(x1,y1,x2,y1);
GLine leftArm2 = new GLine(x1,y1,x1,y2);
add(leftArm1);
add(leftArm2);
break;
case 4:
GLine rightArm1 = new GLine(x2,y1,x3,y1);
GLine rightArm2 = new GLine(x3,y1,x3,y2);
add(rightArm1);
add(rightArm2);
break;
case 5:
GLine leftLeg1 = new GLine(x4,y3,x2,y3);
GLine leftLeg2 = new GLine(x4,y3,x4,y4);
add(leftLeg1);
add(leftLeg2);
break;
case 6:
GLine rightLeg1 = new GLine(x5,y3,x2,y3);
GLine rightLeg2 = new GLine(x5,y3,x5,y4);
add(rightLeg1);
add(rightLeg2);
break;
case 7:
GLine leftFoot = new GLine(x4,y4,x6,y4);
add(leftFoot);
break;
case 8:
GLine rightFoot = new GLine(x5,y4,x7,y4);
add(rightFoot);
break;
default:
GLabel error = new GLabel("ERROR!");
add(error,(getWidth()-error.getWidth())/2, (getHeight()-error.getAscent())/2);
}
GLabel err = new GLabel(str);
add(err,x_beam,y_beam+SCAFFOLD_HEIGHT+label.getAscent()+err.getAscent()+ 10);
}
/* Constants for the simple version of the picture (in pixels) */
private static final int SCAFFOLD_HEIGHT = 360;
private static final int BEAM_LENGTH = 144;
private static final int ROPE_LENGTH = 18;
private static final int HEAD_RADIUS = 36;
private static final int BODY_LENGTH = 144;
private static final int ARM_OFFSET_FROM_HEAD = 28;
private static final int UPPER_ARM_LENGTH = 72;
private static final int LOWER_ARM_LENGTH = 44;
private static final int HIP_WIDTH = 36;
private static final int LEG_LENGTH = 108;
private static final int FOOT_LENGTH = 28;
/*Instance variables*/
private int x_beam;
private int y_beam;
private GLabel label;
private String str = "";
}
以下是第三部分(HangmanLexicon.java)的代码:
import acm.util.*;
import java.io.*;
import java.util.*;
public class HangmanLexicon {
/**Stores the wordlist.*/
ArrayList<String> strList = new ArrayList<String>();
BufferedReader rd = OpenFile();
/** Returns the number of words in the lexicon. */
public int getWordCount() {
BufferedReader rd = OpenFile();
try{
while (true){
String line = rd.readLine();
if (line == null) break;
strList.add(line);
}
rd.close();
} catch (IOException ex){
throw new ErrorException(ex);
}
return strList.size();
}
/** Returns the word at the specified index. */
public String getWord(int index) {
return strList.get(index);
}
/**Retunes the BufferedReader pointing to HangmanLexicon.txt.*/
private BufferedReader OpenFile(){
BufferedReader rd = null;
try{
String name = "HangmanLexicon.txt";
rd = new BufferedReader(new FileReader(name));
} catch (IOException ex){
throw new ErrorException(ex);
}
return rd;
}
}
另外上传一张游戏的效果图做为结尾: