Making moves

Our new board looks nice, but we’re not going to get anywhere unless we can make our pieces move around. In this step we’ll take an innocent looking ImageView and tell it how to start making moves on the enemy. Quite literally.

We’re starting with ImageViews because they do most of what we spent so long doing the first time around. They scale images, draw them on demand. I’m assuming a sensible amount of caching is going on there too. Certainly half a dozen things I don’t want to know how to do, but definitely want done anyway. They certainly let us… ah well, let’s come back to that later…

All I care about is what happens when you poke the piece with your finger, and for that we can create an OnClickListener.

[fragment name=”PieceHandler.java” root]
public class PieceHandler implements OnClickListener {
[fragment name=”onClick”]
[fragment name=”checkMoveType”]
[fragment name=”addMoves”]
[fragment name=”createMoveView”]
[fragment name=”addPaperMoves”]
[fragment name=”addRockMoves”]
[fragment name=”addScissorsMoves”]
}[/fragment]

When the player clicks a piece, we want to put new ImageViews on the board that highlight the valid moves and captures. Not only does this make it nice and easy for players to see what moves are permitted, but we can detect the player clicking them too.

There’s not a lot to this class that’s Android specific, but we’ll take a look anyway. First at the function required by the OnClickListener interface, onClick().

[fragment name=”onClick”]
public void onClick(View v) {
final ViewGroupBoard b = (ViewGroupBoard)v.getParent();
b.clearMoves();
b.setMovingPiece(v);
highlightThisPiece(v, b);
addMoves(v, b);
}[/fragment]

This is the click handler for the piece, so the view getting passed in is a reference to the piece ImageView. We are using a ViewGroup so the view’s parent is the ViewGroupBoard object, though the View class can’t know that so we have to do some casting.

We clear any existing moves, set the moving piece to this for later and add the highlights. I’m highlighting the moving piece for clarity, and doing it separately so it doesn’t get the same event handler applied as the move highlights.

[fragment name=”onClick”]
private void highlightThisPiece(View v, ViewGroupBoard b) {
//Highlight the moving piece too, but not with a moveHandler
final ImageView highlight = new ImageView(v.getContext());
highlight.setImageResource(R.drawable.hightlight);
highlight.setAlpha(50);
highlight.setTag(“MOVE”); //still tag it as a move so it gets cleared out when the move is over
highlight.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
b.addView(highlight);
}[/fragment]

In principle there are eight valid moves for any give piece but we’re curtailed by things like other pieces and the edges of the board. The board edges are easy enough, the move will be off the ViewGroup so it won’t be visible. Other pieces are a bit more interesting. We need a quick function to determine if a capture is valid. It can return one of three values, Valid, Invalid or Capture, because the rocks and papers can’t jump other pieces we need to know how one move would work out before we can know if the next move in that direction is valid.

[fragment name=”checkMoveType”]
private moveType checkMoveType(View attacker, View defender) {
if (defender == null)
return moveType.VALID;

final int attackType = (Integer)attacker.getTag();
final int defType = (Integer)defender.getTag();

if ((attackType == R.drawable.blackpaper && defType == R.drawable.whiterock)
|| (attackType == R.drawable.blackrock && defType == R.drawable.whitescissors)
|| (attackType == R.drawable.blackscissors && defType == R.drawable.whitepaper)
|| (attackType == R.drawable.whitepaper && defType == R.drawable.blackrock)
|| (attackType == R.drawable.whiterock && defType == R.drawable.blackscissors)
|| (attackType == R.drawable.whitescissors && defType == R.drawable.blackpaper))
return moveType.CAPTURE;
else
return moveType.INVALID;
}[/fragment]

Next we’ll need a helper function that churns out move targets. They are just ImageViews at heart, but they take a little setting up. Transparencies, click handlers, etc. so we may as well wrap that in another neat method.

[fragment name=”createMoveView”]
private ImageView createMoveView(Context c, int left, int top, int right, int bottom) {
ImageView moveTarget = new ImageView(c);
moveTarget.setImageResource(R.drawable.hightlight);
moveTarget.setAlpha(100);
moveTarget.setTag(“MOVE”);
moveTarget.layout(left, top, right, bottom);
moveTarget.setOnClickListener(new MoveHandler());
return moveTarget;
}[/fragment]

Now we are good to go. I’m going to break this down into a few separate functions to try and keep things as clear as possible. First of all the addMoves function is concerned with what kind of piece we are using. Happily black and white pieces move exactly the same way, no reflection to deal with so that step is very easy. Those of you making an actual chess game at home are going to have to give this more thought.

[fragment name=”addMoves”]
private void addMoves(View v, ViewGroupBoard b){
switch ((Integer)v.getTag()) {
case R.drawable.blackpaper:
case R.drawable.whitepaper:
addPaperMoves(b, v);
break;
case R.drawable.blackrock:
case R.drawable.whiterock:
addRockMoves(b, v);
break;
case R.drawable.blackscissors:
case R.drawable.whitescissors:
addScissorsMoves(b, v);
}
}[/fragment]

This next bit goes on. And on and on and on. And on. So I’m just going to give a partial example.

[fragment name=”addPaperMoves”]
private void addPaperMoves(ViewGroupBoard b, View v){
//up and left
moveType firstMove = checkMoveType(v, b.getViewAtLocation(v.getLeft() – v.getWidth(), v.getTop() – v.getHeight()));
moveType secondMove = null;
if (firstMove != moveType.INVALID) {
b.addView(createMoveView(v.getContext(), v.getLeft() – v.getWidth(), v.getTop() – v.getHeight(), v.getLeft(), v.getTop()));
secondMove = checkMoveType(v, b.getViewAtLocation(v.getLeft() – v.getWidth() * 2, v.getTop() – v.getHeight() * 2));
if (firstMove == moveType.CAPTURE && secondMove != moveType.INVALID)
b.addView(createMoveView(v.getContext(), v.getLeft() – v.getWidth() * 2, v.getTop() – v.getHeight() * 2, v.getLeft() – v.getWidth(), v.getTop() – v.getHeight()));
}
[/fragment]

This places the move images on the board for paper moves for the up-left diagonal. The first move is one square up and left. We check what it is. Anything that isn’t INVALID is ok. Next we check two squares up and left, but as I said before, the validity of this move requires that the previous one be a VALID move. Not INVALID or CAPTURE.

Copy and paste this code over and over for the other directions. Adjust the offsets for rocks. Scissors are a special case, because they can jump each move is independent of every other, which makes its version of this method much simpler.

You will have compiler errors right now, because the MoveHandler referenced in createMoveView() doesn’t exist yet. If you comment out the line temporarily you’re game will run and show you the valid moves for any piece you click on. Next week we’re going to build that move handler. A post that will be super-short, but one that will demonstrate why the ViewGroup approach is the right way to go.

[download_code]

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