GameGrid: Game programming with Java

Research project PHBern  
HomePrintJava-Online

Four in a row


Four in a row (aka Connect four) is a two-player strategy game. Its goal is to be the player to first connect four stones of the same color in one horizontal, vertical or diagonal line. The classic game is played on a vertical and perforated board into which the players drop their stones one after the other. The board has seven columns and six rows. Each player gets twenty-one stones of the same color. The game ends in a draw when each cell is filled with a stone (board is full) and no "four in a row" is achieved.

This game can be found in four different versions on this webpage:

  • two players on one PC
  • one player vs PC
  • two players play via bluetooth
  • two players play via TCP/IP
 

Version 1: two players on one PC

Both players play one after the other on the same computer. The mouse cursor is set above the designated column and with a click the stone falls to the lowest possible cell.

Run this game

Edit this game in the Online-Editor

Download the program code (FourInARow1.zip)

 

Program code of version 1:

// FourInARow1.java

import ch.aplu.jgamegrid.*;
import java.awt.*;

public class FourInARow1 extends GameGrid implements GGMouseListener
{
  private int currentPlayer = 0;
  public boolean finished = false;
  Token activeToken;

  public FourInARow1()
  {
    super(7, 7, 70, nullnullfalse);
    addMouseListener(thisGGMouse.lPress | GGMouse.move);
    this.getBg().setBgColor(Color.white);
    activeToken = new Token(currentPlayer, this);
    addActor(activeToken, new Location(0, 0), Location.SOUTH);
    addActor(new BG(), new Location(3, -1)); //outside of grid, so it doesn't disturb game
    getBg().setFont(new Font("SansSerif"Font.BOLD, 48));
    getBg().setPaintColor(Color.red);
    show();
    setSimulationPeriod(30);
    doRun();
    addStatusBar(30);
    setStatusText("Move mouse to a column and click to set the token.");
    setTitle("Four In A Row (for two local players)");
  }

  public void reset()
  {
    getBg().clear();
    removeActors(Token.class)//remove all tokens
    setStatusText("Game reset! " + (currentPlayer == 0 ? "Yellow" : "Red") + " player begins.");
    activeToken = new Token(currentPlayer, this);
    addActor(activeToken, new Location(0, 0), Location.SOUTH);
    finished = false;
  }

  public boolean mouseEvent(GGMouse mouse)
  {
    Location mouseLoc = toLocation(mouse.getX(), mouse.getY());
    if (mouse.getEvent() == GGMouse.move)
    { //move active Token with mouse
      if (!finished && activeToken.getX() != mouseLoc.x)
        activeToken.setX(mouseLoc.x);
      return true;
    }

    if (finished)
    {
      reset();
      return true;
    }

    if (getOneActorAt(new Location(mouseLoc.x, 1)) == null)
    { //drop Token if column isn't full
      activeToken.setActEnabled(true);
      setMouseEnabled(false);
      currentPlayer = (currentPlayer + 1) % 2;
    }
    else
    {
      setStatusText("This column is full.");
    }
    return true;
  }

  public int getPlayerOfTokenAt(int x, int y)
  {
    Location loc = new Location(x, y);
    if (getOneActorAt(loc) == null)
      return -1;
    else
      return ((Token)getOneActorAt(loc)).getPlayer();
  }

  //return true, if four are connected through that token
  public boolean check4Win(Location loc)
  {
    int col = loc.x;
    int row = loc.y;
    return (checkVertically(col, row, 4) || checkHorizontally(col, row, 4)
      || checkDiagonally1(col, row, 4)
      || checkDiagonally2(col, row, 4));

  }

  //for checking nrOfTokens (win situation: nrOfTokens = 4)
  private boolean checkDiagonally1(int col, int row, int nrOfTokens)
  {
    for (int = 0; j < nrOfTokens; j++)
    {
      int adjacentSameTokens = 0;
      for (int = 0; i < nrOfTokens; i++)
      {
        if ((col + - j>= && (col + - j< nbHorzCells
          && (row + - j>= && (row + - j< nbVertCells
          && getPlayerOfTokenAt(col + - j, row + - j== getPlayerOfTokenAt(col, row))
        {
          adjacentSameTokens++;
        }
      }
      if (adjacentSameTokens >= nrOfTokens)
        return true;
    }
    return false;
  }

  private boolean checkDiagonally2(int col, int row, int nrOfTokens)
  {
    for (int = 0; j < nrOfTokens; j++)
    {
      int adjacentSameTokens = 0;
      for (int = 0; i < nrOfTokens; i++)
      {
        if ((col - + j>= && (col - + j< nbHorzCells
          && (row + - j>= && (row + - j< nbVertCells
          && getPlayerOfTokenAt(col - + j, row + - j== getPlayerOfTokenAt(col, row))
        {
          adjacentSameTokens++;
        }
      }
      if (adjacentSameTokens >= nrOfTokens)
        return true;
    }
    return false;
  }

  private boolean checkHorizontally(int col, int row, int nrOfTokens)
  {
    int adjacentSameTokens = 1;
    int = 1;
    while (col - >= && getPlayerOfTokenAt(col - i, row) == getPlayerOfTokenAt(col, row))
    {
      adjacentSameTokens++;
      i++;
    }
    i = 1;
    while (col + < nbHorzCells && getPlayerOfTokenAt(col + i, row) == getPlayerOfTokenAt(col, row))
    {
      adjacentSameTokens++;
      i++;
    }
    return (adjacentSameTokens >= nrOfTokens);
  }

  private boolean checkVertically(int col, int row, int nrOfTokens)
  {
    int adjacentSameTokens = 1;
    int = 1;
    while (row + < nbVertCells && getPlayerOfTokenAt(col, row + i== getPlayerOfTokenAt(col, row))
    {
      adjacentSameTokens++;
      i++;
    }
    return (adjacentSameTokens >= nrOfTokens);
  }

  public static void main(String[] args)
  {
    new FourInARow1();
  }
}
//------------- class BG ----------------
class BG extends Actor
{
  public BG()
  {
    super(false"sprites/4inARowBG.png");
  }

  public void reset()
  {
    this.setLocationOffset(new java.awt.Point(0, 4 * 70));
    this.setOnTop();
  }
}
//------------- class Token -------------------
class Token extends Actor
{
  private int player, nb;
  private FourInARow1 gg;

  public Token(int player, FourInARow1 gg)
  {
    super(false"sprites/token.png"2);
    this.player = player;
    this.gg = gg;
    setActEnabled(false);
    show(player); // 0 = yellow , 1 = red
  }

  public void act()
  {
    Location nextLoc = new Location(getX(), getY() + 1);
    if (gameGrid.getOneActorAt(nextLoc) == null && isMoveValid())
    {
      if (nb == 6)
      {
        nb = 0;
        setLocationOffset(new java.awt.Point(0, 0));
        move();
      }
      else
        setLocationOffset(new java.awt.Point(0, 10 * nb));
      nb++;
    }
    else
    //token has arrived
      setActEnabled(false);
      if (gg.check4Win(getLocation()))
      {
        gg.setStatusText((player == 0 ? "Yellow" : "Red")
          + " player won! Click on the board to play again.");
        gg.getBg().drawText("Game Over"new Point(10, 55));
        gg.finished = true;
       }
      else
      {
        // make new Token:
        gg.activeToken = new Token((player + 1) % 2, gg);
        gg.addActor(gg.activeToken, new Location(getX(), 0),
          Location.SOUTH);
      }
      gg.setMouseEnabled(true);
    }
  }

  public int getPlayer()
  {
    return player;
  }
}

Explaining the program code of version 1:

class Token Token is a independent object. The token (stone) knows how to move, where its final position is and which player played it
Location nextLoc = new Location(getX(), getY() + 1) the stone moves down the grid
if (gameGrid.getOneActorAt(nextLoc) == null && isMoveValid()) checks if the next lower cell is emtpy or if the stone has already reached the lowest row

setLocationOffset(new java.awt.Point(0, 10 * nb))

the stone does not jump from cell to cell, but falls smoothly down the column. Its "positions in between cells" are calculated into pixel coordinates
class BG
addActor(new BG(), new Location(3, -1))
the class is only used, to let the stones fall behind the board. The perforated board is a Actor with transparent holes, which is set in front of the stones
boolean check4Win(Location loc) after each newly set stone the horizontal, vertical and diagonal lines need to be checked

 

 

Version 2: one player vs PC

The player starts with a yellow stone. The mouse cursor is set above the designated column and with a click the stone falls to the lowest possible cell.

The red stone is set immediately afterwards by the computer. Even though the PC plays with an ideal strategy, it is possible to beat it.

 

Run this game

Download program code (FourInARow2.zip)