Connect 4 Game

<< LizardSplat | OtherProjectsTrailIndex | Gift Interface >>

A working copy is at http://www.mathorama.com/Connect4.html Here is the starter code

Connect4GameApplet.java

package connect4;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;

import java.awt.event.MouseListener;

@SuppressWarnings("serial")
public class Connect4GameApplet extends Applet implements MouseListener
{
	Board board;
	Player[] player;
	String message="Turn No. 1 Red's Move";
	String comment="No Comment";
	String score="New Game";
	int turn=1;
	private final int left=20;
	private final int top=80;
    private final int boxSize=60;
	public void init()
    {
      player = new Player[2];
      player[0]=new Player("Red", Color.RED);
      player[0].setRobot(false);
      player[1]= new Player("Black", Color.BLACK);
      player[1].setRobot(false);

	  board= new Board();
	  board.setCellSize(boxSize);

      addMouseListener(this);
    }
	public void paint(Graphics g)
	{

        g.setColor(Color.white);
        g.fillRect(0, 0, 500, 500);
        g.setColor(Color.black);
        g.drawString("Connect 4 Game", 20, 20);
        g.setColor(Color.blue);
        g.drawString("click to see next turn", 20, 40);
        g.drawString(message , left, 65);
        g.drawString(comment , left, 490);
        g.drawString(score , left, 470);
        board.draw(g, left, top);


	}
	public boolean gameOver(){
		return (board.legalMoves().size()==0 
				|| board.hMax()>3
				|| board.vMax()>3
				|| board.dMax()>3);
	}
	public void mouseClicked(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}

	public void mouseExited(MouseEvent e) {}

	public void mousePressed(MouseEvent e) {	}
	public void mouseReleased(MouseEvent e) {
		int click=e.getX();
		click-=left;
		int selectedCol=click/boxSize;
		if (selectedCol>6)
			selectedCol=6;
		if (!gameOver()){
			turn++;
			message="Turn No. "+turn+" ";
			Color c=player[turn%2].getColor();
			if (player[turn%2].isRobot()){
				selectedCol=player[turn%2].getMove(board.legalMoves());
				board.add(new Piece(c, board.columnTop(selectedCol) ,selectedCol));
			}else{
				if(board.legalMoves().contains(selectedCol)){
					board.add(new Piece(c, board.columnTop(selectedCol) ,selectedCol));
				}else{
					message+="Illegal move - still ";
					turn--;
				}
			}


			if (gameOver())
				message=player[turn%2].getName()+" WON!";
			else
				message+=player[(turn-1)%2].getName()+"'s move";

		} else{
			message="Game Over";
		}
		comment="Last Selected Column: "+selectedCol+" (top is "+board.columnTop(selectedCol);
		comment+= ") legal Moves: "+board.legalMoves();
		score="horiz Max="+board.hMax()+" vert max="+board.vMax()+" diag max="+board.dMax();
		repaint();

	}

}

Player.java


package connect4;

import java.awt.Color;
import java.util.ArrayList;

public class Player {
	private String name;
	private boolean robot;
	private Color color;

	public Player(){
		setName("Player Name");
		setRobot(true);
		setColor(Color.RED);
	}
	public Player(String theName){
		this();
		setName(theName);
	}
	public Player(boolean isRobot){
		this();
		setRobot(isRobot);
	}
	public Player(String theName, boolean isRobot){
		this();
		setName(theName);
		setRobot(isRobot);
	}
	public Player(String theName, Color c){
		this();
		setName(theName);
		setColor(c);
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param robot define whether the player is a robot or not
	 */
	public void setRobot(boolean robot) {
		this.robot = robot;
	}

	/**
	 * @return is the player is a robot or not.
	 */
	public boolean isRobot() {
		return robot;
	}
	public int getMove(ArrayList<Integer> legalMoves){
		if (robot){
			int choice=randomInt(0,legalMoves.size());
			return legalMoves.get(choice);
		}
		return -1;
	}
	private int randomInt(int min, int max) {

		return min+(int)((max-min)*Math.random());
	}
	/**
	 * @param color the color to set
	 */
	public void setColor(Color color) {
		this.color = color;
	}
	/**
	 * @return the color
	 */
	public Color getColor() {
		return color;
	}

}

Piece.java


package connect4;

import java.awt.Color;

public class Piece 
{

	private Color color;
	private int row;
	private int col;

	public Piece(){

		setColor(Color.RED);
		setRow(-1);
		setCol(-1);
	}

	public Piece(Color color, int r, int c){
		this();
		setColor(color);
		setRow(r);
		setCol(c);
	}
	/**
	 * @param color the color to set
	 */
	public void setColor(Color color) {
		this.color = color;
	}
	/**
	 * @return the color
	 */
	public Color getColor() {
		return color;
	}
	/**
	 * @param row the row to set
	 */
	public void setRow(int row) {
		this.row = row;
	}
	/**
	 * @return the row
	 */
	public int getRow() {
		return row;
	}
	/**
	 * @param col the col to set
	 */
	public void setCol(int col) {
		this.col = col;
	}
	/**
	 * @return the col
	 */
	public int getCol() {
		return col;
	}
	public void setLocation(int r, int c){
		setRow(r);
		setCol(c);
	}
	public String toString(){
		String theColor="Black";
		if (color.equals(Color.RED)) theColor=" Red ";
		return "Piece[row = "+row+" col = "+col+" ("+theColor+")]";
	}
	public boolean equals(Piece p){
		return p.getColor().equals(color);
	}

}

Board.java


package connect4;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;

public class Board {
	private static final int rows=6;
	private static final int cols=7;
	private int cellSize;
	private Piece [][] grid;
	public Board(){
		grid = new Piece[rows][cols];
		setCellSize(100);
	}
	public static int getRows() {
		return rows;
	}
	public static int getCols() {
		return cols;
	}

	public Piece [][] getGrid() {
		return grid;
	}
	/**
	 * columnTop returns the index of the row of the
	 * next available cell of the column
	 * or -1 if the column is full
	 * @param c the column to check
	 * @return the row index of the columnTop
	 */
	public int columnTop(int c){
		if (grid[0][c]!=null) return -1;
		int result=rows-1; //start at the bottom index
		while(grid [result][c]!=null && result>0)
			result--;
		return result;
	}
	/**
	 * legalMoves
	 * @return ArrayList<Integer> of open columns
	 */
	public ArrayList<Integer> legalMoves(){
		ArrayList<Integer> moves = new ArrayList<Integer>();
		for(int i=0;i<cols; i++)
			if(columnTop(i)>-1)
				moves.add(i);
		return moves;
	}
	/**
	 * added a peice to the board
	 * @param p the Peice
	 * @return if sucessful
	 */
	public boolean add(Piece p){
		int r=p.getRow();
		int c=p.getCol();
		if (r<0 || c<0 ) return false;
		if (grid[r][c]!=null) return false;
		grid[r][c]=p;
		return true;
	}
	public void draw(Graphics g, int left, int top){
		g.setColor(Color.BLACK);
		for (int r=0; r<rows; r++)
			for (int c=0; c<cols; c++){
				g.setColor(Color.BLACK);
				g.drawRect(left+c*cellSize, top+r*cellSize, cellSize, cellSize);
				if (grid[r][c]!=null){
					Piece p=grid[r][c];
					g.setColor(p.getColor());
					g.fillOval(left+c*cellSize, top+r*cellSize, cellSize, cellSize);
				}
			}

	}
	public void setCellSize(int cellSize) {
		this.cellSize = cellSize;
	}
	public int getCellSize() {
		return cellSize;
	}
	public int hMax(){
		int result=0;
		for (int row=0;row<rows;row++){
			int count=1;
			for (int col=1;col<cols;col++){
				Piece left=grid[row][col-1];
				Piece right=grid[row][col];
				if (left !=null && right!=null && left.equals(right)){
					count++;
				}else{
					if (count>result)
						result=count;
					count=1;
				}
			}
			if (count>result)
				result=count;
		}
		return result;
	}
	public int vMax(){
		int result=0;
		for (int col=0;col<cols;col++){
			int count=1;
			for (int row=1;row<rows;row++){
				Piece top=grid[row-1][col];
				Piece bottom=grid[row][col];
				if (top !=null && bottom!=null && top.equals(bottom)){
					count++;
				}else{
					if (count>result)
						result=count;
					count=1;
				}
			}
			if (count>result)
				result=count;
		}
		return result;
	}
	public Piece getGrid(int hash){
		int r=hash/7;
		int c=hash%7;
		return grid[r][c];
	}
	public int dMax(){
		int result=0;
		for (int n=1;n<6;n++){
			int count=1;
			for (int i=n;i<n*7;i+=6){
				Piece left=getGrid(i);
				Piece right=getGrid(i+6);
				if (left !=null && right!=null && left.equals(right)){
					count++;
				}else{
					if (count>result)
						result=count;
					count=1;
				}
			}
			if (count>result)
				result=count;
		}
		for (int n=36;n<41;n++){
			int count=1;
			for (int i=n;i>(n-35)*7;i-=6){
				Piece left=getGrid(i);
				Piece right=getGrid(i-6);
				if (left !=null && right!=null && left.equals(right)){
					count++;
				}else{
					if (count>result)
						result=count;
					count=1;
				}
			}
			if (count>result)
				result=count;
		}
		for (int n=1;n<6;n++){
			int count=1;
			for (int i=n;i<48-n*7;i+=8){
				Piece left=getGrid(i);
				Piece right=getGrid(i+8);
				if (left !=null && right!=null && left.equals(right)){
					count++;
				}else{
					if (count>result)
						result=count;
					count=1;
				}
			}
			if (count>result)
				result=count;
		}
		for (int n=36;n<40;n++){
			int count=1;
			for (int i=n;i>(40-n)*7;i-=8){
				Piece left=getGrid(i);
				Piece right=getGrid(i-8);
				if (left !=null && right!=null && left.equals(right)){
					count++;
				}else{
					if (count>result)
						result=count;
					count=1;
				}
			}
			if (count>result)
				result=count;
		}
		return result;
	}

}

Connect4App.java

This is an application version that uses key presses rather than mouse clicks:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Connect4App extends JPanel implements KeyListener
{
	public static final int WIDTH=500;
	public static final int HEIGHT=600;
	private static final int LEFT=40;
	private static final int TOP=80;
	private static final int BOX_SIZE=60;
	private Font titleFont, regularFont;
	private Board board;
	private Player[] player;
	private String message;
	private String comment;
	private String score;
	int turn=1;


	public Connect4App()
	{

		//initialize variables here...
		titleFont = new Font("Roman", Font.BOLD, 18);
		regularFont = new Font("Helvetica", Font.PLAIN, 12);
		// 
		newGame();
	}
	public void newGame()
	{
		player = new Player[2];
		player[0]=new Player("Red", Color.RED);
		player[0].setRobot(false);
		player[1]= new Player("Black", Color.BLACK);
		player[1].setRobot(true);

		board= new Board();
		board.setCellSize(BOX_SIZE);
		message="Turn No. 1 Red's Move";
		comment="No Comment";
		score="New Game";

	}
	public boolean gameOver(){
		return (board.legalMoves().size()==0 
				|| board.hMax()>3
				|| board.vMax()>3
				|| board.dMax()>3);
	}
	public static void main(String[] args) {
		Connect4App app= new Connect4App();
		JFrame window = new JFrame("My Generic Application");
		window.setSize(WIDTH, HEIGHT);
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		window.getContentPane().add(app);
		window.addKeyListener(app);
		//window.pack();
		window.setVisible(true);

	}
	public void paintComponent(Graphics g){
		super.paintComponent(g);
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, getWidth(),getHeight());
		g.setColor(Color.BLUE);
		g.setFont(titleFont);
		g.drawString("Connect 4", 20, 20);
		g.setColor(Color.BLACK);
		g.setFont(regularFont);
		g.drawString("Type a column number from 0 to 6", 20, 65);
		g.drawString(message , LEFT, 40);
		g.drawString(comment , LEFT, 490);
		g.drawString(score , LEFT, 470);
		if (board!=null)
			board.draw(g, LEFT, TOP);		
	}
	// update is a workaround to cure Windows screen flicker problem
	public void update(Graphics g){
		paint(g);
	}

	// These 3 methods need to be declares to implement the KeyListener Interface
	@Override
	public void keyTyped(KeyEvent e) {}

	@Override
	public void keyPressed(KeyEvent e) {}

	@Override
	public void keyReleased(KeyEvent e) {
		String key = ""+e.getKeyChar();
		comment = key +" pressed";
		int selectedCol=-1;
		try{
			selectedCol = Integer.parseInt(key);
		} catch(NumberFormatException formatException) {
			comment = key +" pressed-- try 0-6 silly ";
		}
		if (selectedCol>6)
			selectedCol=6;
		if (!gameOver()){
			turn++;
			message="Turn No. "+turn+" ";
			Color c=player[turn%2].getColor();
			if (player[turn%2].isRobot()){
				selectedCol=player[turn%2].getMove(board.legalMoves());
				board.add(new Piece(c, board.columnTop(selectedCol) ,selectedCol));
			}else{
				if(board.legalMoves().contains(selectedCol)){
					board.add(new Piece(c, board.columnTop(selectedCol) ,selectedCol));
				}else{
					message+="Illegal move - still ";
					turn--;
				}
			}


			if (gameOver())
				message=player[turn%2].getName()+" WON!";
			else
				message+=player[(turn-1)%2].getName()+"'s move";

		} else{
			message="Game Over";
		}
		comment="Last Selected Column: "+selectedCol+" (top is "+board.columnTop(selectedCol);
		comment+= ") legal Moves: "+board.legalMoves();
		score="horiz Max="+board.hMax()+" vert max="+board.vMax()+" diag max="+board.dMax();
		repaint();
	}

}