BullsAndCows

A project for a Job Interview to build a quick game of "Bulls and Cows". Review draft for r/CodeReview Creator- u/Cheesy_Core
mail@pastecode.io avatar
unknown
csharp
2 years ago
4.4 kB
2
Indexable
Never
using System;
using System.Collections.Generic;
using System.Linq;

// namespace:   BullsAndCows
//
// summary:	    A project for a Job Interview 
//              to build a quick game of "Bulls and Cows".
//              Review draft for r/CodeReview
//              Creator- u/Cheesy_Core
namespace BullsAndCows
{
    /// <summary>   A static class to provide assistant functions over generic type. </summary>
    public static class Applier
    {
        /// <summary>   Applies a function over argument. </summary>
        ///
        /// <typeparam name="TIn">  Type of the argument for application. </typeparam>
        /// <typeparam name="TOut"> Type of the applied argument result. </typeparam>
        /// <param name="x">    The applied argument. </param>
        /// <param name="f">    The function applied over the argument. </param>
        ///
        /// <returns>   The applied argument result. </returns>
        public static TOut Apply<TIn, TOut>(this TIn x, Func<TIn, TOut> f) => f(x);
    }

    /// <summary>   The program main class. </summary>
    class Program
    {
        /// <summary>   Returns how many values are similar
        ///             in the same position in guess and gen,
        ///             and in different position in guess and gen. </summary>
        ///
        /// <exception cref="ArgumentException">    Thrown when guess or gen 
        ///                                         aren't the same length. </exception>
        ///
        /// <typeparam name="T">    Type of argument to compare guess and gen. </typeparam>
        /// <param name="guess">    The guessed input. </param>
        /// <param name="gen">      The generated value. </param>
        ///
        /// <returns>   First result number of similar values in the same position,
        ///             Second result number of similar values in different position. </returns>
        private static (int, int) Score<T>(IEnumerable<T> guess, IEnumerable<T> gen) where T : IEquatable<T>
        {
            var (bulls, nonbulls) = guess.Zip(gen)
                .ToLookup(x =>
                {
                    if (x.First == null || x.Second == null)
                        throw new System.ArgumentException("guess and gen aren't the same length");
                    return x.First.Equals(x.Second);
                })
                .Apply(x => (x[true], x[false]));

            var cows = nonbulls
                .Apply(x => (x.Select(xs => xs.First),
                            x.Select(xs => xs.Second)))
                .Apply(x => x.Item1.Intersect(x.Item2));

            return (bulls.Count(), cows.Count());
        }

        /// <summary>   The game's logic loop. </summary>
        ///
        /// <param name="gen">      The generated value. </param>
        /// <param name="length">   The length of the value. </param>
        /// <param name="nTry">     The current iteration. </param>
        ///
        /// <returns>   whether the game have been won </returns>
        private static bool GameLoop(IEnumerable<char> gen, int length, int nTry)
        {
            var bulls   = 0;
            var cows    = 0;

            Console.Write"{0}: ", nTry);
            var guess   = Console.ReadLine().ToCharArray();

            if (guess.Length == length)
            {
                (bulls, cows) = Score(guess, gen);
                Console.WriteLine("Bulls={0}, Cows={1}", bulls, cows);
                return bulls == length;
            }
            else
            {
                Console.WriteLine("Wrong length");
                return false;
            }
        }

        /// <summary>   Main entry-point for this application. </summary>
        public static void Main()
        {
            var rand    = new Random(Guid.NewGuid().GetHashCode());
            var nTry    = 1;

            Console.Write("Enter the length of the number you intending to guess: ");
            var length  = int.Parse(Console.ReadLine());
            var gen     = new char[length]
                .Select(_ => (char)rand.Next('0', '9'))
                .ToList(); //forces evaluation of rand.next at initialization, preventing access to uninitialized data 

            while (!GameLoop(gen, length, nTry))
                nTry++;

            Console.WriteLine("you Win! tries: {0}", nTry);
        }
    }
}