5
\$\begingroup\$

This is my first project over 100 lines and I would like some feedback on it. If anyone has any ideas for things to add or ways to make the code more efficient they would be very much appreciated.

class Program
{
    static void Main(string[] args)
    {
        Betting game1 = new Betting();
        game1.rules();
        Console.WriteLine("");
        while (true)
        {
            game1.game();
            string answer = "";
            Console.WriteLine("");
            while (true)
            {
                Console.Write("Would you like to play again?(y or n) ");
                answer = Console.ReadLine();
                if (!answer.ToLower().Equals("y") && !answer.ToLower().Equals("n"))
                {
                    Console.WriteLine("Please enter a valid answer.");
                    Console.WriteLine("");
                    continue;
                }
                else
                    break;
            }
            if (answer.ToLower().Equals("y")) {
                game1.playAgain(answer);
                continue;
            }
            else
                break;
        }
    }
}

class Betting
{
    private static long cashAmount;
    private static long player1Amount;
    private static long player2Amount;
    private static int rollAmount;
    private static long betAmountPlayer1;
    private static long betAmountPlayer2;
    private int numGames;


    public Betting()
    {
        cashAmount = 1500;
        player1Amount = cashAmount;
        player2Amount = cashAmount;
        rollAmount = 0;
        betAmountPlayer1 = 100;
        betAmountPlayer2 = 100;
        numGames = 0;
    }
    public void rules()
    {
        Console.WriteLine("How the game works!");
        Console.WriteLine("First: You can either set the custom cash amount for each player, or leave it default at $1500.");
        Console.WriteLine("Second: Select how many dice you want to roll(5,10,15,20).");
        Console.WriteLine("Third: Ask each player how much they want to bet.");
        //Console.WriteLine("Fourth: The higher your bet, the better chance you have at winning"); not implemented yet
        Console.WriteLine("Lastly it will print the results, the new balance amounts, and ask if you want to play again.");
    }

    public void game()
    {

        Dice dice1 = new Dice();
        Dice dice2 = new Dice();
        string answer = "";

        while (numGames == 0)
        {
            Console.Write("Would you like to leave the cash default(def) or set your own?(custom) ");
            answer = Console.ReadLine();
            if (!answer.ToLower().Equals("def") && !answer.ToLower().Equals("custom"))
            {
                continue;
            }
            else
                break;
        }
        if (answer.ToLower().Equals("custom"))
        {
            string newAmount = "";
            while (true)
            {
                long temp = 0;
                Console.Write("Please enter your custom amount: ");
                newAmount = Console.ReadLine();
                if (Int64.TryParse(newAmount, out temp))
                {
                    cashAmount = temp;
                    player1Amount = cashAmount;
                    player2Amount = cashAmount;
                    break;
                }
                else
                {
                    Console.WriteLine("ENTER A NUBMER");
                    Console.WriteLine("");
                    continue;
                }
            }
        }

        // show amout to each player
        Console.WriteLine("");
        Console.WriteLine("Player 1 amount: " + player1Amount);
        Console.WriteLine("Player 2 amount: " + player2Amount);

        // ask how many dice they want to roll
        while (true)
        {
            Console.WriteLine("");
            Console.Write("How many dice would you like to roll?(5,10,15,20) ");
            answer = Console.ReadLine();
            if (!answer.ToLower().Equals("5") && !answer.ToLower().Equals("10") && !answer.ToLower().Equals("15") && !answer.ToLower().Equals("20"))
            {
                Console.WriteLine("Enter a valid amount.");
                continue;
            }
            else
                break;

        }

        // ask how much each player wants to bet
        while (true)
        {
            Console.WriteLine("");
            Console.Write("Player 1 bet amount: ");
            answer = Console.ReadLine();
            if (Int64.TryParse(answer, out betAmountPlayer1))
            {
                if (betAmountPlayer1 < 0 || betAmountPlayer1 > player1Amount)
                {
                    Console.WriteLine("Please enter a valid number.");
                    continue;
                }
                else if (betAmountPlayer1 <= player1Amount)
                    break;
            }
            else
            {
                Console.WriteLine("That is an invalid amount.");
                continue;
            }
        }

        while (true)
        {
            Console.WriteLine("");
            Console.Write("Player 2 bet amount: ");
            answer = Console.ReadLine();
            if (Int64.TryParse(answer, out betAmountPlayer2))
            {
                if (betAmountPlayer2 < 0 || betAmountPlayer2 > player2Amount)
                {
                    Console.WriteLine("Please enter a valid number.");
                    continue;
                }
                else if (betAmountPlayer2 <= player2Amount)
                    break;
            }
            else
            {
                Console.WriteLine("That is an invalid amount.");
                continue;
            }
        }
        // roll the dice
        while (true)
        {
            if (Int32.TryParse(answer, out rollAmount))
            {
                //Dice dice1 = new Dice();
                //Dice dice2 = new Dice();
                dice1.rollDiceNoShow(rollAmount, true);
                dice2.rollDiceNoShow(rollAmount, false);
                break;
            }
            else
                Console.WriteLine("Please enter a vaild number");
            continue;
        }

        // print who had the higher sum
        Console.WriteLine("");
        Console.WriteLine("Player 1 had a score of: " + dice1.getPlayer1Sum());
        Console.WriteLine("Player 2 had a score of: " + dice2.getPlayer2Sum());
        Console.WriteLine("");
        winner(dice1, dice2);

        // ask if they want to play again

    }

    public void winner(Dice dice1, Dice dice2)
    {
        // check for the winner and update the cash amounts and show's their new balance

        if(dice1.getPlayer1Sum() > dice2.getPlayer2Sum())
        {
            Console.WriteLine("Player 1 wins!");
            Console.WriteLine("");
            player1Amount += betAmountPlayer2;
            player2Amount -= betAmountPlayer2;
            Console.WriteLine("Player 1's new balance: " + player1Amount);
            Console.WriteLine("Player 2's new balance: " + player2Amount);

        }
        else if (dice1.getPlayer1Sum() == dice2.getPlayer2Sum())
        {
            Console.WriteLine("It's a tie! no one wins!");
            Console.WriteLine("");
            Console.WriteLine("Player 1's balance: " + player1Amount);
            Console.WriteLine("Player 2's balance: " + player2Amount);
        }
        else
        {
            Console.WriteLine("Player 2 wins!");
            Console.WriteLine("");
            player2Amount +=  betAmountPlayer1;
            player1Amount -= betAmountPlayer1;
            Console.WriteLine("Player 1's new balance: " + player1Amount);
            Console.WriteLine("Player 2's new balance: " + player2Amount);
        }

    }
    public void playAgain(string answer)
    {
        if (answer.ToLower().Equals("y"))
        {
            numGames++;
        }
    }
}

class Dice
{
    private int player1Sum;
    private int player2Sum;
    private static int sides;
    public Dice()
    {
        sides = 6;
        player1Sum = 0;
        player2Sum = 0;
    }

    public int rollDice(long nd) //nd = numDice
    {
        Random rand = new Random();
        if (nd == 1)
        {
            int newSide = rand.Next(1, sides + 1);
            return newSide;
        }
        for (int i = 1; i <= nd; i++)
        {

            int newSide = rand.Next(1, sides + 1);
            Console.WriteLine("Dice Number " + i + " has landed on " + newSide);
        }

        return 0;
    }

    public void rollDiceNoShow(long nd, Boolean player1) //nd = numDice
    {
        Random rand = new Random();
        // if there is only one dice
        if (player1)
        {
            int newSide = rand.Next(1, sides + 1);
            player1Sum += newSide;
        }
        else
        {
            int newSide = rand.Next(1, sides + 1);
            player2Sum += newSide;
        }

        // more than one dice
        if (player1)
        {
            for (int i = 1; i <= nd; i++)
            {

                int newSide = rand.Next(1, sides + 1);
                player1Sum += newSide;
            }
        }
        else
        {
            for (int i = 1; i <= nd; i++)
            {

                int newSide = rand.Next(1, sides + 1);
                player2Sum += newSide;
            }
        }

    }

    public int getPlayer1Sum()
    {

        return player1Sum;
    }

    public int getPlayer2Sum()
    {
        return player2Sum;
    }
}
\$\endgroup\$
4
\$\begingroup\$

First off, let's start by refactoring the Dice class:

public sealed class Die
{
    private readonly Func<int, int, int> randomNumberGenerator;

    public int MinimumValue { get; }
    public int NumberOfSides { get; }

    public Die(int minimumValue, int numberOfSides, Func<int, int, int> randomNumberGenerator) {
        this.randomNumberGenerator = randomNumberGenerator;

        this.MinimumValue = minimumValue;
        this.NumberOfSides = checked(numberOfSides + 1);
    }

    public IEnumerable<int> Roll(int numberOfDice) {
        if (0 == numberOfDice) {
            yield break;
        }
        else {
            for (var i = 0; (i < numberOfDice); i++) {
                yield return randomNumberGenerator(MinimumValue, NumberOfSides);
            }
        }
    }
    public int Roll() => Roll(1).Single();
    public long RollAndSum(int numberOfDice) => Roll(numberOfDice).Select(d => ((long)d)).Sum();
}

By saving a dedicated random number generator delegate during construction we can save the overhead of calling new Random() during repeated calls to Roll. In addition, the static member sides has been converted into an instance member because sharing that value amongst all Dice seemed like a very restrictive choice.

Altering the Roll method so that it returns a set of values gives us a bit of flexibility and allows us to get rid of the duplicate code that was in rollDiceNoShow (now called RollAndSum).

Now we need something to hold the metadata for our players:

public class BettingPlayer
{
    public long CurrentAmount { get; }
    public string Name { get; }
    public Func<int, int, int> RandomNumberGenerator { get; }
    public long WagerAmount { get; }

    public BettingPlayer(long currentAmount, string name, Func<int, int, int> randomNumberGenerator, long wagerAmount) {
        this.CurrentAmount = currentAmount;
        this.Name = name;
        this.RandomNumberGenerator = randomNumberGenerator;
        this.WagerAmount = wagerAmount;
    }

    public BettingPlayer Lost(long newWager) => new BettingPlayer((CurrentAmount - WagerAmount), Name, RandomNumberGenerator, newWager);
    public BettingPlayer Lost() => Lost(WagerAmount);
    public BettingPlayer Won(long newWager) => new BettingPlayer((CurrentAmount + WagerAmount), Name, RandomNumberGenerator, newWager);
    public BettingPlayer Won() => Won(WagerAmount);
}

This allows us to shuttle state around and grants us the ability us assign each player their own (potentially biased) random number generator; can be used to assign a handicap or advantage to certain players. Finally, the game class itself:

public sealed class BettingGame
{
    private readonly int minimumValuePerDie;
    private readonly int numberOfDice;
    private readonly int numberOfSidesPerDie;

    private BettingPlayer[] players;

    public int NumberOfGamesPlayed { get; private set; }

    public BettingGame(int numberOfDice, int numberOfSidesPerDie, int minimumValuePerDie, params BettingPlayer[] players) {
        this.minimumValuePerDie = minimumValuePerDie;
        this.numberOfDice = numberOfDice;
        this.numberOfSidesPerDie = numberOfSidesPerDie;
        this.players = players;

        this.NumberOfGamesPlayed = 0;
    }

    public BettingPlayer GetWinner() => players.Aggregate((x, y) => (x.CurrentAmount > y.CurrentAmount) ? x : y);
    public void PlayRound() {
        var highScoreIds = new List<long>();
        var highScoreValue = 0L;
        var numberOfPlayers = players.Length;

        for (var i = 0; (i < numberOfPlayers); i++) {
            var currentPlayer = players[i];
            var score = new Die(minimumValuePerDie, numberOfSidesPerDie, currentPlayer.RandomNumberGenerator).RollAndSum(numberOfDice);

            if (score > highScoreValue) {
                highScoreIds.Clear();
                highScoreIds.Add(i);

                highScoreValue = score;
            }
            else if (score == highScoreValue) {
                highScoreIds.Add(i);
            }
        }

        var results = new BettingPlayer[numberOfPlayers];

        for (var i = 0; (i < highScoreIds.Count); i++) {
            var playerId = highScoreIds[i];

            results[playerId] = players[playerId].Won();
        }

        for (var i = 0; (i < results.Length); i++) {
            var temp = results[i];

            if (temp == null) {
                results[i] = players[i].Lost();
            }
        }

        players = results;

        NumberOfGamesPlayed++;
    }

    private static int GetIntegerInput(string message) {
        Console.Write($"{message}: ");

        while (true) {
            if (int.TryParse(Console.ReadLine(), out int numberOfPlayers)) {
                return numberOfPlayers;
            }
        }
    }

    public static int GetNumberOfPlayers() => GetIntegerInput("enter the number of players");
    public static long GetPlayerWager(int playerNumber) => GetIntegerInput($"enter the wager amount for player {playerNumber}");
    public static long GetPlayerStartingCash(int playerNumber) => GetIntegerInput($"enter the starting cash for player {playerNumber}");
    public static BettingGame Initalize(int numberOfDice, int numberOfSidesPerDie) {
        var minimumValuePerDie = 0;
        var numberOfPlayers = GetNumberOfPlayers();
        var players = new BettingPlayer[numberOfPlayers];

        for (var i = 0; (i < numberOfPlayers); i++) {
            var rng = new Random();

            players[i] = new BettingPlayer(GetPlayerStartingCash(i), $"Player {i}", rng.Next, GetPlayerWager(i));
        }

        return new BettingGame(numberOfDice, numberOfSidesPerDie, minimumValuePerDie, players);
    }
}

The BettingGame class encapsulates the state required to play; the core logic of the game has been separated from everything else in order to clean everything up a bit and an initialization step has been added to allow for a variable number of players/settings.

Example Usage:

class Program
{
    static void Main(string[] args) {
        var game = BettingGame.Initalize(numberOfDice: 1, numberOfSidesPerDie: 1);

        game.PlayRound();

        var winner = game.GetWinner();

        Console.WriteLine($"the winner is: {winner.Name}");
        Console.WriteLine($"they ended the game with: {winner.CurrentAmount}");
        Console.ReadKey();
    }
}

Notes:

  • GetWinner doesn't handle the case where one or more players end the series a tie; I was too lazy to come up with something better
  • there are bits of code that were intentionally left out (such as the rules method); again, out of laziness
\$\endgroup\$
0
3
\$\begingroup\$

Dealing with Console and User Input

You have lots of verbose, repeating code requesting user input.

while (true)
{
    Console.Write("Would you like to play again?(y or n) ");
    answer = Console.ReadLine();
    if (!answer.ToLower().Equals("y") && !answer.ToLower().Equals("n"))
    {
        Console.WriteLine("Please enter a valid answer.");
        Console.WriteLine("");
        continue;
    }
    else
        break;
}

Wouldn't you rather write something like ..

if (PlayAgain())
{
   // perform logic ..
} 
else {
   // perform logic ..
}

You should seperate user input logic from the game and use a simple pattern for requesting user input.

Show a message to the user, with a list of options appended. The user output is tested against the options, unless no options are specified. Two functions projection and layout are provided to map the user output from string to T and vice versa.

Pattern used for requesting user input

static T ReadInput<T>(
    string message, 
    Func<string, T> projection,
    Func<T, string> layout,
    params T[] options)
{
    var correctInput = false;
    var input = default(T);
    var formattedMessage = options == null || options.Length == 0
        ? message : $"{message} [{string.Join(", ", options.Select(layout))}]";

    Console.WriteLine();
    Console.WriteLine(formattedMessage);

    do
    {
        Console.Write(">");
        Console.ForegroundColor = ConsoleColor.White;
        var inputToken = Console.ReadLine();
        var error = false;
        Console.ForegroundColor = ConsoleColor.Gray;

        try
        {
            input = projection(inputToken);
        }
        catch
        {
            error = true;
        }

        correctInput = !error && (
               options == null 
            || options.Length == 0
            || options.Any(o => o.Equals(input))
        );

        if (!correctInput)
        {
            Console.WriteLine("Please try again.");
        }

    } while (!correctInput);

    return input;
}

Operations requesting for user input

public static bool PlayAgain()
{
    return ReadInput(
        "Would you like to play again?", 
         x => {
             switch (x.Trim().ToLowerInvariant())
             {
                 case "y":
                     return true;
                 case "n":
                     return false;
                 default:
                     throw new FormatException();
             }
         },
         x => x ? "y" : "n",
         true, false);
}

public enum CashOption
{
    Default,
    Custom
}

public static CashOption PickCashOption()
{
    return ReadInput(
        "Would you like to leave the cash default or set your own?",
         x => (CashOption)Enum.Parse(typeof(CashOption), x.Trim(), true),
         x => x.ToString(),
         Enum.GetValues(typeof(CashOption)).Cast<CashOption>().ToArray());
}

public static long PickAmount()
{
    return ReadInput(
        "Please enter your custom amount:",
         x => long.Parse(x),
         x => x.ToString());
}

public static int PickDiceRolls()
{
    return ReadInput(
        "How many dice would you like to roll?",
         x => int.Parse(x),
         x => x.ToString(),
         5, 10, 15, 20);
}

Usage in your game

var cashOption = PickCashOption();
if (cashOption = CashOption.Default)
{
  // perform logic ..
}

Display

Would you like to leave the cash default or set your own? [Default, Custom]
>
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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