Once less with feeling

We often get told the right thing to do which is expedient but it’s not very educational. Why is it the right thing to do? What if the person instructing you is wrong? Sometimes an illuminating method of finding the right solution is to establish a clear idea of the wrong thing to do, then run in the opposite direction as hard as you can.

I’ll post some code. You guys get your trainers on.

//Board.java
@Override 
public void onDraw(Canvas canvas) { 
		
	drawBoard(canvas);
		
	drawPieces(canvas);

}

private void drawBoard(Canvas canvas) {
	Paint light = new Paint(); 
	light.setColor(Color.parseColor("#FFFFAA")); 
	
	Paint dark = new Paint(); 
	dark.setColor(Color.parseColor("#AAAA88")); 
	
	for (int i = 0; i < 8 ; i++) { 
		for (int j = 0; j < 8 ; j++) { 
			if ((i+j) % 2 == 0) 
				canvas.drawRect(j * mTileWidth , i * mTileHeight , (j+1) * mTileWidth , (i+1) * mTileHeight, dark); 
			else
				canvas.drawRect(j * mTileWidth , i * mTileHeight , (j+1) * mTileWidth , (i+1) * mTileHeight, light);
		} 
	} 
}

private void drawPieces(Canvas canvas) {
	Log.i("Board", String.valueOf(Calendar.getInstance().getTimeInMillis())); 
	Resources res = getResources();
	for (int i = 0 ; i < mPieces.length ; i++) { 
		for (int j = 0 ; j < mPieces[i].length ; j++) { 
			if (mPieces[i][j] != 0) { 
				Bitmap bm = BitmapFactory.decodeResource(res, mPieces[i][j]); 
				bm = Bitmap.createScaledBitmap(bm, (int)mTileWidth, (int)mTileHeight, true); 
				canvas.drawBitmap(bm, i*mTileWidth, j*mTileHeight, null); 
			} 
		}
	} 
	Log.i("Board", String.valueOf(Calendar.getInstance().getTimeInMillis())); 
}
	
private int[][] mPieces = new int[8][8]; 
	
public void addPiece(int type, int x, int y) { 
	mPieces[x][y] = type; 
}
//RPSChess.java
package uk.co.quarterstaff.rpschess;

import android.app.Activity;
import android.os.Bundle;

public class RpsChess extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        getBoard();
        layoutBoard();
    }
    
    Board b = null;
    
    private void getBoard() {
    	b = (Board)findViewById(R.id.gameboard);
    }
    
    private void layoutBoard() {
        
        b.addPiece(R.drawable.whiterock, 0, 0);
        b.addPiece(R.drawable.whiterock, 2, 0);
        b.addPiece(R.drawable.whiterock, 4, 0);
        b.addPiece(R.drawable.whiterock, 6, 0);
        b.addPiece(R.drawable.whiterock, 1, 1);
        b.addPiece(R.drawable.whiterock, 3, 1);
        b.addPiece(R.drawable.whiterock, 5, 1);
        b.addPiece(R.drawable.whiterock, 7, 1);
        b.addPiece(R.drawable.whiterock, 0, 2);
        b.addPiece(R.drawable.whiterock, 2, 2);
        b.addPiece(R.drawable.whiterock, 4, 2);
        b.addPiece(R.drawable.whiterock, 6, 2);

        b.addPiece(R.drawable.blackrock, 1, 7);
        b.addPiece(R.drawable.blackrock, 3, 7);
        b.addPiece(R.drawable.blackrock, 5, 7);
        b.addPiece(R.drawable.blackrock, 7, 7);
        b.addPiece(R.drawable.blackrock, 0, 6);
        b.addPiece(R.drawable.blackrock, 2, 6);
        b.addPiece(R.drawable.blackrock, 4, 6);
        b.addPiece(R.drawable.blackrock, 6, 6);
        b.addPiece(R.drawable.blackrock, 1, 5);
        b.addPiece(R.drawable.blackrock, 3, 5);
        b.addPiece(R.drawable.blackrock, 5, 5);
        b.addPiece(R.drawable.blackrock, 7, 5);
    }
}

I’ve decided to deviate from the plan slightly and arrange a game of draughts on the board. 12 red rocks and 12 blue rocks in the traditional pattern. Half the number of pieces we will have at the start of a proper game of RPS Chess.

The part I am interested in is the Log.i() statements. These will put message into the log of the running process. Obviously there are some what-ifs regarding precision timing here, but in my tests I get 1905 milliseconds to draw the pieces without the board. Raw frame rate might not be important for a board game (it is, we’ll come to that again later), but clearly something is up. Even if we can wait that long, we’re stealing CPU from something else that could be doing better things with it.

Our basic problem is we are decoding and rescaling the same image twelve times over. We will improve the efficiency here by caching the scaled images and reusing them.

//Board.java
private HashMap<Integer, Bitmap> mBitmapCache = new HashMap<Integer, Bitmap>();  
protected Bitmap getBitmap(int id) { 
  Bitmap bm = mBitmapCache.get(id); 
  //Create and cache it if it doesn't exist already 
  if (bm == null) { 
    Resources res = getResources();
    bm = BitmapFactory.decodeResource(res, id);
    bm = Bitmap.createScaledBitmap(bm, (int)mTileWidth, (int)mTileHeight, true);
    mBitmapCache.put(id, bm); 
  } 
  return bm; 
}

Most of this has been moved from the drawBoard() function, only this time we are checking a HashMap for cached bitmaps and if it doesn’t exist we create and scale the image then store it ready for next time. Then we replace the lines that work out the graphic with a simple call to the protected getBitmap().

//Board.java
private void drawPieces(Canvas canvas) {
	Log.i("Board", String.valueOf(Calendar.getInstance().getTimeInMillis())); 
	Resources res = getResources();
	for (int i = 0 ; i < mPieces.length ; i++) { 
		for (int j = 0 ; j < mPieces[i].length ; j++) { 
			if (mPieces[i][j] != 0) { 
				Bitmap bm = getBitmap(mPieces[i][j]);
				canvas.drawBitmap(bm, i*mTileWidth, j*mTileHeight, null); 
			} 
	    }
	} 
	Log.i("Board", String.valueOf(Calendar.getInstance().getTimeInMillis())); 
}

Re-running with this update makes our draw routine somewhat faster. 118 milliseconds from start to finish. With more serviceable performance we can put back the code to draw the board and our game of draughts is starting to look the part. Updating this to look like RPS Chess is just a matter of adding some new piece resources and setting the activity to lay them out in the right starting positions.

Looking the part, but we still can’t play the game until we can move the pieces. Join me next time and we will motivate our pieces to action.

A board view laid out as a game of draughts

Draughts

This entry was posted in android and tagged . Bookmark the permalink.

6 Responses to Once less with feeling

  1. kumar says:

    What is use of addPiece( ) function in above code.
    please can you explain clearly about that function

    • Chris says:

      The addPiece() function is used to initialise the game. It updates the mPieces array which contains the game state and is called from the activity that is displaying the board view (I’ve not shown any of the activity code yet). The reason for doing it like that is I don’t want to hard code any of the graphics resources, piece layouts or game rules into the board view so I can reuse it for any other board games I choose to make. I’m expecting people to be more interested in creating regular chess games than this somewhat obscure version.

      Does that explain enough?

  2. kumar says:

    Hi Chris,
    Thanks for your early replies .Could you please tell me where did you use addPiece(int type, int x, int y) method. I did’nt understand where exactly addPiece() method called. so I am waiting for your early and prompt reply.

    • Chris says:

      Here is the relevant code from the activity.

      public class RockPaperScissorsChessActivity extends Activity {
      /** Called when the activity is first created. */

      @Override
      public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);

      Board b = (Board)findViewById(R.id.board);

      //add red pieces
      b.addPiece(R.drawable.redrock, 0, 0);
      b.addPiece(R.drawable.redrock, 1, 0);
      b.addPiece(R.drawable.redrock, 2, 0);

      This repeats another forty five times with different drawables and positions to populate the board.

      I hope that helps.

  3. kumar says:

    Hi
    Using this tutorial I have managed to draw the checkers board and the pieces succesfully(here I need to put the different image on canvas )
    How do i move the bitmap using onTouch() ?
    can you please solve my problem

    • Chris says:

      I’ve split handling moves into two posts: “Moving sharply” and “Smooth Operator”. Moving Sharply covers the basics of overriding the onTouchEvent() function and handles the press and release actions. Smooth Operator extends that so the image will track your finger as it moves. It’s all built around handing the MotionEvents in the “switch (event.getAction())” part of the code.

Comments are closed.