# Combine similar methods with different text?

I made the popular Guess-The-Number game and included an AI version that picks random numbers. The AI version has slightly different Console output that differ slightly, for example:

I generated a number between 1 and 100. Can you guess it?
You generated a number between 1 and 100. Let me try


First I did this:

Console.WriteLine("{0} generated a number between 1 and 100. {1}", ai? "I" : "You", ai? "Can you guess it?", "Let me try");


As this needed to be done a few more times, it looked very clumsy, but could implement both versions in one medium length method. After that I seperated the different versions and made two shorter methods GuessUser and GuessAI which are cleaner, but both share a lot of similar code, almost everything besides Console.WriteLine(x).

Is there a smart way to combine those two versions into a small method?

Complete version

-
Could you post the relevant code directly here? –  svick Jan 7 '13 at 15:57

I can't say that it's going to be more efficient, or better designed, but one option is to take advantage of polymorphism by creating a set of classes for this.

public abstract class GuessBase
{
protected abstract string GuessMessage { get; }
protected abstract string WrongMessage { get; }
protected abstract string RightMessage { get; }

protected abstract int GetGuess();

public void DoGuessing()
{
Console.WriteLine(GuessMessage);
while (guess != number)
{
if (GetGuess() != number)
Console.WriteLine(WrongMessage, guess);
tries++;
}
Console.WriteLine(RightMessage, number, tries);
}
}
public class AIGuess : GuessBase
{
protected override string GuessMessage
{
get
{
return "You generated a number between 1 and 100. Let me try";
}
}
// Similar for WrongMessage and RightMessage

protected override int GetGuess()
{
return Pick(number, ref guess);
}
// Put Pick() method here.
}
public class UserGuess : GuessBase
{
protected override string GuessMessage
{
get
{
return "I generated a number between 1 and 100. Can you guess it?";
}
}
// Similar for WrongMessage and RightMessage

protected override int GetGuess()
{
int guess;
int.TryParse(Console.ReadLine(), out guess); // This won't do what you want if the user doesn't provide a valid number.
return guess;
}
}


Then, in your main code, do this:

GuessBase guess;
if (/* AI */) guess = new AIGuess();
else guess = new UserGuess();
guess.DoGuessing();

-

While I do like the polymorphism way a bit more, you could opt for something along the following lines. Notice I've cut out a bit of the sczizophrenic talking to itself, and improved on the use of variables aswell.

class Program
{
static readonly Random Rng = new Random();
static int min;
static int max = 100;

private static void Main()
{
var number = Rng.Next(1, 100);
Console.WriteLine("Do you want to [g]uess or [c]hoose?");

string choice = Console.ReadLine() ?? "";

if (choice.StartsWith("c"))
{
Console.WriteLine("Insert your number between 1 and 100:");

Console.WriteLine("You generated a number between 1 and 100. Let me try");
int previousGuess = 0;
var tries = Guess(number, () => Pick(number, ref previousGuess));
Console.WriteLine("Great, the answer was {0}. It took me {1} tries.", number, tries);
}
else
{
Console.WriteLine("I generated a number between 1 and 100. Can you guess it?");
var tries = Guess(number, () => int.Parse(Console.ReadLine()));
Console.WriteLine("Great, the answer was {0}. It took you {1} tries.", number, tries);
}

}

private static int Guess(int number, Func<int> guessMethod)
{
int tries = 1;
while(true)
{
var guess = guessMethod();
if (guess != number)
{
Console.WriteLine("No, it's {0} than {1}.", guess < number ? "higher" : "lower", guess);
tries++;
}
else
{
return tries;
}
}
}

private static int Pick(int number, ref int guess)
{
if (guess > number)
max = guess;
else
min = guess;
return guess = Rng.Next(min, max);
}
}

-

Well, I combined both everything into one method that I think is both elegant and suffices for this short program. I'm aware of abstractions similar to Bobson proposed, but I think that would be overkill for this. Any comments?

Random rng = new Random();
int min = 0, guess = 0, tries = 0, max = 100, number = rng.Next(1, 100);
bool ai = false;

Console.WriteLine("Do you want to [g]uess or [c]hoose?");
if (choice.StartsWith("c"))
{
Console.WriteLine("Insert your number between 1 and 100:");
ai = true;
}

string message = ai  ? "You generated a number between 1 and 100. Let me try"
: "I generated a number between 1 and 100. Can you guess it?";
Console.WriteLine(message);

while (guess != number)
{
int.TryParse(ai  ? (guess > number ? rng.Next(min, max = guess) : rng.Next(min = guess, max)).ToString()