The Robot Pilot
The following is one possible solution to this challenge.
// Setup the game.
int cityHealth = 15;
int manticoreHealth = 10;
int round = 1;
// Randomly choose the starting distance for the Manticore.
Random random = new Random();
int range = random.Next(100);
// Run the game until the city is destroyed or the Manticore is destroyed.
while (cityHealth > 0 && manticoreHealth > 0)
{
// Display the status for the round.
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("-----------------------------------------------------------");
DisplayStatus(round, cityHealth, manticoreHealth);
// Display the amount of damage expected on a hit.
int damage = DamageForRound(round);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"The cannon is expected to deal {damage} damage this round.");
// Get a number from the player.
Console.ForegroundColor = ConsoleColor.White;
int targetRange = AskForNumber("Enter desired cannon range:");
// Display the outcome of the number.
Console.ForegroundColor = ConsoleColor.Magenta;
DisplayOverOrUnder(targetRange, range);
// Deal damage to the Manticore if it was a hit.
if (targetRange == range) manticoreHealth -= damage;
// Deal damage to the city if the Manticore is still alive.
if (manticoreHealth > 0) cityHealth--;
// Go on to the next round.
round++;
}
// Display the outcome of the game.
bool won = cityHealth > 0;
DisplayWinOrLose(won);
// ------------------------------- METHODS --------------------------------
// Displays the result of the game.
void DisplayWinOrLose(bool won)
{
if (won)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("The Manticore has been destroyed! The city of Consolas has been saved!");
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("The city has been destroyed. The Manticore and the Uncoded One have won.");
}
}
// Tells the player if they fell short, overshot, or hit their target.
void DisplayOverOrUnder(int targetRange, int range)
{
if (targetRange < range) Console.WriteLine("That round FELL SHORT of the target.");
else if (targetRange > range) Console.WriteLine("That round OVERSHOT the target.");
else Console.WriteLine("That round was a DIRECT HIT!");
}
// Displays the status of the game, including the round number, and health of city and Manticore.
void DisplayStatus(int round, int cityHealth, int manticoreHealth) =>
Console.WriteLine($"STATUS: Round: {round} City: {cityHealth}/15 Manticore: {manticoreHealth}/10");
// Computes how much damage will be done depending on the current round.
// A multiple of 3 is a Fire blast. A multiple of 5 is an Electric blast.
// A multiple of both 3 and 5 is an extremely powerful combined blast.
// Anything else is a normal round.
int DamageForRound(int roundNumber)
{
if (roundNumber % 5 == 0 && roundNumber % 3 == 0) return 10; // Combined Electric and Fire
else if (roundNumber % 5 == 0) return 3; // Electric
else if (roundNumber % 3 == 0) return 3; // Fire
return 1; // Normal.
}
// Gets a number from the user, asking the prompt supplied by 'text'.
int AskForNumber(string text)
{
Console.Write(text + " ");
Console.ForegroundColor = ConsoleColor.Cyan;
int number = Convert.ToInt32(Console.ReadLine());
return number;
}
Answer this question: How might you use inheritance, polymorphism, or interfaces to allow the game to be either a single player (the computer randomly chooses the starting location and direction) or two players ( the second human determines the starting location and direction)?
When you want to support different competing strategies, an interface or abstract base class is a perfect tool to define what the strategy looks like on the outside, with two (or more) classes to implement each of the strategies. I tend to use interfaces over abstract base classes, though either could work.
So I’d start by defining an interface that captures the concept. Maybe something like this:
public interface IPilot
{
int PickDistance();
}
Then I’d create two classes that implement that interface. The following are the simplest versions I can come up with, and they probably deserve to be fleshed out a lot more if it were a “serious” program:
public class RandomPilot : IPilot
{
public int PickDistance() => new Random().Next(101);
}
public class HumanPilot : IPilot
{
public int PickDistance()
{
Console.WriteLine("Pick a number between 0 and 100 as your distance.");
return Convert.ToInt32(Console.ReadLine());
}
}
I’ve ignored all the input validation. Somebody could type in "a billion" and it would crash, or 1000000000 and it would be out of range (but not crash), but I’m not going to worry about that here.
Then, you only need to decide which of these two classes to instantiate. You could make a menu to pick, or load the setting from a file, or even pick between the two randomly. In my case, I’m going to hardcode the specifically chosen strategy, which means I’d need to recompile if I wanted to swap strategies, but that’s sometimes a fine choice, especially as you’re getting started. It could look like this:
IPilot pilot = new RandomPilot();
// IPilot pilot = new HumanPilot();
int manticoreDistance = pilot.PickDistance();
You can change which line is commented out.
Importantly, the rest of the code doesn’t really know or care which flavor of pilot is in use. The rest of the code just knows it has an IPilot to pick the distance whenever it is needed, and the rest of the code is perfectly happy with that.
(Note: I’ve fleshed this out far more than is really required by the book, but the hope is that you can spot the core concept of, “You could make an interface or abstract base class, and make different specific classes that build on that, so that the chosen strategy is swappable.” And if that didn’t come to your mind, hopefully, my explanation makes it a little easier to spot the next time you encounter something that could benefit from it.)
“Because RB said to.”
Okay, a better answer.
Color seems like the right name for both of these concepts, but the needs are different.
In the previous challenge, where we needed enough information to represent literally millions of colors with different R, G, and B values, we clearly need something more than an enumeration.
But could we have reused the Color class we made in the previous challenge for our enumeration here?
Yeah, it could have been done.
But it does drag along a lot of extra stuff unnecessarily.
In the context of a card, the color is really just a game mechanic that needs to differentiate four suits/colors from each other.
They just need unique names/values.
The full Color class is overkill and would result in more complicated code all around.
So using an enumeration here makes a lot of sense to keep the code simpler and more manageable.