Game Android

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package app.test;
import java.util.ArrayList;
import java.util.Random;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.TextView;
/**
 * TileView: a View-variant designed for handling arrays of "icons" or other
 * drawables.
 * 
 */
class TileView extends View {
  /**
   * Parameters controlling the size of the tiles and their range within view.
   * Width/Height are in pixels, and Drawables will be scaled to fit to these
   * dimensions. X/Y Tile Counts are the number of tiles that will be drawn.
   */
  protected static int mTileSize;
  protected static int mXTileCount;
  protected static int mYTileCount;
  private static int mXOffset;
  private static int mYOffset;
  /**
   * A hash that maps integer handles specified by the subclasser to the
   * drawable that will be used for that reference
   */
  private Bitmap[] mTileArray;
  /**
   * A two-dimensional array of integers in which the number represents the
   * index of the tile that should be drawn at that locations
   */
  private int[][] mTileGrid;
  private final Paint mPaint = new Paint();
  public TileView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    TypedArray a = context.obtainStyledAttributes(attrs,
        R.styleable.TileView);
    mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
    a.recycle();
  }
  public TileView(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray a = context.obtainStyledAttributes(attrs,
        R.styleable.TileView);
    mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
    a.recycle();
  }
  /**
   * Rests the internal array of Bitmaps used for drawing tiles, and sets the
   * maximum index of tiles to be inserted
   * 
   * @param tilecount
   */
  public void resetTiles(int tilecount) {
    mTileArray = new Bitmap[tilecount];
  }
  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    mXTileCount = (int) Math.floor(w / mTileSize);
    mYTileCount = (int) Math.floor(h / mTileSize);
    mXOffset = ((w - (mTileSize * mXTileCount)) / 2);
    mYOffset = ((h - (mTileSize * mYTileCount)) / 2);
    mTileGrid = new int[mXTileCount][mYTileCount];
    clearTiles();
  }
  /**
   * Function to set the specified Drawable as the tile for a particular
   * integer key.
   * 
   * @param key
   * @param tile
   */
  public void loadTile(int key, Drawable tile) {
    Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize,
        Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    tile.setBounds(0, 0, mTileSize, mTileSize);
    tile.draw(canvas);
    mTileArray[key] = bitmap;
  }
  /**
   * Resets all tiles to 0 (empty)
   * 
   */
  public void clearTiles() {
    for (int x = 0; x < mXTileCount; x++) {
      for (int y = 0; y < mYTileCount; y++) {
        setTile(0, x, y);
      }
    }
  }
  /**
   * Used to indicate that a particular tile (set with loadTile and referenced
   * by an integer) should be drawn at the given x/y coordinates during the
   * next invalidate/draw cycle.
   * 
   * @param tileindex
   * @param x
   * @param y
   */
  public void setTile(int tileindex, int x, int y) {
    mTileGrid[x][y] = tileindex;
  }
  @Override
  public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    for (int x = 0; x < mXTileCount; x += 1) {
      for (int y = 0; y < mYTileCount; y += 1) {
        if (mTileGrid[x][y] > 0) {
          canvas.drawBitmap(mTileArray[mTileGrid[x][y]], mXOffset + x
              * mTileSize, mYOffset + y * mTileSize, mPaint);
        }
      }
    }
  }
}
/**
 * SnakeView: implementation of a simple game of Snake
 * 
 * 
 */
class SnakeView extends TileView {
  private static final String TAG = "SnakeView";
  /**
   * Current mode of application: READY to run, RUNNING, or you have already
   * lost. static final ints are used instead of an enum for performance
   * reasons.
   */
  private int mMode = READY;
  public static final int PAUSE = 0;
  public static final int READY = 1;
  public static final int RUNNING = 2;
  public static final int LOSE = 3;
  /**
   * Current direction the snake is headed.
   */
  private int mDirection = NORTH;
  private int mNextDirection = NORTH;
  private static final int NORTH = 1;
  private static final int SOUTH = 2;
  private static final int EAST = 3;
  private static final int WEST = 4;
  /**
   * Labels for the drawables that will be loaded into the TileView class
   */
  private static final int RED_STAR = 1;
  private static final int YELLOW_STAR = 2;
  private static final int GREEN_STAR = 3;
  /**
   * mScore: used to track the number of apples captured mMoveDelay: number of
   * milliseconds between snake movements. This will decrease as apples are
   * captured.
   */
  private long mScore = 0;
  private long mMoveDelay = 600;
  /**
   * mLastMove: tracks the absolute time when the snake last moved, and is
   * used to determine if a move should be made based on mMoveDelay.
   */
  private long mLastMove;
  /**
   * mStatusText: text shows to the user in some run states
   */
  private TextView mStatusText;
  /**
   * mSnakeTrail: a list of Coordinates that make up the snake's body
   * mAppleList: the secret location of the juicy apples the snake craves.
   */
  private ArrayList mSnakeTrail = new ArrayList();
  private ArrayList mAppleList = new ArrayList();
  /**
   * Everyone needs a little randomness in their life
   */
  private static final Random RNG = new Random();
  /**
   * Create a simple handler that we can use to cause animation to happen. We
   * set ourselves as a target and we can use the sleep() function to cause an
   * update/invalidate to occur at a later date.
   */
  private RefreshHandler mRedrawHandler = new RefreshHandler();
  class RefreshHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
      SnakeView.this.update();
      SnakeView.this.invalidate();
    }
    public void sleep(long delayMillis) {
      this.removeMessages(0);
      sendMessageDelayed(obtainMessage(0), delayMillis);
    }
  };
  /**
   * Constructs a SnakeView based on inflation from XML
   * 
   * @param context
   * @param attrs
   */
  public SnakeView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initSnakeView();
  }
  public SnakeView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    initSnakeView();
  }
  private void initSnakeView() {
    setFocusable(true);
    Resources r = this.getContext().getResources();
    resetTiles(4);
    loadTile(RED_STAR, r.getDrawable(R.drawable.redstar));
    loadTile(YELLOW_STAR, r.getDrawable(R.drawable.yellowstar));
    loadTile(GREEN_STAR, r.getDrawable(R.drawable.greenstar));
  }
  private void initNewGame() {
    mSnakeTrail.clear();
    mAppleList.clear();
    // For now we're just going to load up a short default eastbound snake
    // that's just turned north
    mSnakeTrail.add(new Coordinate(7, 7));
    mSnakeTrail.add(new Coordinate(6, 7));
    mSnakeTrail.add(new Coordinate(5, 7));
    mSnakeTrail.add(new Coordinate(4, 7));
    mSnakeTrail.add(new Coordinate(3, 7));
    mSnakeTrail.add(new Coordinate(2, 7));
    mNextDirection = NORTH;
    // Two apples to start with
    addRandomApple();
    addRandomApple();
    mMoveDelay = 600;
    mScore = 0;
  }
  /**
   * Given a ArrayList of coordinates, we need to flatten them into an array
   * of ints before we can stuff them into a map for flattening and storage.
   * 
   * @param cvec
   *            : a ArrayList of Coordinate objects
   * @return : a simple array containing the x/y values of the coordinates as
   *         [x1,y1,x2,y2,x3,y3...]
   */
  private int[] coordArrayListToArray(ArrayList cvec) {
    int count = cvec.size();
    int[] rawArray = new int[count * 2];
    for (int index = 0; index < count; index++) {
      Coordinate c = cvec.get(index);
      rawArray[2 * index] = c.x;
      rawArray[2 * index + 1] = c.y;
    }
    return rawArray;
  }
  /**
   * Save game state so that the user does not lose anything if the game
   * process is killed while we are in the background.
   * 
   * @return a Bundle with this view's state
   */
  public Bundle saveState() {
    Bundle map = new Bundle();
    map.putIntArray("mAppleList", coordArrayListToArray(mAppleList));
    map.putInt("mDirection", Integer.valueOf(mDirection));
    map.putInt("mNextDirection", Integer.valueOf(mNextDirection));
    map.putLong("mMoveDelay", Long.valueOf(mMoveDelay));
    map.putLong("mScore", Long.valueOf(mScore));
    map.putIntArray("mSnakeTrail", coordArrayListToArray(mSnakeTrail));
    return map;
  }
  /**
   * Given a flattened array of ordinate pairs, we reconstitute them into a
   * ArrayList of Coordinate objects
   * 
   * @param rawArray
   *            : [x1,y1,x2,y2,...]
   * @return a ArrayList of Coordinates
   */
  private ArrayList coordArrayToArrayList(int[] rawArray) {
    ArrayList coordArrayList = new ArrayList();
    int coordCount = rawArray.length;
    for (int index = 0; index < coordCount; index += 2) {
      Coordinate c = new Coordinate(rawArray[index], rawArray[index + 1]);
      coordArrayList.add(c);
    }
    return coordArrayList;
  }
  /**
   * Restore game state if our process is being relaunched
   * 
   * @param icicle
   *            a Bundle containing the game state
   */
  public void restoreState(Bundle icicle) {
    setMode(PAUSE);
    mAppleList = coordArrayToArrayList(icicle.getIntArray("mAppleList"));
    mDirection = icicle.getInt("mDirection");
    mNextDirection = icicle.getInt("mNextDirection");
    mMoveDelay = icicle.getLong("mMoveDelay");
    mScore = icicle.getLong("mScore");
    mSnakeTrail = coordArrayToArrayList(icicle.getIntArray("mSnakeTrail"));
  }
  /*
   * handles key events in the game. Update the direction our snake is
   * traveling based on the DPAD. Ignore events that would cause the snake to
   * immediately turn back on itself.
   * 
   * (non-Javadoc)
   * 
   * @see android.view.View#onKeyDown(int, android.os.KeyEvent)
   */
  @Override
  public boolean onKeyDown(int keyCode, KeyEvent msg) {
    if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
      if (mMode == READY | mMode == LOSE) {
        /*
         * At the beginning of the game, or the end of a previous one,
         * we should start a new game.
         */
        initNewGame();
        setMode(RUNNING);
        update();
        return (true);
      }
      if (mMode == PAUSE) {
        /*
         * If the game is merely paused, we should just continue where
         * we left off.
         */
        setMode(RUNNING);
        update();
        return (true);
      }
      if (mDirection != SOUTH) {
        mNextDirection = NORTH;
      }
      return (true);
    }
    if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
      if (mDirection != NORTH) {
        mNextDirection = SOUTH;
      }
      return (true);
    }
    if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
      if (mDirection != EAST) {
        mNextDirection = WEST;
      }
      return (true);
    }
    if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
      if (mDirection != WEST) {
        mNextDirection = EAST;
      }
      return (true);
    }
    return super.onKeyDown(keyCode, msg);
  }
  /**
   * Sets the TextView that will be used to give information (such as "Game
   * Over" to the user.
   * 
   * @param newView
   */
  public void setTextView(TextView newView) {
    mStatusText = newView;
  }
  /**
   * Updates the current mode of the application (RUNNING or PAUSED or the
   * like) as well as sets the visibility of textview for notification
   * 
   * @param newMode
   */
  public void setMode(int newMode) {
    int oldMode = mMode;
    mMode = newMode;
    if (newMode == RUNNING & oldMode != RUNNING) {
      mStatusText.setVisibility(View.INVISIBLE);
      update();
      return;
    }
    Resources res = getContext().getResources();
    CharSequence str = "";
    if (newMode == PAUSE) {
      str = res.getText(R.string.mode_pause);
    }
    if (newMode == READY) {
      str = res.getText(R.string.mode_ready);
    }
    if (newMode == LOSE) {
      str = res.getString(R.string.mode_lose_prefix) + mScore
          + res.getString(R.string.mode_lose_suffix);
    }
    mStatusText.setText(str);
    mStatusText.setVisibility(View.VISIBLE);
  }
  /**
   * Selects a random location within the garden that is not currently covered
   * by the snake. Currently _could_ go into an infinite loop if the snake
   * currently fills the garden, but we'll leave discovery of this prize to a
   * truly excellent snake-player.
   * 
   */
  private void addRandomApple() {
    Coordinate newCoord = null;
    boolean found = false;
    while (!found) {
      // Choose a new location for our apple
      int newX = 1 + RNG.nextInt(mXTileCount - 2);
      int newY = 1 + RNG.nextInt(mYTileCount - 2);
      newCoord = new Coordinate(newX, newY);
      // Make sure it's not already under the snake
      boolean collision = false;
      int snakelength = mSnakeTrail.size();
      for (int index = 0; index < snakelength; index++) {
        if (mSnakeTrail.get(index).equals(newCoord)) {
          collision = true;
        }
      }
      // if we're here and there's been no collision, then we have
      // a good location for an apple. Otherwise, we'll circle back
      // and try again
      found = !collision;
    }
    if (newCoord == null) {
      Log.e(TAG, "Somehow ended up with a null newCoord!");
    }
    mAppleList.add(newCoord);
  }
  /**
   * Handles the basic update loop, checking to see if we are in the running
   * state, determining if a move should be made, updating the snake's
   * location.
   */
  public void update() {
    if (mMode == RUNNING) {
      long now = System.currentTimeMillis();
      if (now - mLastMove > mMoveDelay) {
        clearTiles();
        updateWalls();
        updateSnake();
        updateApples();
        mLastMove = now;
      }
      mRedrawHandler.sleep(mMoveDelay);
    }
  }
  /**
   * Draws some walls.
   * 
   */
  private void updateWalls() {
    for (int x = 0; x < mXTileCount; x++) {
      setTile(GREEN_STAR, x, 0);
      setTile(GREEN_STAR, x, mYTileCount - 1);
    }
    for (int y = 1; y < mYTileCount - 1; y++) {
      setTile(GREEN_STAR, 0, y);
      setTile(GREEN_STAR, mXTileCount - 1, y);
    }
  }
  /**
   * Draws some apples.
   * 
   */
  private void updateApples() {
    for (Coordinate c : mAppleList) {
      setTile(YELLOW_STAR, c.x, c.y);
    }
  }
  /**
   * Figure out which way the snake is going, see if he's run into anything
   * (the walls, himself, or an apple). If he's not going to die, we then add
   * to the front and subtract from the rear in order to simulate motion. If
   * we want to grow him, we don't subtract from the rear.
   * 
   */
  private void updateSnake() {
    boolean growSnake = false;
    // grab the snake by the head
    Coordinate head = mSnakeTrail.get(0);
    Coordinate newHead = new Coordinate(1, 1);
    mDirection = mNextDirection;
    switch (mDirection) {
    case EAST: {
      newHead = new Coordinate(head.x + 1, head.y);
      break;
    }
    case WEST: {
      newHead = new Coordinate(head.x - 1, head.y);
      break;
    }
    case NORTH: {
      newHead = new Coordinate(head.x, head.y - 1);
      break;
    }
    case SOUTH: {
      newHead = new Coordinate(head.x, head.y + 1);
      break;
    }
    }
    // Collision detection
    // For now we have a 1-square wall around the entire arena
    if ((newHead.x < 1) || (newHead.y < 1) || (newHead.x > mXTileCount - 2)
        || (newHead.y > mYTileCount - 2)) {
      setMode(LOSE);
      return;
    }
    // Look for collisions with itself
    int snakelength = mSnakeTrail.size();
    for (int snakeindex = 0; snakeindex < snakelength; snakeindex++) {
      Coordinate c = mSnakeTrail.get(snakeindex);
      if (c.equals(newHead)) {
        setMode(LOSE);
        return;
      }
    }
    // Look for apples
    int applecount = mAppleList.size();
    for (int appleindex = 0; appleindex < applecount; appleindex++) {
      Coordinate c = mAppleList.get(appleindex);
      if (c.equals(newHead)) {
        mAppleList.remove(c);
        addRandomApple();
        mScore++;
        mMoveDelay *= 0.9;
        growSnake = true;
      }
    }
    // push a new head onto the ArrayList and pull off the tail
    mSnakeTrail.add(0, newHead);
    // except if we want the snake to grow
    if (!growSnake) {
      mSnakeTrail.remove(mSnakeTrail.size() - 1);
    }
    int index = 0;
    for (Coordinate c : mSnakeTrail) {
      if (index == 0) {
        setTile(YELLOW_STAR, c.x, c.y);
      } else {
        setTile(RED_STAR, c.x, c.y);
      }
      index++;
    }
  }
  /**
   * Simple class containing two integer values and a comparison function.
   * There's probably something I should use instead, but this was quick and
   * easy to build.
   * 
   */
  private class Coordinate {
    public int x;
    public int y;
    public Coordinate(int newX, int newY) {
      x = newX;
      y = newY;
    }
    public boolean equals(Coordinate other) {
      if (x == other.x && y == other.y) {
        return true;
      }
      return false;
    }
    @Override
    public String toString() {
      return "Coordinate: [" + x + "," + y + "]";
    }
  }
}
/**
 * Snake: a simple game that everyone can enjoy.
 * 
 * This is an implementation of the classic Game "Snake", in which you control a
 * serpent roaming around the garden looking for apples. Be careful, though,
 * because when you catch one, not only will you become longer, but you'll move
 * faster. Running into yourself or the walls will end the game.
 * 
 */
public class Snake extends Activity {
  private SnakeView mSnakeView;
  private static String ICICLE_KEY = "snake-view";
  /**
   * Called when Activity is first created. Turns off the title bar, sets up
   * the content views, and fires up the SnakeView.
   * 
   */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.snake_layout);
    mSnakeView = (SnakeView) findViewById(R.id.snake);
    mSnakeView.setTextView((TextView) findViewById(R.id.text));
    if (savedInstanceState == null) {
      // We were just launched -- set up a new game
      mSnakeView.setMode(SnakeView.READY);
    } else {
      // We are being restored
      Bundle map = savedInstanceState.getBundle(ICICLE_KEY);
      if (map != null) {
        mSnakeView.restoreState(map);
      } else {
        mSnakeView.setMode(SnakeView.PAUSE);
      }
    }
  }
  @Override
  protected void onPause() {
    super.onPause();
    // Pause the game along with the activity
    mSnakeView.setMode(SnakeView.PAUSE);
  }
  @Override
  public void onSaveInstanceState(Bundle outState) {
    // Store the game state
    outState.putBundle(ICICLE_KEY, mSnakeView.saveState());
  }
}
//res\layout\snake_layout.xml


  android:layout_width="match_parent"
  android:layout_height="match_parent">
  
     android:id="@+id/snake"
    android:layout_width="match_parent"
                android:layout_height="match_parent"
                tileSize="24"
                />
  
      android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
         android:id="@+id/text"
      android:text="@string/snake_layout_text_text"
      android:visibility="visible"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_centerInParent="true"
      android:gravity="center_horizontal"
      android:textColor="#ff8888ff"
      android:textSize="24sp"/>
  

//res\values\attrs.xml



  
    
  

//res\values\strings.xml



  Snake\nPress Up To Play
  Paused\nPress Up To Resume
  Game Over\nScore: 
  \nPress Up To Play