advertisement
javaboutique
Search Tips
Articles  |   Tutorials  |   Reviews  |   Tools  |   by Category  |   by Date  |   by Name  |   Submit  |   Source  |   Forums  |  
javaboutique
Browse DevX


Partners & Affiliates











advertisement

SnakePit


/************************************************************************************************

SnakePit.java

  Usage:

  <applet code="SnakePit.class" width=625 height=430></applet>

  Keyboard Controls:

  S           - Start Game
  P           - Pause Game
  M           - Mute Sound
  Cursor Keys - Change Direction

************************************************************************************************/

import java.awt.*;
import java.net.*;
import java.util.*;
import java.applet.Applet;
import java.applet.AudioClip;

/************************************************************************************************
  Main applet code.
************************************************************************************************/

public class SnakePit extends Applet implements Runnable {

  // Thread control variables.

  Thread loopThread;
  Thread loadThread;

  // Constants

  static final int MAX_DELAY  = 75;    // Milliseconds between screen updates.
  static final int MIN_DELAY  = 50;
  static final int DELAY_INCR =  5;

  static final int GRID_WIDTH  = 39;    // Size of playing field.
  static final int GRID_HEIGHT = 25;
  static final int GRID_SIZE   = 16;    // Grid size in pixels.

  static final int NUM_LEVELS = 5;    // Number of levels.
  static final int NUM_MICE   = 6;    // Number of active mice.
  static final int NUM_LIVES  = 3;    // Starting number of lives.

  static final int INIT  = 1;    // Game states.
  static final int PLAY  = 2;
  static final int LEVEL = 3;
  static final int END   = 4;
  static final int OVER  = 5;

  static final int END_COUNT   = 30;    // Counter values.
  static final int LEVEL_COUNT = 40;

  static final int TYPE_MASK  = 0x00FF0000;    // Grid cell types.
  static final int EMPTY      = 0x00000000;
  static final int BLOCK      = 0x00010000;
  static final int SNAKE      = 0x00020000;
  static final int MOUSE      = 0x00030000;
  static final int KEY        = 0x00040000;

  static final int DIR_MASK   = 0x0000FF00;    // Grid cell directions.
  static final int NONE       = 0x00000000;
  static final int LEFT       = 0x00000100;
  static final int RIGHT      = 0x00000200;
  static final int UP         = 0x00000300;
  static final int DOWN       = 0x00000400;

  static final int SHAPE_MASK = 0x000000FF;    // Grid cell shapes.
  static final int SQUARE     = 0x00000000;
  static final int SNAKEHEAD  = 0x00000001;
  static final int SNAKEBODY  = 0x00000002;
  static final int SNAKEELB1  = 0x00000003;
  static final int SNAKEELB2  = 0x00000004;
  static final int SNAKETAIL  = 0x00000005;
  static final int MOUSEBODY  = 0x00000006;
  static final int KEYSHAPE   = 0x00000007;

  static final int MOUSE_POINTS =  10;    // Scoring values.
  static final int LEVEL_POINTS = 200;
  static final int EXTRA_LIFE   = 500;

  // Sizing data.

  int width;
  int height;

  // Game data.

  int     score;         // Scoring data.
  int     highScore;
  int     lives;
  int     extraLife;

  int     level;         // Level data.
  int     levelTotal;
  int     miceNeeded;
  int     miceEaten;
  int     delay;

  int[][] grid;          // Playing field.

  Point[] snake;         // Snake data.
  int     headPtr;
  int     tailPtr;
  int     direction;
  int     lastDirection;
  boolean lockKeys;

  Point[] mouse;         // Mouse data.

  Point   key;           // Key data.
  boolean keyActive;

  int     gameState;     // Game state data.
  int     levelCounter;
  int     endCounter;
  boolean paused;

  // Screen colors.

  Color bgColor    = Color.black;
  Color fgColor    = Color.white;
  Color blockColor = new Color(0, 0, 153);
  Color fieldColor = new Color(204, 153, 102);
  Color snakeColor = new Color(0, 153, 0);
  Color mouseColor = Color.gray;
  Color keyColor   = new Color(204, 102, 102);

  // Basic shapes.

  Polygon snakeHead;
  Polygon snakeBody;
  Polygon snakeTail;
  Polygon snakeElb1;
  Polygon snakeElb2;
  Polygon mouseBody;
  Polygon keyShape;

  // Sound data.

  boolean   loaded = false;
  boolean   sound;
  AudioClip bonkSound;
  AudioClip munchSound;
  AudioClip squeakSound;
  AudioClip chimeSound;
  AudioClip advanceSound;

  // Values for the offscreen image.

  Dimension offDimension;
  Image     offImage;
  Graphics offGraphics;

  // Font data.

  Font font = new Font("Helvetica", Font.BOLD, 12);
  FontMetrics fm;
  int fontWidth;
  int fontHeight;

  // Applet information.

  public String getAppletInfo() {

    return("Snake Pit, Copyright 1998 by Mike Hall.");
  }

  public void init() {

    Graphics g;
    int i;

    // Take credit.

    System.out.println("Snake Pit, Copyright 1998 by Mike Hall.");

    // Set font data.

    g = getGraphics();
    g.setFont(font);
    fm = g.getFontMetrics();
    fontWidth = fm.getMaxAdvance();
    fontHeight = fm.getHeight();

    // Define the playing grid.

    grid = new int[GRID_WIDTH][GRID_HEIGHT];

    // Initialize basic shapes.

    snakeHead = new Polygon();
    snakeHead.addPoint(4, 15);
    snakeHead.addPoint(3, 14);
    snakeHead.addPoint(1, 12);
    snakeHead.addPoint(1, 8);
    snakeHead.addPoint(3, 6);
    snakeHead.addPoint(3, 4);
    snakeHead.addPoint(6, 1);
    snakeHead.addPoint(9, 1);
    snakeHead.addPoint(12, 4);
    snakeHead.addPoint(12, 6);
    snakeHead.addPoint(14, 8);
    snakeHead.addPoint(14, 12);
    snakeHead.addPoint(12, 14);
    snakeHead.addPoint(11, 15);

    snakeBody = new Polygon();
    snakeBody.addPoint(11, 0);
    snakeBody.addPoint(11, 15);
    snakeBody.addPoint(4, 15);
    snakeBody.addPoint(4, 0);

    snakeElb1 = new Polygon();
    snakeElb1.addPoint(11, 0);
    snakeElb1.addPoint(11, 4);
    snakeElb1.addPoint(15, 4);
    snakeElb1.addPoint(15, 11);
    snakeElb1.addPoint(7, 11);
    snakeElb1.addPoint(4, 8);
    snakeElb1.addPoint(4, 0);

    snakeElb2 = mirror(snakeElb1);

    snakeTail = new Polygon();
    snakeTail.addPoint(11, 0);
    snakeTail.addPoint(8, 15);
    snakeTail.addPoint(7, 15);
    snakeTail.addPoint(4, 0);

    mouseBody = new Polygon();
    mouseBody.addPoint(8, 1);
    mouseBody.addPoint(12, 5);
    mouseBody.addPoint(12, 6);
    mouseBody.addPoint(10, 7);
    mouseBody.addPoint(12, 9);
    mouseBody.addPoint(12, 12);
    mouseBody.addPoint(10, 14);
    mouseBody.addPoint(5, 14);
    mouseBody.addPoint(3, 12);
    mouseBody.addPoint(3, 9);
    mouseBody.addPoint(5, 7);
    mouseBody.addPoint(3, 6);
    mouseBody.addPoint(3, 5);
    mouseBody.addPoint(7, 1);

    keyShape = new Polygon();
    keyShape.addPoint(1, 6);
    keyShape.addPoint(7, 6);
    keyShape.addPoint(9, 4);
    keyShape.addPoint(13, 4);
    keyShape.addPoint(15, 6);
    keyShape.addPoint(15, 10);
    keyShape.addPoint(13, 12);
    keyShape.addPoint(9, 12);
    keyShape.addPoint(7, 10);
    keyShape.addPoint(3, 10);
    keyShape.addPoint(3, 11);
    keyShape.addPoint(0, 11);
    keyShape.addPoint(0, 7);

    // Initialize data.

    highScore = 0;
    sound = true;
    snake = new Point[GRID_WIDTH * GRID_HEIGHT];
    for (i = 0; i < snake.length; i++)
      snake[i] = new Point(-1, -1);
    mouse = new Point[NUM_MICE];
    for (i = 0; i < NUM_MICE; i++)
      mouse[i] = new Point(-1, -1);
    key = new Point(-1, -1);
    lockKeys = false;
    initGame();
    endGame();
    for (i = 0; i < NUM_MICE; i++)
      killMouse(i);
    gameState = INIT;
  }

  public void initGame() {

    // Initialize game data.

    score = 0;
    lives = NUM_LIVES;
    level = 0;
    levelTotal = 0;
    extraLife = EXTRA_LIFE;
    delay = MAX_DELAY;
    paused = false;
    initLevel();
  }

  public void endGame() {

    gameState = OVER;
  }

  public void initLevel() {

    int i;

    // Advance level. Once we have gone thru each, start at the beginning and
    // increase speed.

    level++;
    if (level > NUM_LEVELS) {
      level = 1;
      if (delay > MIN_DELAY)
        delay -= DELAY_INCR;
    }

    // Level total for display.

    levelTotal++;

    // Initialize game data.

    initBlocks();
    initSnake();
    for (i = 0; i < NUM_MICE; i++)
      initMouse(i);
    miceEaten = 0;
    miceNeeded = 3 * (NUM_MICE + NUM_LEVELS - level);
    keyActive = false;
    gameState = PLAY;
  }

  public void endLevel() {

    // Start counter to pause before changing level.

    gameState = LEVEL;
    levelCounter = LEVEL_COUNT;
  }


  public void initLife() {

    // Create a new snake.

    killSnake();
    initSnake();
    gameState = PLAY;
  }

  public void endLife() {

    // Start counter to pause before starting a new snake.

    gameState = END;
    endCounter = END_COUNT;
  }

  public void start() {

    if (loopThread == null) {
      loopThread = new Thread(this);
      loopThread.start();
    }
    if (!loaded && loadThread == null) {
      loadThread = new Thread(this);
      loadThread.start();
    }
  }

  public void stop() {

    if (loopThread != null) {
      loopThread.stop();
      loopThread = null;
    }
    if (loadThread != null) {
      loadThread.stop();
      loadThread = null;
    }
  }

  public void run() {

    int i;
    long startTime;

    // Lower this thread's priority and get the current time.

    Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
    startTime = System.currentTimeMillis();

    // Run thread for loading sounds.

    if (!loaded && Thread.currentThread() == loadThread) {
      loadSounds();
      loaded = true;
      loadThread.stop();
    }

    // This is the main loop.

    while (Thread.currentThread() == loopThread) {

      if (!paused) {

        // Move snake and mice.

        if (gameState == PLAY) {
          moveSnake();
          for (i = 0; i < NUM_MICE; i++)
            moveMouse(i);
        }

        // Check the score and advance high score if necessary.

        if (score > highScore)
          highScore = score;

        // Add an extra life if score is high enough.

        if (score >= extraLife) {
          lives++;
          extraLife += EXTRA_LIFE;
        }

        // See if the snake was killed. If any lives are left, continue with
        // a new one.

        if (gameState == END && --endCounter < 0)
          if (--lives == 0)
            endGame();
          else
            initLife();

        // If enough mice have been eaten, show the key.

        if (gameState == PLAY && miceEaten == miceNeeded && !keyActive) {
          if (loaded && sound)
            chimeSound.play();
          initKey();
        }

        // If level was completed, go to the next one when the counter finishes.

        if (gameState == LEVEL && --levelCounter < 0)
          initLevel();
      }

      // Update the screen and set the timer for the next loop.

      repaint();
      try {
        if (gameState == PLAY)
          startTime += delay;
        else
          startTime += MIN_DELAY;
        Thread.sleep(Math.max(0, startTime - System.currentTimeMillis()));
      }
      catch (InterruptedException e) {
        break;
      }
    }
  }

  public void loadSounds() {

    // Load all sound clips by playing and immediately stopping them.

    try {
      bonkSound    = getAudioClip(new URL(getDocumentBase(), "bonk.au"));
      munchSound   = getAudioClip(new URL(getDocumentBase(), "munch.au"));
      squeakSound  = getAudioClip(new URL(getDocumentBase(), "squeak.au"));
      chimeSound   = getAudioClip(new URL(getDocumentBase(), "chime.au"));
      advanceSound = getAudioClip(new URL(getDocumentBase(), "advance.au"));
    }
    catch (MalformedURLException e) {}

    bonkSound.play();    bonkSound.stop();
    munchSound.play();   munchSound.stop();
    squeakSound.play();  squeakSound.stop();
    chimeSound.play();   chimeSound.stop();
    advanceSound.play(); advanceSound.stop();
  }

  public boolean keyDown(Event e, int key) {

    // Check if any cursor keys have been pressed and set direction but don't
    // allow a reversal of direction.

    if (!paused && !lockKeys) {
      if (key == Event.LEFT && lastDirection != RIGHT)
        direction = LEFT;
      else if (key == Event.RIGHT && lastDirection != LEFT && lastDirection != NONE)
        direction = RIGHT;
      else if (key == Event.UP && lastDirection != DOWN)
        direction = UP;
      else if (key == Event.DOWN && lastDirection != UP)
        direction = DOWN;
    }

    // 'M' key: toggle sound.

    if (key == 109) {
      if (sound) {
        bonkSound.stop();
        munchSound.stop();
        squeakSound.stop();
        chimeSound.stop();
        advanceSound.stop();
      }
      sound = !sound;
    }

    // 'P' key: toggle pause mode.

    if (key == 112)
      paused = !paused;

    // 'S' key: start the game, if not already in progress.

    if (loaded && key == 115 && (gameState == INIT || gameState == OVER))
      initGame();

    return true;
  }

  public void initBlocks() {

    int i, j;

    // Clear the grid.

    for (i = 0; i < GRID_WIDTH; i++)
      for (j = 0; j < GRID_HEIGHT; j++)
        grid[i][j] = EMPTY | NONE | SQUARE;

    // Add outer walls.

    for (i = 0; i < GRID_WIDTH; i++) {
      grid[i][0] = BLOCK | NONE | SQUARE;
      grid[i][GRID_HEIGHT - 1] = BLOCK | NONE | SQUARE;
    }
    for (j = 1; j < GRID_HEIGHT - 1; j++) {
      grid[0][j] = BLOCK | NONE | SQUARE;
      grid[GRID_WIDTH - 1][j] = BLOCK | NONE | SQUARE;
    }

    // Add inner walls depending on current level.

    if (level == 2 || level == 4) {
      i = GRID_WIDTH / 4;
      for (j = GRID_HEIGHT / 4; j <= GRID_HEIGHT / 2 - 3; j++) {
        grid[i][j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][j] = BLOCK | NONE | SQUARE;
        grid[i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
      }
      j = GRID_HEIGHT / 4;
      for (i = GRID_WIDTH / 4; i <= GRID_WIDTH / 2 - 3; i++) {
        grid[i][j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][j] = BLOCK | NONE | SQUARE;
        grid[i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
      }
    }

    if (level == 3) {
      i = GRID_WIDTH / 2;
      for (j = 0; j <= 3; j++) {
        grid[i][j] = BLOCK | NONE | SQUARE;
        grid[i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
      }
    }

    if (level == 3 || level == 5) {
      j = GRID_HEIGHT / 4;
      for (i = 5; i <= GRID_WIDTH / 4; i++) {
        grid[i][j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][j] = BLOCK | NONE | SQUARE;
        grid[i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
      }
      i = GRID_WIDTH / 4;
      for (j = GRID_HEIGHT / 4; j <= GRID_HEIGHT / 2; j++) {
        grid[i][j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][j] = BLOCK | NONE | SQUARE;
        grid[i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
      }
      j = GRID_HEIGHT / 2;
      for (i = 0; i <= GRID_WIDTH / 4 - 5; i++) {
        grid[i][j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][j] = BLOCK | NONE | SQUARE;
      }
    }

    if (level == 4) {
      i = 4;
      for (j = GRID_HEIGHT / 2 - 2; j <= GRID_HEIGHT / 2; j++) {
        grid[i][j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][j] = BLOCK | NONE | SQUARE;
        grid[i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
      }
      i = GRID_WIDTH / 2;
      for (j = 0; j <= 2; j++) {
        grid[i][j] = BLOCK | NONE | SQUARE;
        grid[i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
      }
    }

    if (level == 5) {
      i = 3 * GRID_WIDTH / 8;
      for (j = 4; j <= GRID_HEIGHT / 3 - 1; j++) {
        grid[i][j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][j] = BLOCK | NONE | SQUARE;
        grid[i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
      }
      j = GRID_HEIGHT / 3 - 1;
      for (i = 3 * GRID_WIDTH / 8; i <= GRID_WIDTH / 2; i++) {
        grid[i][j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][j] = BLOCK | NONE | SQUARE;
        grid[i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
        grid[GRID_WIDTH - 1 - i][GRID_HEIGHT - 1 - j] = BLOCK | NONE | SQUARE;
      }
    }
  }

  public void initSnake() {

    int i, j;
    int m, n;

    // Create a new snake in the center of the grid and reset cell list.

    i = GRID_WIDTH  / 2;
    j = GRID_HEIGHT / 2;

    snake[4].x = i - 2; snake[4].y = j;
    snake[3].x = i - 1; snake[3].y = j;
    snake[2].x = i - 0; snake[2].y = j;
    snake[1].x = i + 1; snake[1].y = j;
    snake[0].x = i + 2; snake[0].y = j;

    headPtr = 4;
    tailPtr = 0;

    grid[i - 2][j] = SNAKE | LEFT | SNAKEHEAD;
    grid[i - 1][j] = SNAKE | LEFT | SNAKEBODY;
    grid[i][j]     = SNAKE | LEFT | SNAKEBODY;
    grid[i + 1][j] = SNAKE | LEFT | SNAKEBODY;
    grid[i + 2][j] = SNAKE | LEFT | SNAKETAIL;

    // Clear snake direction.

    direction     = NONE;
    lastDirection = NONE;

    // Relocate any mice that were overwritten.

    for (m = 0; m < NUM_MICE; m++)
      for (n = -2; n <= 2 ; n++)
        if (mouse[m].x == i + n && mouse[m].y == j)
          initMouse(m);

    // Relocate key if overwritten.

    if (keyActive)
      for (n = -2; n <= 2 ; n++)
        if (key.x == i + n && key.y == j)
          initKey();
  }

  public void moveSnake() {

    Point pt;
    int i, j;
    int k;
    int c;
    int d;

    // Lock cursor keys to prevent the current direction from being changed
    // until we are done processing it.

    lockKeys = true;

    // Move snake's head into the next cell based on current direction.

    pt = snake[headPtr];
    i = pt.x;
    j = pt.y;
    switch (direction) {
      case LEFT:
        i--;
        break;
      case RIGHT:
        i++;
        break;
      case UP:
        j--;
        break;
      case DOWN:
        j++;
        break;
      default:
        lockKeys = false;
        return;
    }

    // Unlock cursor keys and save direction.

    lockKeys = false;
    lastDirection = direction;

    // Skip if no direction given.

    if (direction == NONE)
      return;

    // Get the type of the new cell.

    c = grid[i][j] & TYPE_MASK;

    // Check if we hit a wall or ourselves.

    if (c == BLOCK || c == SNAKE) {
      if (loaded && sound)
        bonkSound.play();
      endLife();
      return;
    }

    // Replace current head with the appropriate body part.

    d = grid[pt.x][pt.y] & DIR_MASK;
    if (d == direction)
      grid[pt.x][pt.y] = SNAKE | d | SNAKEBODY;
    else if (d == LEFT && direction == UP)
      grid[pt.x][pt.y] = SNAKE | direction | SNAKEELB1;
    else if (d == UP && direction == RIGHT)
      grid[pt.x][pt.y] = SNAKE | direction | SNAKEELB1;
    else if (d == RIGHT && direction == DOWN)
      grid[pt.x][pt.y] = SNAKE | direction | SNAKEELB1;
    else if (d == DOWN && direction == LEFT)
      grid[pt.x][pt.y] = SNAKE | direction | SNAKEELB1;
    else if (d == UP && direction == LEFT)
      grid[pt.x][pt.y] = SNAKE | direction | SNAKEELB2;
    else if (d == LEFT && direction == DOWN)
      grid[pt.x][pt.y] = SNAKE | direction | SNAKEELB2;
    else if (d == DOWN && direction == RIGHT)
      grid[pt.x][pt.y] = SNAKE | direction | SNAKEELB2;
    else if (d == RIGHT && direction == UP)
      grid[pt.x][pt.y] = SNAKE | direction | SNAKEELB2;

    // Change the new cell to a snake's head and set it in the cell list.

    grid[i][j] = SNAKE | direction | SNAKEHEAD;
    if (++headPtr >= snake.length)
      headPtr = 0;
    snake[headPtr].x = i;
    snake[headPtr].y = j;

    // If we got a mouse, bump the score and create a new one. Otherwise,
    // move the tail.

    if (c == MOUSE) {
      score += MOUSE_POINTS;
      if (loaded && sound)
        munchSound.play();
      for (k = 0; k < NUM_MICE; k++)
        if (mouse[k].x == i && mouse[k].y == j)
          initMouse(k);
      miceEaten++;
    }
    else {
      pt = snake[tailPtr];
      grid[pt.x][pt.y] = EMPTY & NONE & SQUARE;
      if (++tailPtr >= snake.length)
        tailPtr = 0;
      pt = snake[tailPtr];
      d = grid[pt.x][pt.y] & DIR_MASK;
      grid[pt.x][pt.y] = SNAKE | d | SNAKETAIL;
    }

    // Check if we got the key. If so, end the current level.

    if (c == KEY) {
      score += LEVEL_POINTS;
      if (loaded && sound)
        advanceSound.play();
      endLevel();
    }
  }

  public void killSnake() {

    Point pt;
    int   n;

    // Remove snake from the grid.

    n = headPtr + 1;
    if (n >= snake.length)
      n = 0;
    while(tailPtr != n) {
      pt = snake[tailPtr];
      grid[pt.x][pt.y] = EMPTY | NONE | SQUARE;
      if (++tailPtr >= snake.length)
        tailPtr = 0;
    }
  }

  public void initMouse(int n) {

    int i, j;
    int d;

    // Find an empty cell.

    do {
      i = (int) (Math.random() * GRID_WIDTH);
      j = (int) (Math.random() * GRID_HEIGHT);
    } while ((grid[i][j] & TYPE_MASK) != EMPTY);

    // Get a random direction.

    d = (int) (Math.random() * 4) + 1;
    d <<= 8;

    // Save mouse position.

    mouse[n].x = i;
    mouse[n].y = j;

    // Set the cell with mouse data.

    grid[i][j] = MOUSE | d | MOUSEBODY;
  }

  public void moveMouse(int n) {

    int i, j;
    int d;
    int m;

    // Skip move at random.

    if (Math.random() > 0.25)
      return;

    // Skip move if mouse is dead.

    if (mouse[n].x == -1 || mouse[n].y == -1)
      return;

    // Toss in a random squeak.

    if (loaded && sound && Math.random() > 0.975)
      squeakSound.play();

    // Get a random direction.

    d = (int) (Math.random() * 5);
    d <<= 8;

    // Don't allow a reversal of direction.

    m = grid[mouse[n].x][mouse[n].y] & DIR_MASK;
    if ((m == LEFT  && d == RIGHT) ||
        (m == RIGHT && d == LEFT)  ||
        (m == UP    && d == DOWN)  ||
        (m == DOWN  && d == UP))
      return;

    i = mouse[n].x;
    j = mouse[n].y;
    switch (d) {
      case LEFT:
        i--;
        break;
      case RIGHT:
        i++;
        break;
      case UP:
        j--;
        break;
      case DOWN:
        j++;
        break;
      default:
        return;
    }

    // See if the new cell is empty. If not, skip move.

    if ((grid[i][j] & TYPE_MASK) != EMPTY)
      return;

    // Clear mouse from old cell and move to the new one.

    grid[mouse[n].x][mouse[n].y] = EMPTY | NONE | SQUARE;
    mouse[n].x = i;
    mouse[n].y = j;
    grid[i][j] = MOUSE | d | MOUSEBODY;
  }

  public void killMouse(int n) {

    // Clear mouse from grid and set coordinates to (-1, -1) so we know it is
    // dead.

    grid[mouse[n].x][mouse[n].y] = EMPTY | NONE | SQUARE;
    mouse[n].x = -1;
    mouse[n].y = -1;
  }

  public void initKey() {

    int i, j;

    // Find an empty cell.

    do {
      i = (int) (Math.random() * GRID_WIDTH);
      j = (int) (Math.random() * GRID_HEIGHT);
    } while ((grid[i][j] & TYPE_MASK) != EMPTY);

    // Save key position.

    key.x = i;
    key.y = j;

    // Set the cell with key data and set the flag to show it's been added.

    grid[i][j] = KEY | NONE | KEYSHAPE;
    keyActive = true;
  }

  public Polygon translate(Polygon p, int dx, int dy) {

    Polygon polygon;
    int i;

    // Returns the polygon created by translating the given shape.

    polygon = new Polygon();
    for (i = 0; i < p.npoints; i++)
      polygon.addPoint(p.xpoints[i] + dx, p.ypoints[i] + dy);

    return polygon;
  }

  public Polygon rotate(Polygon p, int d) {

    Polygon polygon;
    int i;

    // Returns the polygon created by rotating the given shape in 90 deg. increments.

    polygon = new Polygon();
    for (i = 0; i < p.npoints; i++)
      switch (d) {
        case LEFT:
          polygon.addPoint(p.ypoints[i], (GRID_SIZE - 1) - p.xpoints[i]);
          break;
        case RIGHT:
          polygon.addPoint((GRID_SIZE - 1) - p.ypoints[i], p.xpoints[i]);
          break;
        case DOWN:
          polygon.addPoint((GRID_SIZE - 1) - p.xpoints[i], (GRID_SIZE - 1) - p.ypoints[i]);
          break;
        default:
          polygon.addPoint(p.xpoints[i], p.ypoints[i]);
          break;
      }

    return polygon;
  }

  public Polygon mirror(Polygon p) {

    Polygon polygon;
    int i;

    // Returns the polygon created by mirroring the given shape.

    polygon = new Polygon();
    for (i = 0; i < p.npoints; i++)
      polygon.addPoint((GRID_SIZE - 1) - p.xpoints[i], p.ypoints[i]);

    return polygon;
  }

  public Color fade(Color s, Color e, double pct) {

    int r, g, b;

    // Fade the starting color to the ending color by the given percentage.

    if (pct < 0.0)
      return e;
    if (pct > 1.0)
      return s;

    r = e.getRed() + (int) Math.round(pct * (s.getRed() - e.getRed()));
    g = e.getGreen() + (int) Math.round(pct * (s.getGreen() - e.getGreen()));
    b = e.getBlue() + (int) Math.round(pct * (s.getBlue() - e.getBlue()));

    return(new Color(r, g, b));
  }

  public void paint(Graphics g) {

    update(g);
  }

  public void update(Graphics g) {

    Dimension d = size();
    int width, height;
    int xOff, yOff;
    int i, j;
    int m;
    int n;
    Polygon p;
    String s;

    // Create the offscreen graphics context, if no good one exists.

    if (offGraphics == null || d.width != offDimension.width || d.height != offDimension.height) {
      offDimension = d;
      offImage = createImage(d.width, d.height);
      offGraphics = offImage.getGraphics();
    }

    // Fill in applet background.

    offGraphics.setColor(bgColor);
    offGraphics.fillRect(0, 0, d.width, d.height);

    // Center game area.

    width  = GRID_WIDTH  * GRID_SIZE;
    height = GRID_HEIGHT * GRID_SIZE;
    xOff = (d.width - width) / 2;
    yOff = (d.height - (height + 2 * fontHeight)) / 2;
    offGraphics.translate(xOff, yOff);

    // Fill in playing field.

    if (gameState == LEVEL)
      offGraphics.setColor(fade(fieldColor, bgColor, (double) levelCounter / (double) LEVEL_COUNT));
    else
      offGraphics.setColor(fieldColor);
    offGraphics.fillRect(0, 0, GRID_WIDTH * GRID_SIZE, GRID_HEIGHT * GRID_SIZE);

    // Fill in each grid cell with the appropriate shape.

    for (i = 0; i < GRID_WIDTH; i++)
      for (j = 0; j < GRID_HEIGHT; j++)
        switch (grid[i][j] & TYPE_MASK) {

          case EMPTY:
            break;

          case BLOCK:
            if (gameState == LEVEL)
              offGraphics.setColor(fade(blockColor, bgColor, (double) levelCounter / (double) LEVEL_COUNT));
            else
              offGraphics.setColor(blockColor);
            offGraphics.fillRect(i * GRID_SIZE, j * GRID_SIZE, GRID_SIZE, GRID_SIZE);
            offGraphics.setColor(bgColor);
            offGraphics.drawRect(i * GRID_SIZE, j * GRID_SIZE, GRID_SIZE, GRID_SIZE);
            break;

          case SNAKE:
            n = grid[i][j] & SHAPE_MASK;
            switch (n) {
              case SNAKEHEAD:
                p = snakeHead;
                break;
              case SNAKEBODY:
                p = snakeBody;
                break;
              case SNAKEELB1:
                p = snakeElb1;
                break;
              case SNAKEELB2:
                p = snakeElb2;
                break;
              case SNAKETAIL:
                p = snakeTail;
                break;
              default:
                p = snakeHead;
                break;
            }
            p = translate(rotate(p, grid[i][j] & DIR_MASK), i * GRID_SIZE, j * GRID_SIZE);
            if (gameState == LEVEL)
              offGraphics.setColor(fade(snakeColor, bgColor, (double) levelCounter / (double) LEVEL_COUNT));
            else if (gameState == END || gameState == OVER)
              offGraphics.setColor(fade(snakeColor, fieldColor, (double) endCounter / (double) END_COUNT));
            else
              offGraphics.setColor(snakeColor);
            offGraphics.fillPolygon(p);
            offGraphics.drawPolygon(p);
            offGraphics.drawLine(p.xpoints[p.npoints - 1], p.ypoints[p.npoints - 1], p.xpoints[0], p.ypoints[0]);
            if (gameState == END || gameState == OVER)
              offGraphics.setColor(fade(bgColor, fieldColor, (double) endCounter / (double) END_COUNT));
            else
              offGraphics.setColor(bgColor);
            if (n == SNAKEHEAD || n == SNAKETAIL)
              for (m = 0; m < p.npoints - 1; m++)
                offGraphics.drawLine(p.xpoints[m], p.ypoints[m], p.xpoints[m + 1], p.ypoints[m + 1]);
            if (n == SNAKEBODY) {
              offGraphics.drawLine(p.xpoints[0], p.ypoints[0], p.xpoints[1], p.ypoints[1]);
              offGraphics.drawLine(p.xpoints[2], p.ypoints[2], p.xpoints[3], p.ypoints[3]);
            }
            if (n == SNAKEELB1 || n == SNAKEELB2) {
              offGraphics.drawLine(p.xpoints[0], p.ypoints[0], p.xpoints[1], p.ypoints[1]);
              offGraphics.drawLine(p.xpoints[1], p.ypoints[1], p.xpoints[2], p.ypoints[2]);
              offGraphics.drawLine(p.xpoints[3], p.ypoints[3], p.xpoints[4], p.ypoints[4]);
              offGraphics.drawLine(p.xpoints[4], p.ypoints[4], p.xpoints[5], p.ypoints[5]);
              offGraphics.drawLine(p.xpoints[5], p.ypoints[5], p.xpoints[6], p.ypoints[6]);
            }
            break;

          case MOUSE:
            if (gameState == LEVEL)
              offGraphics.setColor(fade(mouseColor, bgColor, (double) levelCounter / (double) LEVEL_COUNT));
            else
              offGraphics.setColor(mouseColor);
            p = translate(rotate(mouseBody, grid[i][j] & DIR_MASK), i * GRID_SIZE, j * GRID_SIZE);
            offGraphics.fillPolygon(p);
            offGraphics.setColor(bgColor);
            offGraphics.drawPolygon(p);
            offGraphics.drawLine(p.xpoints[p.npoints - 1], p.ypoints[p.npoints - 1], p.xpoints[0], p.ypoints[0]);
            break;

          case KEY:
            p = translate(rotate(keyShape, grid[i][j] & DIR_MASK), i * GRID_SIZE, j * GRID_SIZE);
            offGraphics.setColor(keyColor);
            offGraphics.fillPolygon(p);
            offGraphics.setColor(bgColor);
            offGraphics.drawPolygon(p);
            offGraphics.drawLine(p.xpoints[p.npoints - 1], p.ypoints[p.npoints - 1], p.xpoints[0], p.ypoints[0]);
            break;

          default:
            break;
        }

    // Outline playing field.

    offGraphics.setColor(fieldColor);
    offGraphics.drawRect(0, 0, GRID_WIDTH * GRID_SIZE, GRID_HEIGHT * GRID_SIZE - 1);

    // Display status and messages.

    offGraphics.setFont(font);

    offGraphics.setColor(fieldColor);
    i = height - 1;
    j = height + 3 * fontHeight / 2;
    offGraphics.drawRect(0, i, width, 2 * fontHeight);

    offGraphics.setColor(fgColor);
    s = "Score: " + score;
    offGraphics.drawString(s, fontWidth, j);
    s = "Level: " + levelTotal;
    offGraphics.drawString(s, (width - fm.stringWidth(s)) / 4, j);
    s = "Lives: " + lives;
    offGraphics.drawString(s, 3 * (width - fm.stringWidth(s)) / 4, j);
    s = "High: " + highScore;
    offGraphics.drawString(s, width - (fontWidth + fm.stringWidth(s)), j);
    if (paused) {
      s = "Paused";
      offGraphics.drawString(s, (width - fm.stringWidth(s)) / 2, j);
    }
    else if (!sound) {
      s = "Muted";
      offGraphics.drawString(s, (width - fm.stringWidth(s)) / 2, j);
    }

    if (gameState == INIT || gameState == OVER) {
      offGraphics.setColor(bgColor);
      s = "Snake Pit";
      offGraphics.drawString(s, (width - fm.stringWidth(s)) / 2 + 1, height / 3 + fontHeight + 1);
      s = "Copyright 1998 by Mike Hall";
      offGraphics.drawString(s, (width - fm.stringWidth(s)) / 2 + 1, height / 3 + 2 * fontHeight + 1);
      offGraphics.setColor(fgColor);
      s = "Snake Pit";
      offGraphics.drawString(s, (width - fm.stringWidth(s)) / 2, height / 3 + fontHeight);
      s = "Copyright 1998 by Mike Hall";
      offGraphics.drawString(s, (width - fm.stringWidth(s)) / 2, height / 3 + 2 * fontHeight);
      offGraphics.setColor(fgColor);
      if (!loaded)
        s = "Loading sounds...";
      else
      s = "Game Over - 'S' to Start";
      offGraphics.setColor(bgColor);
      offGraphics.drawString(s, (width - fm.stringWidth(s)) / 2 + 1, 2 * height / 3 + 1);
      offGraphics.setColor(fgColor);
      offGraphics.drawString(s, (width - fm.stringWidth(s)) / 2, 2 * height / 3);
    }
    if (gameState == LEVEL) {
      s = "Advancing to Level " + (levelTotal + 1);
      offGraphics.setColor(bgColor);
      offGraphics.drawString(s, (width - fm.stringWidth(s)) / 2 + 1, height / 2 + 1);
      offGraphics.setColor(fgColor);
      offGraphics.drawString(s, (width - fm.stringWidth(s)) / 2, height / 2);
    }

    // Copy the off screen buffer to the screen.

    offGraphics.translate(-xOff, -yOff);
    g.drawImage(offImage, 0, 0, this);
  }
}


Back to the SnakePit applet page

How to Add Java Applets to Your Site

New on the Java Boutique:

New Review:

Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling API boasts simplicity, ease-of-integration, a well-rounded feature set, and it's free!

New Applet:

Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA sequences into three useful formats.

Elsewhere on internet.com:

WebDeveloper Java
Lots of Java information on webdeveloper.com

WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.

ScriptSearch Java
Hundreds of free Java code files to download.

jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.

 DevX Skillbuilding from IBM developerWorks
 RIA Run Contest: Build Next-Gen Apps in Microsoft Silverlight 2
 Avaya DevConnect Center
 Intel Go Parallel Portal
 Internet.com eBook Library
 Microsoft RIA Development Center
 Destination .NET
XML error: not well-formed (invalid token) at line 53
advertisement
Receive Articles via our XML/RSS feed
Receive Articles via our XML/RSS feed

JavaBytes
Internet Cyclone
This powerful, easy-to-use, internet optimizer is for Windows 95, 98, ME, NT, 2000 and XP. It's designed to automatically optimize your Windows settings, boosting your Internet connection up to 200%.

RIM Ups Ante With Mobile Software Push
Novell Readies Silverlight Clone for Linux
Yahoo Pitches The 'Next Generation of Search'
Alfresco's Latest ECM: Prying Open a Sector?
SaaS Tool Offers Custom Database Development
Microsoft’s Automated Agent: Can We Talk?
Borland Finally Sells CodeGear
Red Hat Heads for the JON 2.0
Out with the Old, in with the New at JavaOne
Trolltech Expands WebKit Footprint

Create Secure Java Applications Productively, Part 1: Use Rational Application Developer and Data Studio
.NET Building Blocks: Custom User Control Fundamentals
Secure Internet File-Sharing with PHP, MySQL, and JavaScript
Getting Started with TBB on Windows
Moving to VoIP: Should You Go It Alone?
Introduction to the WPF Command Framework
7.0, Microsoft's Lucky Version?
Will Hyper-V Make VMware This Decade's Netscape?
Eliminate Fragmentation Frustration with Netbiscuits
Taming Trees: Building Branching Structures

Advertising Info  |   Member Services  |   Contact Us  |   Help  |   Feedback  |   Site Map  |   Network Map  |   About



JupiterOnlineMedia

internet.comearthweb.comDevx.commediabistro.comGraphics.com

Search:

Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

Jupitermedia Corporate Info


Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers

Solutions
Whitepapers and eBooks
Microsoft Article: Will Hyper-V Make VMware This Decade's Netscape?
Microsoft Article: 7.0, Microsoft's Lucky Version?
Microsoft Article: Hyper-V--The Killer Feature in Windows Server 2008
Avaya Article: How to Feed Data into the Avaya Event Processor
Microsoft Article: Install What You Need with Windows Server 2008
HP eBook: Putting the Green into IT
Whitepaper: HP Integrated Citrix XenServer for HP ProLiant Servers
Intel Go Parallel Portal: Interview with C++ Guru Herb Sutter, Part 1
Intel Go Parallel Portal: Interview with C++ Guru Herb Sutter, Part 2--The Future of Concurrency
Avaya Article: Setting Up a SIP A/S Development Environment
IBM Article: How Cool Is Your Data Center?
Microsoft Article: Managing Virtual Machines with Microsoft System Center
HP eBook: Storage Networking , Part 1
Microsoft Article: Solving Data Center Complexity with Microsoft System Center Configuration Manager 2007
MORE WHITEPAPERS, EBOOKS, AND ARTICLES
Webcasts
Intel Video: Are Multi-core Processors Here to Stay?
On-Demand Webcast: Five Virtualization Trends to Watch
HP Video: Page Cost Calculator
Intel Video: APIs for Parallel Programming
HP Webcast: Storage Is Changing Fast - Be Ready or Be Left Behind
Microsoft Silverlight Video: Creating Fading Controls with Expression Design and Expression Blend 2
MORE WEBCASTS, PODCASTS, AND VIDEOS
Downloads and eKits
Sun Download: Solaris 8 Migration Assistant
Sybase Download: SQL Anywhere Developer Edition
Red Gate Download: SQL Backup Pro and free DBA Best Practices eBook
Red Gate Download: SQL Compare Pro 6
Iron Speed Designer Application Generator
MORE DOWNLOADS, EKITS, AND FREE TRIALS
Tutorials and Demos
How-to-Article: Preparing for Hyper-Threading Technology and Dual Core Technology
eTouch PDF: Conquering the Tyranny of E-Mail and Word Processors
IBM Article: Collaborating in the High-Performance Workplace
HP Demo: StorageWorks EVA4400
Intel Featured Algorhythm: Intel Threading Building Blocks--The Pipeline Class
Microsoft How-to Article: Get Going with Silverlight and Windows Live
MORE TUTORIALS, DEMOS AND STEP-BY-STEP GUIDES