1
\$\begingroup\$

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

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Could you post the relevant code directly here? \$\endgroup\$
    – svick
    Jan 7, 2013 at 15:57

3 Answers 3

4
\$\begingroup\$

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);
        Console.ReadLine();
    } 
}
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();
\$\endgroup\$
3
\$\begingroup\$

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:");
            int.TryParse(Console.ReadLine(), out number);

            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);
        }

        Console.ReadLine();
    }

    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);
    }
}
\$\endgroup\$
1
\$\begingroup\$

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?");
string choice = Console.ReadLine();
if (choice.StartsWith("c"))
{
    Console.WriteLine("Insert your number between 1 and 100:");
    int.TryParse(Console.ReadLine(), out number);
    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()
                     : Console.ReadLine(),
                     out guess);
    if (guess != number)
        Console.WriteLine("No, it's {0} than {1}. Try again.", guess < number ? "higher" : "lower", guess);
    tries++;
}
message = ai  ? "Great, the answer was {0}. It took me {1} tries."
              : "Great, the answer was {0}. It took you {1} tries.";
Console.WriteLine(message, number, tries);
Console.ReadLine();
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.