Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Microsoft CSharp Programming For The Absolute Beginner (2002) [eng]-1.pdf
Скачиваний:
46
Добавлен:
16.08.2013
Размер:
15.71 Mб
Скачать

next as you use polymorphism to build the bitter critter.

A few changes are necessary to add polymorphism to a class. At its simplest, all you do is make a class with the same method name as one of the methods of its parent class:

///<summary>

///BitterCritter

///Designed to demonstrate polymorphism

///</summary>

public class BitterCritter: Critter

{

//note it always starts out ticked off public BitterCritter():base("", 2, 2, 0){ } // end basic constructor

public BitterCritter(string name): base(name, 2, 2, 0){ } // end one string constructor

public new string talk(){

string message = name + " glowers moodily. \n"; message += base.talk();

return message;

} // end talk method

}// end bitterCritter

I made a few changes to the constructors so that the bitter critter always starts off hungry and angry. I also added the talk() method. To specify that I intend the talk() method to override an existing method, I added the new keyword to the method call. The new keyword indicates that this new version of the talk() method is the one that should take precedence. If you want to call the talk() method of the parent object, use the base.talk() syntax.

In the Real World

Polymorphism entails more than what I’ve described in this section, but you won’t need to worry much about it until you get into specific types of situations (for example, casting an object into its parent’s type). If the new keyword does not provide the behavior you need, look up the combination virtual and overrides in MSDN help.

Creating the Snowball Fight

The snowball fight will be easy to build now that you have the object−oriented principles within your reach. The program has only three classes: the menu, the human player, and the robot player.

Hint When you are designing a program, you often start by thinking about the main elements of the program. In this case, it is simple to see that the three entities in this game are the two players and the interface, which will handle the program logic and the user input. Seeing the objects you need to create is not quite as easy, so sketch out your thoughts on paper before you start programming.

The Fighter class is the most important part of this game because it forms the basis of both the human and robot players. When I first designed the program, I wondered whether both the player and the robot should be the same object. Even though that turned out not to be the case, the robot

117

fighter is based on the human fighter. The interactions between the human and robot fighters form the foundation of the program. I started by building the human fighter. After I had it working well, I extended the fighter to make the robot fighter. As I was building these two objects, I used the main menu program to test my objects constantly and make sure that each fighter was acting as I expected. When I was comfortable that the two player objects were working correctly, I added scoring and end−of−game situations.

Building the Fighter

The Fighter class is the heart of this game because it represents the human player. Because the robot fighter is derived from the Fighter class, both classes share essential characteristics. For this reason, I had to design the Fighter class to be very flexible so that it could work well as a human or robot player.

Setting Up the Basic Fighter

When you are looking at a new class, look at the instance variables. The instance variables often describe the most important data in the class. This data is so important that it is often encapsulated into properties. The first part of the Fighter class’s code creates instance variables and properties to handle strength, snowballs, and name:

using System;

namespace Snowball

{

///<summary>

///Basic Snowball fighter

///</summary>

public class Fighter

{

//instance variables private int pStrength; private int pSnowballs; private string pName;

//properties

public int strength { get {

return pStrength;

}// end get set {

pStrength = value;

}// end set

}// end strength

public int snowballs { get {

return pSnowballs;

}// end get set {

pSnowballs = value;

}// end set

}// end snowballs

public string name { get {

return pName; } // end get set {

118

pName = value;

} // end set

}// end name

The code is simple. The Fighter class has a name property, which is a string. The name is important in this program because there is no graphic interface. Each player must have a distinctive name, or the player will have difficulty figuring out what is happening. The fighter also has two numeric properties. The snowballs property determines how many snowballs the player currently has stockpiled. The fighter won’t be able to throw if he doesn’t have any snowballs. (It sounds obvious, but this is the type of behavior you have to write in later.) The strength property describes how many hits are left before this fighter loses the game.

Writing the Fighter Constructor

The constructor for the fighter simply initializes the instance variables. The name is accepted as a parameter to the constructor. Although I thought about overloading the constructor, it wasn’t necessary in this situation because there was no need for other constructors.

Trick Generally, you use overloaded constructors to make a class more flexible. This is important when you’re building a multipurpose class that will be used in many programs. If you’re not expecting to reuse your class, multiple constructors are not necessary. However, the point of building classes is to have code reuse, so overloaded constructors are never a bad idea.

public Fighter(string theName)

{

//initialize snowballs = 3; strength = 3; name = theName;

} // end constructor

Throwing a Snowball

The most exciting part of the game happens when a fighter throws a snowball. The throwSnow() method handles the act of throwing a snowball. The program uses a random number generator to determine whether the snowball hits its target. It requires a parameter to determine the range between the thrower and the target:

public bool throwSnow(int range){

//calculates likeliness of a hit at a given range //returns true if snowball hit

bool hit = false; int myRoll;

Random roller = new Random(); if (snowballs <= 0) {

Console.WriteLine ("{0} is out of snowballs!", name);

}else {

myRoll = roller.Next(10); if (myRoll > range) {

hit = true;

}// end hit if snowballs−−;

}// end out of snowballs if return hit;

}// end throwSnow method

}// end fighter

}// end namespace

119

The roller variable is an instance of the Random class. The myRoll variable holds a random value between 0 and 9.

The throwSnow() method checks whether the player has snowballs remaining. If the player has zero snowballs, the throw is canceled. If the player has snowballs remaining, the program determines whether the throw hits its target.

I used a simple algorithm to determine whether the throw succeeds. I wanted the players to trade safety for accuracy. The farther away the target is from the thrower, the less likely the target will be hit (and the less danger the thrower is in because the robot uses similar calculations to determine likelihood of a hit). To implement this algorithm, I used the Next() method of the Random object to obtain a number between 0 and 9. (If you send an int parameter to the next method, it returns a positive integer smaller than the parameter.)

If the random number is larger than the range, the snowball hits the target. As the range gets closer, it becomes easier to hit the target.

Hint Creating complex algorithms is tempting, but simple approaches are usually better. There’s absolutely no scientific basis to the way I figured out the algorithm for determining hits. It was the simplest way I could think of to make a random value more likely to hit when the throwers are closer. Start simple and make things more complex only when you have a good reason to do so.

Building the Robot Fighter

The basic Fighter class is functional enough for most purposes. However, it relies on human control. To make a credible robot player, I needed a player almost like the human fighter, but with the capability to make autonomous decisions. Designing a challenging computer opponent can be difficult, but designing rudimentary computer behavior is easy. The robot fighter is inherited from the ordinary Fighter class. This means that when I built the robot class, all I needed to create were those elements of the robot fighter that add the robot player’s very rudimentary intelligence. Inheritance can be magical.

Initializing the RoboFighter

The RoboFighter class is a classic candidate for inheritance. Because the class will be simply an enhancement of the Fighter class, it’s natural to extend RoboFighter from Fighter. The RoboFighter will have name, snowballs, and strength properties and the throwSnow() method. All these characteristics are inherited from the Fighter class. The RoboFighter has a couple new characteristics of its own.

namespace Snowball

{

///<summary>

///RoboFighter

///A computer−controlled snowball fighter

///derived from fighter

///</summary>

public class RoboFighter: Fighter

{

private Fighter player;

public RoboFighter(Fighter thePlayer, string theName): base(theName)

{

120

player = thePlayer; } // end constructor

I added one private instance variable to the RoboFighter. I wanted to have access to the other player so that the robot fighter could access the human player’s properties. The only constructor for the RoboFighter requires both a standard fighter (which is the human player) and the name of the robot.

Choosing the Robot’s Play

All the key artificial intelligence comes in the choosePlay() method added to the RoboFighter. Basically, the choosePlay() method uses another random number generator to determine which play the robot should make:

public int choosePlay(int range){ int thePlay;

Random roller = new Random(); if (snowballs <= 0){

//make a new snowball if out of them Console.WriteLine(name + " is making a snowball"); snowballs++;

}else {

//decide to throw or move thePlay = roller.Next(6); switch (thePlay){

case 0:

//go closer

Console.WriteLine("{0} moves closer.", name); range−−;

break; case 1:

//back up

Console.WriteLine("{0} backs away.", name); range++;

break; case 2:

//make a snowball

Console.WriteLine(name + " is making a snowball"); snowballs++;

break;

default:

//otherwise, throw a snowball Console.WriteLine("{0} throws a snowball", name); if (throwSnow(range)){

Console.WriteLine("{0} has been hit", player.name); player.strength−−;

}else {

Console.WriteLine("{0} missed you.", name);

}// end if

break;

}// end switch

}// end out of snowballs if return range;

}// end choosePlay

The roller variable holds a Random object, and thePlay is designed to hold a random value between 0 and 5.

I wanted the robot to throw a snowball about half the time. The other plays should be random

121

Соседние файлы в предмете Программирование на C++