Stanford CS106A Hangman Solution

17 Nov

Ok, so I put up the different parts of the Hangman Game Assignment Solution in the previous posts, so I thought I’d put them all together here. You can also see the solutions on github. If you’re interested in part-by-part solution, check out these posts:


The main Hangman Program is as follows:

/*
 * File: Hangman.java
 * ------------------
 * This program will eventually play the Hangman game from
 * Assignment #4.
 */

import acm.graphics.*;
import acm.program.*;
import acm.util.*;

import java.awt.*;

public class Hangman extends ConsoleProgram {

	private HangmanLexicon hangmanWords;

	private HangmanCanvas canvas;

	private RandomGenerator rgen = RandomGenerator.getInstance();

	/** Tracks the number of guesses the player has */
	private int guessCounter = 8;

    public void run() {
    	setUpGame();
    	playGame();
	}

  //This is the word being guessed
	private String hiddenWord;

  //This is the secret word
	private String word = pickWord();

	//This is the latest character entered by the user
	private char ch;

	//This string keeps track of all the incorrect guessed letters
	private String incorrectLetters = "";

    /*Set up the game by displaying the welcome message,
     *how many letters there are in the word,
     *and how many guesses the user has
     */
    private void setUpGame() {
    	canvas.reset();
    	hiddenWord = showNumberOfLetters();
    	canvas.displayWord(hiddenWord);
    	println("Welcome to Hangman!");
    	println("The word now looks like this: " + hiddenWord);
    	println("You have " + guessCounter + " guesses left.");
    }

    //Generates a random word selected from the HangmanLexicon
    private String pickWord() {
    	hangmanWords = new HangmanLexicon();
    	int randomWord = rgen.nextInt(0, (hangmanWords.getWordCount() - 1));
    	String pickedWord = hangmanWords.getWord(randomWord);
    	return pickedWord;
    }

    //Shows how many letters there are in the word as part of game setup
	private String showNumberOfLetters() {
		String result = "";
		for(int i = 0; i< word.length(); i++) {
			result = result + "-";
		}
		return result;
		}

	private void playGame() {
		while(guessCounter > 0) {
			String getChar = readLine("Your guess: ");
			while (true) {
				if(getChar.length() > 1) {
					getChar = readLine("You can only guess one letter at a time. Try again: ");
				}
				if(getChar.length() == 1) break;
			}
			ch = getChar.charAt(0);
			if(Character.isLowerCase(ch)) {
				ch = Character.toUpperCase(ch);
			}
			letterCheck();
			if (hiddenWord.equals(word)) {
				println("You guessed the word: " + word);
				println("You win");
				break;
			}
			println("The word now looks like this: " + hiddenWord);
			println("You have " + guessCounter + " guesses left.");

		}
		if (guessCounter == 0) {
			println("You're completely hung.");
			println("The word was:" + word);
			println("You lose.");
		}
	}

	//updates the hiddenWord if the character entered is correct
	private void letterCheck() {
		//checks to see if the guessed letter is in the word
		if(word.indexOf(ch) == -1) {
			println("There are no " + ch + "'s in the word");
			guessCounter--;
			incorrectLetters = incorrectLetters + ch;
			canvas.noteIncorrectGuess(incorrectLetters);
		}
		if(word.indexOf(ch) != -1) {
			println("The guess is correct.");
		}
		//goes through each of the letters in the word and checks if it matches the guessed letter,
		//if it's a match, updates the hidden word to reveal the position of the guessed letter
		for(int i = 0; i < word.length(); i++) {
			if (ch == word.charAt(i)) {
				if (i > 0) {
					hiddenWord = hiddenWord.substring(0, i) + ch + hiddenWord.substring(i + 1);
				}
				if(i == 0) {
					hiddenWord = ch + hiddenWord.substring(1);
				}
				canvas.displayWord(hiddenWord);
			}
		}
	}

	public void init() {
		canvas = new HangmanCanvas();
		add(canvas);
		}
}

The solution to the Hangman Canvas part of the problem is as follows:

/*
 * File: HangmanCanvas.java
 * ------------------------
 * This file keeps track of the Hangman display.
 */

import acm.graphics.*;

public class HangmanCanvas extends GCanvas {

/** Resets the display so that only the scaffold appears */
	public void reset() {
		drawScaffold();
	}

	private void drawScaffold() {
		double scaffoldTopX = getWidth()/2 - UPPER_ARM_LENGTH;
		double scaffoldTopY = getHeight()/2 - BODY_LENGTH - HEAD_RADIUS*2 - ROPE_LENGTH;
		double scaffoldBottomY = scaffoldTopY + SCAFFOLD_HEIGHT;
		GLine scaffold= new GLine (scaffoldTopX, scaffoldTopY, scaffoldTopX, scaffoldBottomY);
		add(scaffold);
		double beamRightX = scaffoldTopX + BEAM_LENGTH;
		GLine beam = new GLine(scaffoldTopX, scaffoldTopY, beamRightX, scaffoldTopY);
		add(beam);
		double ropeBottomY = scaffoldTopY + ROPE_LENGTH;
		GLine rope = new GLine (beamRightX, scaffoldTopY, beamRightX, ropeBottomY);
		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) {
		//adds the label with the correctly guessed letters
		double x = getWidth()/4;
		double y = getHeight() - HEAD_RADIUS*2;
		GLabel unGuessedWord = new GLabel(word, x, y);
		unGuessedWord.setFont("Halvetica-24");
		//removes the latest hidden word and replaces it
		//with the newest one with the new updated guessed letter
		if (getElementAt(x,y) != null){
			remove(getElementAt(x,y));
		}
		add(unGuessedWord);

	}

/**
 * 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(String incorrectGuesses) {
		//adds the label with the incorrect letters
		double x = getWidth()/4;
		double y = getHeight() - HEAD_RADIUS;
		GLabel incorrectLetters = new GLabel(incorrectGuesses, x, y);
		//checks to see if there is already a list of incorrect letters in place,
		//and removes them before adding the new string, with the latest letter
		if(getElementAt(x,y) != null) {
			remove(getElementAt(x,y));
		}
		add(incorrectLetters);
		//checks how many incorrect guessed letters there are
		//and draws the next appropriate body part of the hangman
		if(incorrectGuesses.length() == 1) {
			drawHead();
		}
		if(incorrectGuesses.length() == 2) {
			drawBody();
		}
		if(incorrectGuesses.length() == 3) {
			drawLeftArm();
		}
		if(incorrectGuesses.length() == 4) {
			drawRightArm();
		}
		if(incorrectGuesses.length() == 5) {
			drawLeftLeg();
		}
		if(incorrectGuesses.length() == 6) {
			drawRightLeg();
		}
		if(incorrectGuesses.length() == 7) {
			drawLeftFoot();
		}
		if(incorrectGuesses.length() == 8) {
			drawRightFoot();
		}
	}

	private void drawHead() {
		double x = getWidth()/2 - UPPER_ARM_LENGTH + BEAM_LENGTH - HEAD_RADIUS;
		double y = getHeight()/2 - BODY_LENGTH - HEAD_RADIUS*2;
		GOval head = new GOval (x, y, HEAD_RADIUS*2, HEAD_RADIUS*2);
		add(head);
	}

	private void drawBody() {
		double x = getWidth()/2 + UPPER_ARM_LENGTH/2 + HEAD_RADIUS;
		double topY = getHeight()/2 - BODY_LENGTH;
		double bottomY = topY + BODY_LENGTH;
		GLine body = new GLine (x, topY, x, bottomY);
		add(body);
	}

	private void drawLeftArm() {
		double armStartX = getWidth()/2 + UPPER_ARM_LENGTH/2 + HEAD_RADIUS;
		double armEndX = getWidth()/2 + UPPER_ARM_LENGTH/2 + HEAD_RADIUS - UPPER_ARM_LENGTH;
		double upperArmHeightY = getHeight()/2 - BODY_LENGTH + ARM_OFFSET_FROM_HEAD;
		GLine upperLeftArm = new GLine (armStartX, upperArmHeightY, armEndX, upperArmHeightY);
		add(upperLeftArm);
		double lowerArmEndY = upperArmHeightY + LOWER_ARM_LENGTH;
		GLine lowerLeftArm = new GLine (armEndX, upperArmHeightY, armEndX, lowerArmEndY);
		add(lowerLeftArm);
	}

	private void drawRightArm() {
		double armStartX = getWidth()/2 + UPPER_ARM_LENGTH/2 + HEAD_RADIUS;
		double armEndX = getWidth()/2 + UPPER_ARM_LENGTH/2 + HEAD_RADIUS + UPPER_ARM_LENGTH;
		double upperArmHeightY = getHeight()/2 - BODY_LENGTH + ARM_OFFSET_FROM_HEAD;
		GLine upperRightArm = new GLine (armStartX, upperArmHeightY, armEndX, upperArmHeightY);
		add(upperRightArm);
		double lowerArmEndY = upperArmHeightY + LOWER_ARM_LENGTH;
		GLine lowerRightArm = new GLine (armEndX, upperArmHeightY, armEndX, lowerArmEndY);
		add(lowerRightArm);
	}

	private void drawLeftLeg() {
		double hipStartX = getWidth()/2 + UPPER_ARM_LENGTH/2 + HEAD_RADIUS;
		double hipEndX = hipStartX - HIP_WIDTH;
		double hipHeightY = getHeight()/2;
		GLine leftHip = new GLine(hipStartX, hipHeightY, hipEndX, hipHeightY);
		add(leftHip);
		double leftLegY = hipHeightY + LEG_LENGTH;
		GLine leftLeg = new GLine(hipEndX, hipHeightY, hipEndX, leftLegY);
		add(leftLeg);

	}

	private void drawRightLeg() {
		double hipStartX = getWidth()/2 + UPPER_ARM_LENGTH/2 + HEAD_RADIUS;
		double hipEndX = hipStartX + HIP_WIDTH;
		double hipHeightY = getHeight()/2;
		GLine leftHip = new GLine(hipStartX, hipHeightY, hipEndX, hipHeightY);
		add(leftHip);
		double leftLegEndY = hipHeightY + LEG_LENGTH;
		GLine leftLeg = new GLine(hipEndX, hipHeightY, hipEndX, leftLegEndY);
		add(leftLeg);
	}

	private void drawLeftFoot() {
		double footStartX = getWidth()/2 + UPPER_ARM_LENGTH/2 + HEAD_RADIUS - HIP_WIDTH;
		double footEndX = footStartX - FOOT_LENGTH;
		double footHeightY = getHeight()/2 + LEG_LENGTH;
		GLine leftFoot = new GLine(footStartX, footHeightY, footEndX, footHeightY);
		add(leftFoot);
	}

	private void drawRightFoot() {
		double footStartX = getWidth()/2 + UPPER_ARM_LENGTH/2 + HEAD_RADIUS + HIP_WIDTH;
		double footEndX = footStartX + FOOT_LENGTH;
		double footHeightY = getHeight()/2 + LEG_LENGTH;
		GLine rightFoot = new GLine(footStartX, footHeightY, footEndX, footHeightY);
		add(rightFoot);
	}

/* 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;

}

Finally, the Hangman Lexicon solution is as follows:

/*
 * File: HangmanLexicon.java
 * -------------------------
 * This file contains a stub implementation of the HangmanLexicon
 * class that you will reimplement for Part III of the assignment.
 */

import acm.util.*;
import java.io.*;
import java.util.*;

public class HangmanLexicon {

	private ArrayList  wordList = new ArrayList  ();

	public HangmanLexicon() {
	//adds the individual words in the file to the array list
		try {
			BufferedReader hangmanWords = new BufferedReader(new FileReader("HangmanLexicon.txt"));
			while(true) {
				String line = hangmanWords.readLine();
				if(line == null) break;
				wordList.add(line);
			}
			hangmanWords.close();
		} catch (IOException ex) {
			throw new ErrorException(ex);
		}
	}

/** Returns the word at the specified index. */
	public String getWord(int index) {
		return wordList.get(index);
	}

	/** Returns the number of words in the lexicon. */
	public int getWordCount() {
		return wordList.size();
	}
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s