Computer Science 225
Advanced Programming

Spring 2017, Siena College

WordSearch BlueJ Project

Click here to download a BlueJ project for WordSearch.


WordSearch Source Code

The Java source code for WordSearch is below. Click on a file name to download it.


WordSearch.java

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
import javax.swing.JApplet;

/**
 * A private class to encapsulate a row and column, allowing
 * them to be stored in data structures.
 * 
 * @author Jim Teresco
 * 
 */
class RowCol {

    /** the row */
    protected int row;
    /** the column */
    protected int col;

    /**
     * Construct a new RowCol with the given row and column
     * 
     * @param r the row
     * @param c the column
     */
    public RowCol(int r, int c) {

        row = r;
        col = c;
    }

    /**
     * Determine if two RowCol objects are equal, which is the case only if they
     * have the same row and column.
     * 
     * @param o the other RowCol object
     */
    @Override
    public boolean equals(Object o) {

        RowCol other = (RowCol)o;
        return row == other.row && col == other.col;
    }
}

/**
 * Example WordSearch: word search game developed during class.
 * 
 * The game reads in two required files: one that has the configuration of
 * the letters on the board (GRID_SIZE lines each of which is a string
 * of GRID_SIZE characters), and a list of words contained within that
 * board, oriented horizontally, vertically, or diagonally.
 *
 * @author Jim Teresco and the Siena College
 * Computer Science 225 10:20 class, Spring 2017
 *
 */

public class WordSearch extends JApplet {

    /** named constant for the size of the square grid of letters */
    private static final int GRID_SIZE = 20;

    /** named constant for the size of the graphics square containing each letter */
    private static final int LETTER_SIZE = 20;

    /** named constant for our font size */
    private static final int FONT_SIZE = LETTER_SIZE / 2;

    /** the grid of letters in which we search */
    private String grid[][] = new String[GRID_SIZE][GRID_SIZE];
    private ArrayList<String> wordList = new ArrayList<String>();

    /** status bar text */
    private String status = "";

    /** did we just do a first click in an iteration? */
    private boolean firstClickHappened;

    /** where was first click? */
    private int click1Row, click1Col;

    /** collection of cells for the most recent guessed word */
    private ArrayList<RowCol> cells = new ArrayList<RowCol>();
    
    /** was that last guess correct? */
    private boolean lastGuessCorrect;

    /** collection of cells for all words already guessed correctly */
    private ArrayList<RowCol> foundWords = new ArrayList<RowCol>();

    /**
     * Initialize the applet
     */
    @Override
    public void init() {

        // read in the grid of letters from a file "input.txt"
        // and into our grid array
        try (Scanner inFile = new Scanner(new File("input.txt"))) {
            for (int l = 0; l < GRID_SIZE; l++) {
                String line = inFile.next();
                if (line.length() != GRID_SIZE) {
                    System.err.println("input.txt line " + l + " has " + line.length()
                        + " letters, expected " + GRID_SIZE);
                    System.exit(1);
                }
                for (int charNum = 0; charNum < GRID_SIZE; charNum++) {
                    grid[l][charNum] = line.substring(charNum, charNum+1);
                }
            }

            inFile.close();
        }
        catch (FileNotFoundException e) {
            System.err.println("input.txt not found.");
            System.exit(1);
        }

        try (Scanner wordFile = new Scanner(new File("words.txt"))) {
            while (wordFile.hasNext()) {
                wordList.add(wordFile.next());
            }

            wordFile.close();
        }
        catch (FileNotFoundException e) {
            System.err.println("words.txt not found");
            System.exit(1);
        }

        // We will add our mouse listener as an anonymous inner class.
        // The MouseAdapter is an abstract class that provides default (empty)
        // implementations of the methods in MouseListener, MouseMotionListener,
        // and MouseWheelListener, allowing us to override only those we
        // actually need.
        // In this case, we could not "extend MouseAdapter" with the entire class,
        // since it already extends JApplet and Java does not allow for 
        // multiple inheritance.  We could instead implement MouseListener, write
        // our mouseClicked method as a method of WordSearch, but then we'd need
        // to provide our own empty implementations of the other required methods
        // of MouseLister.
        addMouseListener( new MouseAdapter() 
            {
                /**
                 * This mouseClicked method handles the main gameplay: a first click to
                 * select the first letter of a guess, and a second click to select the
                 * last letter of a guess, at which point the guess is checked against
                 * the word list.
                 * 
                 * @param e mouse event information
                 */
                @Override
                public void mouseClicked(MouseEvent e) {

                    // first make sure the game is not over, determined by an empty word list
                    if (wordList.isEmpty()) return;

                    int x = e.getX();
                    int y = e.getY();

                    // make sure we clicked on the grid
                    if (x < 0 || x > (LETTER_SIZE * GRID_SIZE) ||
                    y < 0 || y > (LETTER_SIZE * GRID_SIZE)) {
                        status = "Please click on a grid cell!";
                        repaint();
                        return;                        
                    }

                    // convert x and y coordinates to the row and column in the grid
                    int row = y / LETTER_SIZE;
                    int col = x / LETTER_SIZE;

                    // how we handle the click depends on the context
                    if (firstClickHappened) {
                        // this means we already did the first click, so process the second click
                        // which will be on a cell where we want to select the last letter of the
                        // guessed word

                        // for next time...
                        firstClickHappened = false;

                        // our procedure here is to build up a guess in the string guess, and
                        // a list of positions occupied by those in the cells ArrayList
                        String guess = "";
                        cells.clear();

                        // there are several cases covering the horizontal, vertical, and diagonal
                        // there are also likely more elegant ways of doing all these checks

                        // horizontal
                        if (row == click1Row) {
                            // forward (left to right)
                            if (click1Col < col) {
                                for (int colPos = click1Col; colPos <= col; colPos++) {
                                    guess += grid[row][colPos];
                                    cells.add(new RowCol(row, colPos));
                                }
                            }   
                            // backward (right to left)
                            else {
                                for (int colPos = click1Col; colPos >= col; colPos--) {
                                    guess += grid[row][colPos];
                                    cells.add(new RowCol(row, colPos));
                                }
                            }   
                        }
                        // vertical
                        if (col == click1Col) {
                            // down
                            if (click1Row < row) {
                                for (int rowPos = click1Row; rowPos <= row; rowPos++) {
                                    guess += grid[rowPos][col];
                                    cells.add(new RowCol(rowPos, col));
                                }
                            }   
                            // up
                            else {
                                for (int rowPos = click1Row; rowPos >= row; rowPos--) {
                                    guess += grid[rowPos][col];
                                    cells.add(new RowCol(rowPos, col));
                                }
                            }   
                        }

                        // off diagonal: \
                        if ((col - click1Col) == (row - click1Row)) {
                            // up and left
                            if (col < click1Col) {
                                for (int offset = 0; offset <= click1Col - col; offset++) {
                                    guess += grid[click1Row - offset][click1Col - offset];
                                    cells.add(new RowCol(click1Row - offset, click1Col - offset));
                                }
                            }
                            // down and right
                            else {
                                for (int offset = 0; offset <= col - click1Col; offset++) {
                                    guess += grid[click1Row + offset][click1Col + offset];
                                    cells.add(new RowCol(click1Row + offset, click1Col + offset));
                                }                         
                            }
                        }
                        // main diagonal: /
                        if ((col - click1Col) == (click1Row - row)) {
                            // down and left
                            if (col < click1Col) {
                                for (int offset = 0; offset <= click1Col - col; offset++) {
                                    guess += grid[click1Row + offset][click1Col - offset];
                                    cells.add(new RowCol(click1Row + offset, click1Col - offset));
                                }
                            }
                            // up and right
                            else {
                                for (int offset = 0; offset <= col - click1Col; offset++) {
                                    guess += grid[click1Row - offset][click1Col + offset];
                                    cells.add(new RowCol(click1Row - offset, click1Col + offset));
                                }                         
                            }
                        }

                        if (guess.equals("")) {
                            status = "Choose a position in the same row, column, or diagonal.";
                            firstClickHappened = true;
                        }
                        else {
                            status = "You guessed: " + guess;
                            // did we find it?
                            if (wordList.contains(guess)) {
                                status += " and it's a match!";
                                foundWords.addAll(cells);
                                wordList.remove(guess);
                                lastGuessCorrect = true;
                            }
                            else {
                                status += " which is not one of the words...";
                                lastGuessCorrect = false;
                            }
                        }
                    }
                    else {
                        firstClickHappened = true;
                        click1Row = row;
                        click1Col = col;
                        cells.clear();
                        status = "Choose the position of the last letter of your guess.";
                    }
                    repaint();
                }
            });

        firstClickHappened = false;
    }

    /**
     * repaint the graphics area for our applet
     * 
     * @param g the Graphics object on which we will draw
     */
    @Override
    public void paint(Graphics g) {

        // "erase" anything that's on the graphics area from earlier
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, getWidth(), getHeight());

        // draw grid of letters
        g.setColor(Color.BLACK);
        Font font = new Font("UTF-8", Font.PLAIN, FONT_SIZE);
        for (int row = 0; row < GRID_SIZE; row++) {
            for (int col = 0; col < GRID_SIZE; col++) {

                // highlight grid cells for previously found words
                if (foundWords.contains(new RowCol(row, col))) {
                    g.setColor(Color.YELLOW);
                    g.fillRect(LETTER_SIZE * col, LETTER_SIZE * row, LETTER_SIZE, LETTER_SIZE);
                    g.setColor(Color.BLACK);
                }

                // highlight grid cells for most recent selected word, green if correct, red if not
                if (cells.contains(new RowCol(row, col))) {
                    if (lastGuessCorrect) {
                        g.setColor(Color.GREEN);
                    }
                    else {
                        g.setColor(Color.RED);
                    }
                    g.fillRect(LETTER_SIZE * col, LETTER_SIZE * row, LETTER_SIZE, LETTER_SIZE);
                    g.setColor(Color.BLACK);
                }

                // highlight grid cell for first click
                if (firstClickHappened && (row == click1Row) && (col == click1Col)) {
                    Color pink = new Color(255, 128, 128);
                    g.setColor(pink);
                    g.fillRect(LETTER_SIZE * col, LETTER_SIZE * row, LETTER_SIZE, LETTER_SIZE);
                    g.setColor(Color.BLACK);
                }

                g.drawRect(LETTER_SIZE * col, LETTER_SIZE * row, LETTER_SIZE, LETTER_SIZE);
                g.drawString(grid[row][col], 
                    LETTER_SIZE * col + (LETTER_SIZE - FONT_SIZE) / 2, 
                    LETTER_SIZE * row + LETTER_SIZE - FONT_SIZE / 2);
            }
        }

        // draw words to search
        int startY = LETTER_SIZE * GRID_SIZE + FONT_SIZE;
        for (String word : wordList) {
            g.drawString(word, 0, startY);
            startY += FONT_SIZE * 3 / 2;
        }

        // draw our current game status
        g.drawString(status, 0, getHeight() - 2*FONT_SIZE);

        // if the game is over (empty word list), put that message up
        if (wordList.isEmpty()) {
            g.setColor(Color.BLUE);
            g.drawString("You found all the words!  Game over.", 0, startY);
        }
    }
}