Chapter 7

Finishing Your First Game

In This Chapter

arrow Handling the end of a hand and the end of a game

arrow Developing a more sophisticated player AI

arrow Using your own launcher icon

After you have every core element that enables you to represent the game state and make plays from both hands, your only remaining tasks are to handle the ends of hands and games — and to make a few improvements. You’re almost finished with your first game!

ontheweb_modern.eps You can download sample files for this chapter at

www.dummies.com/go/androidgameprogramming

Ending Hands and Games

You’ve got all the logic for making plays within a game, but hands and games can’t go on forever, right? You need to make sure the game can recognize states when the hand or game should come to an end so that scores can be tallied so the game can either

check.png Progress to the next hand

check.png Determine who won and who lost

Ending a hand

The hand should end when either player plays their last card. But then these three things should happen:

check.png Update the scores.

check.png Display a dialog indicating that the hand is over.

check.png Start a new hand.

To handle the scoring first, you add a new variable to keep track of how many points were earned by a given player for the hand. You reset this variable after each hand. Add the following line to your variable declarations in GameView:

private int scoreThisHand = 0;

To handle the scoring value for each card, you update the Card object. Open the Card class and add the following variable declaration along with the others:

private int scoreValue = 0;

Now modify the constructor of Card to match Listing 7-1, and add the new getScoreValue() method.

/9781118235997-tb0701.png

Here’s a brief explanation of what the various lines do:

510 These lines check the rank of the card and assign a score value to your variable when the card is created. In Crazy Eights, eights that remain in a player’s hand at the end of the game are worth 50 points to the opponent. Face cards are worth 10 points; aces, 1 point; and all other cards, their face values. There are other scoring conventions, so feel free to use your own, though you’ll stick with this standard one for now.

16 You then need a method to get the score value when you tally the scores.

Next, you add the new method updateScores() to your GameView with the contents of Listing 7-2.

Listing 7-2: The New updateScores() Method

private void updateScores() {

   for (int i = 0; i < myHand.size(); i++) {

      oppScore += myHand.get(i).getScoreValue();

      scoreThisHand += myHand.get(i).getScoreValue();

   }

   for (int i = 0; i < oppHand.size(); i++) {

      myScore += oppHand.get(i).getScoreValue();

      scoreThisHand += oppHand.get(i).getScoreValue();

   }

}

This listing loops through the hand that still has cards:

check.png One hand is empty, so only one of these loops even does anything. If your hand is empty, you increment the score with the value of each card in your opponent’s hand.

check.png You also increment the scoreThisHand variable, which you use to inform the player of how many points were earned from this hand.

check.png Likewise, when your opponent’s hand is empty, they get the point value of each card in your hand.

Now that the scores are updated, you display a new dialog to indicate that the hand has ended. It needs

check.png Text to tell the player who ran out of cards and how many points were awarded

check.png A button to dismiss the dialog and advance to the next hand

If you’re making a new dialog with different UI elements, you need a new layout. Follow these steps:

1. Right-click the res/layout folder and select NewFile.

2. Name the file end_hand_dialog.xml and click OK.

3. Modify the contents to match Listing 7-3.

Listing 7-3: The end_hand_dialog.xml File

<?xml version=”1.0” encoding=”utf-8”?>

<LinearLayout

android:id=”@+id/endHandLayout”

android:layout_width=”275dp”

android:layout_height=”wrap_content”

android:orientation=”vertical”

android:layout_gravity=”top”

xmlns:android=”http://schemas.android.com/apk/res/android”

>

<TextView

android:id=”@+id/endHandText”

android:layout_width=”wrap_content”

android:layout_height=”wrap_content”

android:text=””

android:textSize=”16sp”

android:layout_marginLeft=”5dp”

android:textColor=”#FFFFFF”

>

</TextView>

<Button

android:id=”@+id/nextHandButton”

android:layout_width=”125dp”

android:layout_height=”wrap_content”

android:text=”Next Hand”

>

</Button>

</LinearLayout>

This listing is an even simpler version of the Choose Suit dialog layout. Again, you’re using a LinearLayout to align the elements vertically within the dialog, and you have only two elements:

check.png TextView

You leave the default text for the TextView empty for now because you’ll modify it by informing the player who ran out of cards on a given hand and how many points they earned from their opponent’s remaining cards.

check.png Button.

The button has the default text Next Hand.

Now you add the method that gets called when a given hand is finished. Add the method shown in Listing 7-4 to your GameView.

/9781118235997-tb0704.png

This listing is similar to the method that displays the Choose Suits dialog. It follows this process of events:

1. You create a new dialog, ensure that it doesn’t display a title, and then set the content to the new layout file.

2. You then call the updateScores() method in line 5 to display the scoring information in the dialog. Line 6 creates a reference to the TextView, and then the next few lines set the text, depending on who ran out of cards and how many points were earned.

3. Reference the button (starting with line 25), and set the onClickListener() and its associated logic. You call a new method, initNewHand(), which you haven’t written yet, and then dismiss the dialog.

You’ll also need the import:

import android.widget.TextView;

Now put in the initNewHand() method. Add the code in Listing 7-5 to your GameView.

/9781118235997-tb0705.png

Here’s a brief explanation of the lines in the listing:

2 Reset the points earned for the hand.

37 Determine who will play first in the next hand. The player who goes out gets to play first in the next hand, so you check to see whose hand is empty and set the myTurn variable accordingly.

813 These lines add the discard pile and both players’ hands back to the deck, and then clear the lists for the hands and the discard pile. You’re essentially putting all cards back into the deck.

1417 Deal the cards and add the top card to the discard pile, setting the valid suit and rank based on that card.

1820 Finally, if it isn’t the human player’s turn, you instruct the computer to play. Otherwise, the game simply waits for play input from the player.

Now that you have all the associated logic to handle the end of a hand, you need to call it when either player runs out of cards. Check in the two places where the human and computer make their play — for the human, in the ACTION_UP case of onTouchEvent().

Modify the relevant code in ACTION_UP that handles dropping a card onto the discard pile to match the code in Listing 7-6.

/9781118235997-tb0706a.png

/9781118235997-tb0706b.png

Starting with line 14, you simply add a check to see whether the player’s hand is empty:

check.png If the player’s hand is empty, you call the endHand() method.

check.png Otherwise, you continue the game by either calling the Choose Suits dialog or passing the turn to the computer.

What about the computer player? Find your makeComputerPlay() method in GameView and modify it to match Listing 7-7.

/9781118235997-tb0707a.png

/9781118235997-tb0707b.png

You’ve added a check from line 35 after playing a card, to see whether the opponent’s hand is empty. If so, you call the endHand() method. You should be able to launch the game and play out a real-life hand of Crazy Eights!

If you go out first, you should see a dialog similar to the one in Figure 7-1.

In this case, the human player ran out of cards first and earned 46 points from the opponent’s hand. Note also that the scores displayed at the top and bottom of the screen have been updated. At this point, you can play endless rounds of Crazy Eights, with the scores updating after every hand. But there’s no way for the game to end. Typically, a game ends when one player’s score reaches a predetermined goal. In this case, you use 300 points.

Ending a game

If you end the game when one player reaches or exceeds 300 points, you don’t need to do much other than modify the endHand() method. Points are accumulated only at the conclusion of a hand, so you simply need to add some additional logic to detect end-of-game conditions and display different information in your existing End Hand dialog.

Figure 7-1: Displaying the end-of-hand dialog.

9781118235997-fg0701.tif

Modify your endHand() method to match Listing 7-8.

/9781118235997-tb0708.png

Here’s a brief explanation of various lines in the listing:

9–24 You’ve added conditionals to check whether either score equals or exceeds 300. If so, you display a different message, indicating that the game is over and who won, and asking whether the player wants to play another game.

27 Detect whether either score exceeds 300, and if so, set the text on the button to New Game instead of the default Next Hand.

32–35 The only other modification is zeroing out both players’ scores if the game is over before initializing a new hand.

Wrapping Up the Game

After you have all the necessary logic for playing not just one game but also starting new games, all the essential nuts and bolts are in place. However, the computer player is still not a capable opponent.

You’ll make improvements to make the game more competitive before updating the launcher icon and wrapping everything up.

Coding the opponent AI

You aren’t doing anything particularly sophisticated to the AI. Crazy Eights is a simple game, and its strategy, accordingly, isn’t complicated. Some games warrant different levels of strength for computer players, but that’s not necessary here. You simply want the computer player to provide a decent challenge.

There’s no need to make the game play optimally, because it’s a relatively simple game. Your players may not appreciate a computer that plays better than they do!

technicalstuff.eps When I first released my Dominoes game, I coded a fairly simple AI. All it does is look for the highest possible point gain on its turn. If it can’t score on its turn, it plays a random legal move. I thought that this difficulty level was reasonable for an AI because it doesn’t play defensively or consider the remaining point totals if the human player runs out of cards first. But I still received complaints that the game was too difficult to play against. One user wrote that he had played it dozens of times and simply couldn’t beat it. He might simply have been unlucky, but the moral of the story is that you need to find a reasonable balance for your computer opponents, and not worry about how well they’re playing. You’ll likely get complaints that your computer players are both too good and not good enough.

In the case of the Crazy Eights player, you make only two significant changes to improve it, which should make it suitable for average play. Both have to do with playing an 8:

check.png Ensure that the computer plays an 8 only when it has no other legal play.

check.png Be smart about which suit the computer chooses when it plays an 8.

Open your ComputerPlayer class and modify the makePlay() method to match Listing 7-9.

/9781118235997-tb0709.png

In the previous version of this method, you loop through the computer player’s hand, and if a given card is a legal play, set it as the play to make and then return it. Here, you separate out the logic for playing an 8 and a non-8:

check.png Lines 3–16 loop through the computer’s hand the first time, determining whether each card that isn’t an 8 is a legal move.

If a legal non-8 move is found, it’s set to the current play.

check.png Lines 17–25 are executed only if a non-8 legal play is found. If not, this loop looks for an 8 in the computer player’s hand, and if one is found, it’s set to the current play.

check.png If no legal play is found, 0 is returned, indicating that the computer must draw a card.

This change means that the computer player is holding its 8 cards longer and playing them only when necessary, which is generally a smart way to play.

Next, you modify the chooseSuit() method, as shown in Listing 7-10.

/9781118235997-tb0710a.png

/9781118235997-tb0710b.png

When the computer player plays an 8 and chooses a suit, it should choose the suit that is most prevalent in its hand. Before, it always picked diamonds (which isn’t an effective strategy).

Follow the steps below to improve the suit choice when the computer player plays an 8:

1. Add four variables to track the number of each suit (lines 3–6).

2. In Lines 7–21, loop through the computer player’s hand and count the number of each suit.

3. From line 22 on, check to see which suit count is greater than all the others.

• If it’s greater, set it to the suit to be returned.

• Otherwise, the default of diamonds is returned.

Give it a shot. You should find that this version of the computer player is a reasonably good challenge.

warning_bomb.eps You could continue to make improvements, but as I mention earlier in this chapter, be careful — the more casual the game, the less likely your players will want to face a difficult challenge. If you’re designing a chess app, use strong AI and give a number of different skill levels to your computer opponents. But with Crazy Eights, a modestly good computer player works well.

Making your own launcher icon

After the game is finished, you still haven’t updated the launcher icon from the default. Designing good-looking icons is harder than you might think. Your launcher icon should be simple, visually appealing, and instantly recognizable. If you have money in the budget, consider hiring a graphic designer to make your icon.

The icon should look good at different sizes because you’ll provide a resized icon for the four current screen densities. Figure 7-2 shows the Crazy Eights icon at each of the four generalized screen densities.

Figure 7-2: The Crazy Eights icon for each screen density.

9781118235997-fg0702.tif

From left to right, the icons are for

check.png xhdpi (96 x 96)

check.png hdpi (72 x 72)

check.png mdpi (48 x 48)

check.png ldpi (36 x 36)

The icon graphic has to be located in the proper res/drawable folder, and each file must have the same name. The default launcher is named ic_launcher, so I typically replace that graphic with my own and name it the same as the old default graphic. For example, you should have an icon graphic named ic_launcher sized at 96 x 96 pixels in your res/drawable-xhdpi folder, one named ic_launcher sized 72 x 72 pixels in your res/drawable-hdpi folder, and so on for all four icons.

If you decide to name your icon graphic differently, update your manifest. If you open it, you see in the application tag the android:icon attribute, which points to the appropriate image file for a particular screen density.

After you’ve updated the icon, launch the game and return to the app directory to see the updated icon.

technicalstuff.eps Whew — now your game is finished! The preceding few chapters give you the basic functionality to make a simple, turn-based game. You can use it as a basis for more complex games of the same type, including multiplayer turn-based games. However, you may be more interested in more graphics-intensive, real-time games, such as side-scrollers or action and arcade games.

In the following chapters, I walk you through the steps to implement the popular carnival game Whack-a-Mole, which provides a strong basis for developing games of this type.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset