Spielprogrammierung mit Java
HomeAufgabenDruckenJava-Online

Four In A Row (4 gewinnt)


Four In A Row (auch: 4 gewinnt oder Connect four) ist ein Zweipersonen-Strategiespiel mit dem Ziel, als Erster vier der eigenen Spielsteine in eine Linie (horizontal, vertikal oder diagonal)zu bringen. Das klassische Brettspiel wird auf einem senkrecht stehenden, hohlen Spielbrett gespielt, in das die Spieler abwechselnd ihre Spielsteine fallen lassen. Das Spielbrett besteht aus sieben Spalten (senkrecht) und sechs Reihen (waagrecht). Jeder Spieler besitzt 21 gleichfarbige Spielsteine. Wenn ein Spieler einen Spielstein in eine Spalte fallen lässt, besetzt dieser den untersten freien Platz der Spalte. Gewinner ist der Spieler, der es als Erster schafft, vier seiner Spielsteine waagerecht, senkrecht oder diagonal in eine Linie zu bringen. Das Spiel endet unentschieden, wenn das Spielbrett komplett gefüllt ist, ohne dass ein Spieler eine Viererlinie gebildet hat.

In unserer Lernumgebung finden man dieses Spiel in drei Varianten:

  • Für zwei Spieler, die abwechslungsweise an einem Computer spielen
  • Für einen Spieler, der gegen den Computer spielt
  • Für zwei Spieler, die über das Internet spielen (im Abschnitt TCP/IP - Games)
 

Variante 1: Zwei Spieler spielen abwechlungsweise an einem Computer

Die beiden Spieler spielen abwechslungsweise am gleichen Computer. Man bewegt die Maus zu der ausgewählten Spalte und lässt den Spielstein mit einem Mausklick hinunter fallen.

Programmcode für lokale Bearbeitung Downloaden: FourInARow1.zip

 

Variante 2: Ein Spieler spielt gegen Computer

Der Spieler beginnt jeweils mit einem gelben Spielstein. Man bewegt die Maus zu der ausgewählten Spalte und lässt den Spielstein mit einem Mausklick hinunter fallen.

Der rote Spielstein wird jeweils unmittelbar danach durch den Computer gesetzt. Obwohl der Computer eine optimale Strategie spielt, kann auch der Spieler gewinnen.

 

Programmcode für lokale Bearbeitung Downloaden: FourInARow2.zip

 

 

Variante 3: Ein Spieler gegen Computer mit verschiedenen Spielstrategien

Programmcode Downloaden: FourInARowVsComputer.zip

 

Erklärungen zum Programmcode Variante 1 (zwei Spieler):

class Token Token ist ein eigenständiges Objekt. Der Token weiss, wie er sich bewegen muss und merkt, wenn er an der definitiven Position angekommen ist und welcher Spieler ihn in die Bewegung gesetzt hat
Location nextLoc = new Location(getX(), getY() + 1) Der Spielstein bewegt sich im Gitter nach unten
if (gameGrid.getOneActorAt(nextLoc) == null && isMoveValid()) Es wird überprüft, ob die nächst untere Zelle leer ist und ob der Spielstein nicht bereits in der untersten waagrechten Linie ist

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

Der Spielstein bewegt sich nicht sprunghaft von Zelle zur Zelle, sondern kontinuierlich. Seine "Zwischenpositionen" werden mit Pixelkoordinaten angegeben
class BG
addActor(new BG(), new Location(3, -1))
Die Klasse BG braucht man lediglich dazu, um die Spielsteine hinten dem gelochten Brett fallen zu lassen. Das gelochte Brett ist ein Actor mit transparenten Löchern, der bzgl. Sichtbarkeit vor den Token positioniert wird
boolean check4Win(Location loc) Bei jedem neu gesetzten Spielstein müssen Zeilen, Spalten und Diagonalen überprüft werden

Programmcode zur Variante 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;
  }
}