Fancy Joust

<< Joust | LabTrailIndex | LightsOut >>

For basic version see Joust

The working demo is here

These sound and image files need to be in your project folder: (Sometimes Eclipse will put these in a different folder. Under Properties select Java Build Path and press the Source Tab Its is easier if the output is in the same folder as all the source files.)

Attach:hit.wav Attach:buzzer.wav

Square.java

Complete the isBurned method


import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;


public class Square {
    /**
     * constants
     */
	public static final int black=0;
	public static final int white=1;
	public static final int burned=2;
	/**
	 * instance fields
	 */

	private BufferedImage[] img;
	private Color[] colors= {Color.DARK_GRAY, Color.LIGHT_GRAY, Color.RED}; 
	private int color;
	private boolean highlighted;
	private Rectangle box;
	/**
	 * constructors 
	 */
	public Square(){
		setColor(Square.white);
		img = new BufferedImage[3];
		setHighlighted(false);
		box=null;
	}
	public Square(int color) {
		this(); //use the default constructor to initialize
		this.setColor(color);
	}
	public Square(int color, Rectangle box) {
		this(); //use the default constructor to initialize
		this.setColor(color);
		this.box=box;
	}

	/**
	 * Access Methods
	 */
	public int getColor() {
		return color;
	}
	public void setColor(int color) {
		this.color = color;
	}

	public BufferedImage getImg() {
		return img[color];
	}
	public void setImgArray(BufferedImage blackImg, BufferedImage whiteImg, BufferedImage burnedImg ) {
		img[0]=blackImg;
		img[1]=whiteImg;
		img[2]=burnedImg;
	}
	/**
	 * other methods
	 */
	public void draw(Graphics g){
		if (box==null)
			return;
		int w=2;//width of outline
		int x= (int)box.getX();
		int y= (int)box.getY();
		int size = (int)box.getHeight();
		if (highlighted){
			g.setColor(Color.MAGENTA);
		}else{
			g.setColor(Color.BLACK);
		}
		g.fillRect(x, y, size, size);
		if (img[0]==null){
			g.setColor(colors[color]);
			g.fillRect(x+w, y+w, size-2*w, size-2*w);

		}else{

			g.drawImage(img[color], x+w, y+w, x+size-2*w, y+size-2*w, 0, 0, img[color].getWidth(), img[color].getHeight(), null);
		}
	}

	public boolean isHighlighted() {
		return highlighted;
	}
	public void setHighlighted(boolean highlighted) {
		this.highlighted = highlighted;
	}
	/**
	 * given screen location x,y sets the highlight on or off 
	 * @param x screen coord
	 * @param y screen coord
	 * @return the Square if highlighted, otherwise null
	 */
	public Square select(int x, int y){
		this.setHighlighted(box.contains(x,y));
		if(this.isHighlighted())
			return this;
		else
			return null;
	}
	public boolean isBurned(){
		//your code here

	}
}

Knight.java

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;

public class Knight {

	/**
	 * instance fields
	 */
	private BufferedImage img;
	private int row, col;
	private String color;
	public static final String black="\u2658"; //the unicode filled knight letter
	public static final String white="\u265e"; //the unicode open knight letter
	/**
	 * constructors
	 */
	public Knight() {
		img=null;
		color=Knight.white;
		row=0;
		col=0;
	}
	public Knight(String color){
		this(); //use default constructor to initialize fields
		this.color=color;		
	}
	public Knight(String color, int row, int col){
		this(color); //use other constructor to initialize fields
		this.row=row;
		this.col=col;
	}
	public Knight(String color, int row, int col, BufferedImage img){
		this(color, row, col);
		this.img=img;
	}
	public void draw(Graphics g, Board board){
		int size = board.getSquareSize();
		int offset = (int)(size/5.0);
		int x=board.getLeft()+col*size;
		int y=board.getTop()+(row)*size;
		if (img==null){
			Font font=new Font("Arial Unicode MS", 0, size);//Arial is the most complete for WinXP
    			g.setFont(font);
    			g.setColor(Color.YELLOW);
    		    g.drawString(this.getColor(), x, y+size-offset);  
		}else{
			int w=2;//width of outline
		    g.drawImage(img, x+w, y+w, x+size-2*w, y+size-2*w, 0, 0, img.getWidth(), img.getHeight(), null);
	}
	}
	public boolean isWhite()
	{
		return color==Knight.white;
	}
	public boolean isBlack()
	{
		return !isWhite();
	}
	public int getRow() {
		return row;
	}
	public void setRow(int row) {
		this.row = row;
	}
	public int getCol() {
		return col;
	}
	public void setCol(int col) {
		this.col = col;
	}
	public String getColor(){
		return color;
	}
	public void setColor(String color){
		this.color=color;
	}
	public void setImage(BufferedImage img){
		this.img=img;
	}


}

Board.java

complete the isLegalMove and hasMovesLeft methods

import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;

/**
 * A Joust game board... free to use for educational use
 * @author Chris Thiel, OFMCap
 * 30 Sept 2012
 *
 */
public class Board {

	private int squareSize, top,left;
	private Square[][] grid;
	private Square selectedSquare; 
	private int selectedRow, selectedCol;
	private Knight[] player;
	private Knight currentPlayer;
	private String message;


	public Board(int rows, int cols) 
	{
		this.setSquareSize(70);
		grid=new Square[rows][cols];
		setTop(20);
		setLeft(50);
		setSelectedSquare(null);
		setSelectedRow(-1);
		setSelectedCol(-1);
		setMessage("White's turn");
		for (int r=0; r<getNumRows();r++)
			for (int c=0; c< getNumCols();c++){
				int color=(r+c+1)%2;//alternate color
				int x=left + c*squareSize;
				int y=top + r*squareSize;
				Rectangle rectangle=new Rectangle(x,y,squareSize,squareSize);
				grid[r][c]=new Square(color, rectangle);
			}
		player = new Knight[2];
		int rand=(int)(getNumCols()*Math.random());
		player[0]=new Knight(Knight.white, getNumRows()-1,rand);
		rand=(int)(getNumCols()*Math.random());
		player[1]=new Knight(Knight.black,0,rand);

		setCurrentPlayer(player[0]);

	}
	public Board(int rows, int cols, BufferedImage lightKnight, BufferedImage darkKnight){
		this(rows, cols);
		player[0].setImage(lightKnight);
		player[1].setImage(darkKnight);
	}
	/**
	 * access methods
	 * 
	 */
	public int getNumRows(){
		return grid.length;
	}
	public int getNumCols(){
		return grid[0].length;
	}
	public Square getSquare(int row, int col){
		return grid[row][col];
	}
	public int getTop() {
		return top;
	}
	public void setTop(int top) {
		this.top = top;
	}
	public int getLeft() {
		return left;
	}
	public void setLeft(int left) {
		this.left = left;
	}
	public int getSquareSize() {
		return squareSize;
	}
	public void setSquareSize(int squareSize) {
		this.squareSize = squareSize;
	}
	public Knight getCurrentPlayer() {
		return currentPlayer;
	}
	/**
	 * Check if the square at r,c is burned
	 * or occupied by one of the players
	 * @param r
	 * @param c
	 * @return is the square is availible
	 */
	public boolean isAvailable(int r, int c){
		if (r<0 || c<0 || r>=getNumRows() || c>=getNumCols())
			return false;
		if (grid[r][c].isBurned())
			return false;
		for(int i=0;i<2;i++)
			if (player[i].getRow()==r && player[i].getCol()==c)
				return false;
		return true;
	}
	public void setCurrentPlayer(Knight currentPlayer) {
		this.currentPlayer = currentPlayer;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	public Square getSelectedSquare() {
		return selectedSquare;
	}
	public void setSelectedSquare(Square selectedSquare) {
		this.selectedSquare = selectedSquare;
	}
	public int getSelectedRow() {
		return selectedRow;
	}
	public void setSelectedRow(int selectedRow) {
		this.selectedRow = selectedRow;
	}
	public int getSelectedCol() {
		return selectedCol;
	}
	public void setSelectedCol(int selectedCol) {
		this.selectedCol = selectedCol;
	}
	/**
	 * other methods
	 */
	public void draw(Graphics g){
		for (int r=0; r<getNumRows();r++)
			for (int c=0; c< getNumCols();c++){
				grid[r][c].draw(g);
			}
		player[0].draw(g, this);
		player[1].draw(g, this);

	}
	public void setHighlight(int x, int y){
		for (int r=0; r<getNumRows();r++)
			for (int c=0; c< getNumCols();c++){
				Square s=grid[r][c].select(x, y);
				if (s!=null) {
					setSelectedSquare(s);
					setSelectedRow(r);
					setSelectedCol(c);
				}
			}
		message="("+selectedRow+", "+selectedCol+") is ";
		if (isLegalMove(currentPlayer, selectedRow,selectedCol))
			message+="legal";
		else 
			message+="NOT legal";
	}
	public String getSelected(){
		return "("+selectedRow+", "+selectedCol+")";
	}
	public String whoseTurn(){

		if (! gameOver() && currentPlayer.isWhite())
			return "Whites turn";
		if (! gameOver() && currentPlayer.isBlack())
			return "Black's turn";
		if (hasMovesLeft(player[0]))
			return "White wins";
		return "Black Wins";
	}
	public boolean move(){
		boolean result=false;
		if(gameOver())
			return result;
		if (isLegalMove(currentPlayer, selectedRow, selectedCol)){
			grid[currentPlayer.getRow()][currentPlayer.getCol()].setColor(Square.burned);
			currentPlayer.setRow(selectedRow);
			currentPlayer.setCol(selectedCol);	
			result=true;
			if (!gameOver() ){
				if (currentPlayer==player[0])
					currentPlayer=player[1];
				else
					currentPlayer=player[0];

			}
		}	
		message=whoseTurn();
		return result;
	}
	/**
	 * Checks if both players have any 
	 * available Moves left 
	 * @return whether the game is over
	 */
	public boolean gameOver(){
		for (int i=0;i<2;i++)
			if (!hasMovesLeft(player[i]))
				return true;
		return false;
	}
	/**
     * Using a series of conditional statements
     * checks to see if the square at r,c 
     * is a valid place to move this knight
     * @param board
     * @param r the row in question
     * @param c the column in question
     * @return whether the knight can validly go to the square 
     */
    public boolean isLegalMove(Knight player, int r, int c) {
        //your code here 
        int row=player.getRow();
        int col=player.getCol();
        if (r<0 || c<0 || r>getNumRows() || c>getNumCols())
            return false;
        if (!isAvailable(r, c))
            return false;
        if (r+2==row)
            if(c+1==col || c-1==col)
                return true;
        if (r-2==row)
            if(c+1==col || c-1==col)
                return true;
        if (r+1==row)
            if(c+2==col || c-2==col)
                return true;
        if (r-1==row)
            if(c+2==col || c-2==col)
                return true;
        return false;
    }

    public boolean hasMovesLeft(Knight player){
        int maxRow=getNumRows();
        int maxCol=getNumCols();
        int row=player.getRow();
        int col=player.getCol();
        if (row+2<maxRow ){
            if (col+1< maxCol && isAvailable(row+2, col+1))
                return true;
            if (col-1>=0 && isAvailable(row+2, col-1))
                return true;
        }
        if (row+1<maxRow ){
            if (col+2< maxCol && isAvailable(row+1, col+2))
                return true;
            if (col-2>=0 && isAvailable(row+1, col-2))
                return true;
        }
        if (row-2>=0 ){
            if (col+1< maxCol && isAvailable(row-2, col+1))
                return true;
            if (col-1>=0 && isAvailable(row-2, col-1))
                return true;
        }
        if (row-1>=0 ){
            if (col+2< maxCol && isAvailable(row-1, col+2))
                return true;
            if (col-2>=0 && isAvailable(row-1, col-2))
                return true;
        }

        return false;
    }

}

JoustApplet.java

import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
/**
 * A applet to hold the Joust Board
 * and deal with user input
 * Free for educational use.
 * @author Chris Thiel, OFMCap
 * @version 30 Sept 2012
 *
 */

public class JoustApplet extends Applet implements MouseMotionListener, MouseListener
{
    /**
     * Instance fields for the Game
     *
     */
	private Board board;
	private Rectangle restartBtn;
	/**
     * These instance field with hold the images for
     * the squares and knights and sounds
     */
	private AudioClip click,buzzer;
	private BufferedImage darkSquareImg, lightSquareImg, burnedSquareImg;
	private BufferedImage  darkKnightImg, lightKnightImg;
	/**
	 * These instance fields are for getting rid of flicker
	 * on the Windows platform-- the Applet will draw the 
	 * picture in memory before putting it on the screen
	 */
	private Image virtualMem;
    private Graphics gBuffer;
    private Font font;//The font for our messages
    private String message;
    public void init()
    {
    		readResources();
    		board=new Board(5,5, lightKnightImg, darkKnightImg);

    		for (int r=0; r< board.getNumRows();r++)
    			for (int c=0; c<board.getNumCols(); c++)
    				board.getSquare(r, c).setImgArray(darkSquareImg, lightSquareImg, burnedSquareImg);
    		this.restartBtn = new Rectangle (670, 350, 80,40);
        this.addMouseListener(this);
		this.addMouseMotionListener(this);
        font = new Font("Helvetica", Font.BOLD,  18);  
        message="Joust! "+"White"+"'s move";
    }
    /**
     * On loading the Applet we read the sound and image files
     * from the java archive (jar file), and play the sounds 
     * to initialize them.
     */
    public void readResources()
    {
    	    try {
			darkSquareImg = ImageIO.read( getClass().getResource("/darkSquare.png"));
			lightSquareImg = ImageIO.read( getClass().getResource("/lightSquare.png"));
			burnedSquareImg = ImageIO.read( getClass().getResource("/burnedSquare.png"));
			darkKnightImg = ImageIO.read( getClass().getResource("/darkKnight.png"));
			lightKnightImg = ImageIO.read( getClass().getResource("/lightKnight.png"));
		} catch (IOException e) {
			e.printStackTrace();
		}
    	    click = getAudioClip(getClass().getResource("/hit.wav"));
    		click.play();
    		buzzer = getAudioClip(getClass().getResource("/buzzer.wav"));
    		buzzer.play();
    }
    public void paint (Graphics g)
    {
    		//make a new buffer in case the applet size changed
    	    virtualMem = createImage(getWidth(),getHeight());
        gBuffer = virtualMem.getGraphics();
        gBuffer.setColor(Color.WHITE);
    		gBuffer.fillRect(0, 0, this.getWidth(), this.getHeight());
    		gBuffer.setColor(Color.BLACK);
    		gBuffer.setFont(font);
    		gBuffer.drawString(message, 625, 20);
    		gBuffer.drawString(board.getMessage(), 625, 40);
    		if (board.gameOver()){
    			gBuffer.setColor(Color.BLUE);
    			gBuffer.fillRect(restartBtn.x, restartBtn.y, restartBtn.width, restartBtn.height);
    			gBuffer.setColor(Color.YELLOW);
    			gBuffer.drawString("Restart", restartBtn.x+10, restartBtn.y+25);
    		}
    		board.draw(gBuffer);

    		g.drawImage(virtualMem,0,0,this);//set new display to Screen
    }

	@Override
	public void mouseClicked(MouseEvent e) {}

	@Override
	public void mousePressed(MouseEvent e) {}

	@Override
	public void mouseReleased(MouseEvent e) {

		if (board.move())
			click.play();
		else 
			buzzer.play();
		if(board.gameOver()){
			if (restartBtn.contains(e.getPoint())){
				board=new Board(8,8);
				repaint();
			}
			buzzer.play();
		}
		repaint();
	}

	@Override
	public void mouseEntered(MouseEvent e) {}

	@Override
	public void mouseExited(MouseEvent e) {}

	@Override
	public void mouseDragged(MouseEvent e) {}

	@Override
	public void mouseMoved(MouseEvent e) {
		int x=e.getX();
		int y=e.getY();

		board.setHighlight(x, y);
		message=board.whoseTurn();
		repaint();
	}

	public void update(Graphics g)
    {
        paint(g); //get rid of flicker with this method
    }

}