GameGrid: Game programming with Java

Research project PHBern  
HomePrintJava-Online

Nim game

The Nim game is one of the most favored games in game theory, because its winning strategy is easy to figure out. There are several different variations of the game and it can be played as a single-player game (PC vs. player) or as a two-player game (two players communicate via bluetooth or TCP/IP - check out bluetooth or TCP/IP games).

The simplest variation starts with 15 matches in one row. The player removes 1, 2 or 3 matches by clicking on them. Afterwards the computer removes 1 to 3 matches. And so on. The one removing the last match loses.

nim

Run this example

Edit this example in the Online-Editor

Program code:

// NimEx1.java

import ch.aplu.jgamegrid.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.Point;
import java.util.ArrayList;

public class NimEx1 extends GameGrid 
  implements GGMouseListener, GGButtonListener
{
  private int nbMatches;
  private int nbTakenMatches;
  private GGBackground bg;
  private GGButton okBtn = new GGButton("sprites/ok.gif"true);
  private GGButton newBtn = new GGButton("sprites/new.gif"true);

  public NimEx1()
  {
    super(56912false);
    bg = getBg();
    addMouseListener(this, GGMouse.lClick);
    addActor(okBtn, new Location(504));
    okBtn.addButtonListener(this);
    addActor(newBtn, new Location(504));
    newBtn.addButtonListener(this);
    init();
    show();
  }

  public void init()
  {
    nbMatches = 15;
    nbTakenMatches = 0;
    bg.clear();
    for (int i = 0; i < 15; i++)
      addActor(new Match()new Location(2 + 3 * i, 4));
    okBtn.show();
    newBtn.hide();
    refresh();
    setTitle(nbMatches + " matches. Click on 1, 2 or 3 matches to remove, then click 'OK'.");
  }

  public boolean mouseEvent(GGMouse mouse)
  {
    Location loc = toLocationInGrid(mouse.getX(), mouse.getY());
    Actor actor = null;
    for (int y = 2; y < 7; y++)
    {
      actor = getOneActorAt(new Location(loc.x, y), Match.class);
      if (actor != null)
        break;
    }
    if (actor != null)
    {
      if (nbTakenMatches == 3)
        setTitle("Take a maximum of 3. Click 'OK' to continue!");
      else
      {
        actor.removeSelf();
        nbMatches--;
        setTitle(nbMatches + " matches remaining. Click 'OK' to continue.");
        nbTakenMatches++;
        if (nbMatches == 0)
        {
          setTitle("Press 'new Game' to play again.");
          bg.setPaintColor(Color.red);
          bg.setFont(new Font("Arial"Font.BOLD, 90));
          bg.drawText("You lost!"new Point(toPoint(new Location(36))));
          okBtn.hide();
          newBtn.show();
        }
        refresh();
      }
    }
    return false;
  }

  public void computerMove()
  {
    int nbRemovedMatches = nbMatches - nbWin(nbMatches);
    if (nbRemovedMatches == 0)
      nbRemovedMatches = 1// optimal strategy impossible
    nbMatches = nbMatches - nbRemovedMatches;
    for (int x = 0; x < nbRemovedMatches; x++)
    {
      ArrayList<Actor> matches = getActors(Match.class);
      int k = (int)(Math.random() * matches.size());
      removeActor(matches.get(k))// take random match
    }
    setTitle(nbMatches + " matches remaining. Your move now.");

    if (nbMatches == 0)
    {
      bg.setPaintColor(Color.red);
      bg.setFont(new Font("Arial"Font.BOLD, 96));
      bg.drawText("You win!"new Point(toPoint(new Location(36))));
      okBtn.hide();
      newBtn.show();
      setTitle("You win, press 'New Game' to play again.");
    }
    refresh();
  }

  // returns next lower winning number
  private int nbWin(int n)
  {
    return ((- 1) / 4) * 4 + 1;
  }

  public void buttonClicked(GGButton button)
  {
    if (nbMatches == 0)
        init();
      else
      {
        if (nbTakenMatches == 0)
          setTitle("You have to remove at least 1 match!");
        else
        {
          nbTakenMatches = 0;
          computerMove();
        }
      }
  }

  public void buttonPressed(GGButton button)
  {
  }

  public void buttonReleased(GGButton button)
  {
  }

  public static void main(String[] args)
  {
    new NimEx1();
  }
}

// -------------class Match--------------------------
class Match extends Actor
{
  public Match()
  {
    super("sprites/match.gif");
  }
}

Explaining the program code:

init() the method init() initializes a new game (matches and the OK-button are shown). It is called at the start and after each restart. Since the matches are long and thin, the cell size of the grid is small and a match is placed over more than one cell vertically
setTitle() the output is shown in the title bar of the game window
for (int y = 2; y < 7; y++)
{
     actor = getOneActorAt(new Location(loc.x, y), Match.class);
   
 }
after the mouse click, several cells of the same x-coordinates need to be checked
private int nbWin(int n)
 {
    return ((n - 1) / 4) * 4 + 1;
 }
calculates the remaining number of matches. The computer then adjusts its winning strategy and removes the ideal number of matches.
implements GGButtonListener
implements a ButtonListener with the callbackmethods buttonClicked() buttonPressed() and buttonReleased()
GGButton okBtn = new GGButton("sprites/ok.gif", true);
okBtn.addButtonListener(this);
initializes an OK-button and registers a GGButtonListener. The parameter true allows three different views of the button (default, over, click). The Sprite pictures ok_0.gif, ok_1.git, ok_2.gif need to be placed inside the directory sprites
okBtn.hide()
 newBtn.show()
both buttons okBtn and newBtn have the same dimensions and the same position. At the start of the game, the okBtn-button is shown and is used to end a turn. If the game is over (nbMaches = 0), the okBtn-button is hidden and the newBtn-button is shown. Clicking this button restarts the game

 

Advanced Nim game


In comparison to the easy Nim game varition, this advanced Nim game has six rows instead of one. Each row has a random number of matches (between 1 to 15 matches). The distripution of the matches is redesigned after each (re)start.

Rules:
The player can remove any number of matches out of one row. Clicking on ok the move is terminated and the computer starts removing matches.

This time, it's the player that removes the last match that wins.

Can you figure out the winnig strategy in this variation?

 

Run this example

Edit this example in the Online-Editor