diff --git a/01 Acey Ducey/csharp/AceyDucey.csproj b/01 Acey Ducey/csharp/AceyDucey.csproj new file mode 100644 index 00000000..c73e0d16 --- /dev/null +++ b/01 Acey Ducey/csharp/AceyDucey.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp3.1 + + + diff --git a/01 Acey Ducey/csharp/AceyDucey.sln b/01 Acey Ducey/csharp/AceyDucey.sln new file mode 100644 index 00000000..f406d9cf --- /dev/null +++ b/01 Acey Ducey/csharp/AceyDucey.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30709.132 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AceyDucey", "AceyDucey.csproj", "{A8FE2F04-3DFE-4254-BD5B-281DC8E30B1C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A8FE2F04-3DFE-4254-BD5B-281DC8E30B1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8FE2F04-3DFE-4254-BD5B-281DC8E30B1C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8FE2F04-3DFE-4254-BD5B-281DC8E30B1C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8FE2F04-3DFE-4254-BD5B-281DC8E30B1C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F620B340-EB7A-48C1-B12F-9D3D94AE7771} + EndGlobalSection +EndGlobal diff --git a/01 Acey Ducey/csharp/Game.cs b/01 Acey Ducey/csharp/Game.cs new file mode 100644 index 00000000..76c58cd7 --- /dev/null +++ b/01 Acey Ducey/csharp/Game.cs @@ -0,0 +1,326 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AceyDucey +{ + /// + /// The main class that implements all the game logic + /// + internal class Game + { + /// + /// Our Random number generator object + /// + private Random Rnd { get; } = new Random(); + + /// + /// A line of underscores that we'll print between turns to separate them from one another on screen + /// + private string SeparatorLine { get; } = new string('_', 70); + + + /// + /// Main game loop function. This will play the game endlessly until the player chooses to quit. + /// + internal void GameLoop() + { + // First display instructions to the player + DisplayIntroText(); + + // We'll loop for each game until the player decides not to continue + do + { + // Play a game! + PlayGame(); + + // Play again? + } while (TryAgain()); + } + + /// + /// Play the game + /// + private void PlayGame() + { + GameState state = new GameState(); + + // Clear the display + Console.Clear(); + // Keep looping until the player has no money left + do + { + // Play the next turn. Pass in our state object so the turn can see the money available, + // can update it after the player makes a bet, and can update the turn count. + PlayTurn(state); + + // Keep looping until the player runs out of money + } while (state.Money > 0); + + // Looks like the player is bankrupt, let them know how they did. + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(""); + Console.WriteLine($"Sorry, friend, but you blew your wad. Your game is over after {state.TurnCount} {(state.TurnCount == 1 ? "turn" : "turns")}. Your highest balance was ${state.MaxMoney}."); + } + + + /// + /// Play a turn + /// + /// The current game state + private void PlayTurn(GameState state) + { + // Let the player know what's happening + Console.WriteLine(""); + Console.WriteLine(SeparatorLine); + Console.WriteLine(""); + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(""); + Console.WriteLine("Here are your next two cards:"); + + // Generate two random cards + int firstCard = GetCard(); + int secondCard = GetCard(); + + // If the second card is lower than the first card, swap them over + if (secondCard < firstCard) + { + (firstCard, secondCard) = (secondCard, firstCard); + } + + // Display the cards + DisplayCard(firstCard); + DisplayCard(secondCard); + + // Ask the player what they want to do + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(""); + Console.Write("You currently have "); + Console.ForegroundColor = ConsoleColor.Yellow; + Console.Write($"${state.Money}"); + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(". How much would you like to bet?"); + + // Read the bet amount + int betAmount = PlayTurn_GetBetAmount(state.Money); + + // Display a summary of their inpout + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine(""); + Console.WriteLine($"You choose to {(betAmount == 0 ? "pass" : $"bet {betAmount}")}."); + + // Generate and display the final card + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(""); + Console.WriteLine("The next card is:"); + + int thirdCard = GetCard(); + DisplayCard(thirdCard); + Console.WriteLine(""); + + // Was the third card between the first two cards? + if (thirdCard > firstCard && thirdCard < secondCard) + { + // It was! Inform the player and add to their money + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine("You win!"); + if (betAmount == 0) + { + Console.WriteLine("(It's just a shame you chose not to bet!)"); + } + else + { + state.Money += betAmount; + // If their money exceeds the MaxMoney, update that too + state.MaxMoney = Math.Max(state.Money, state.MaxMoney); + } + } + else + { + // Oh dear, the player lost. Let them know the bad news and take their bet from their money + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine("You lose!"); + if (betAmount == 0) + { + Console.WriteLine("(It's lucky you chose not to bet!)"); + } + else + { + state.Money -= betAmount; + } + } + + Console.ForegroundColor = ConsoleColor.White; + Console.Write("You now have "); + Console.ForegroundColor = ConsoleColor.Yellow; + Console.Write($"${state.Money}"); + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine("."); + + // Update the turn count now that another turn has been played + state.TurnCount += 1; + + // Ready for the next turn... + Console.ForegroundColor = ConsoleColor.DarkGreen; + Console.WriteLine(""); + Console.WriteLine("Press any key to continue..."); + Console.ReadKey(true); + } + + /// + /// Prompt the user for their bet amount and validate their input + /// + /// The player's current money + /// Returns the amount the player chooses to bet + private int PlayTurn_GetBetAmount(int currentMoney) + { + int betAmount; + // Loop until the user enters a valid value + do + { + // Move this to a separate function... + Console.ForegroundColor = ConsoleColor.Yellow; + Console.Write("> $"); + string input = Console.ReadLine(); + + // Is this a valid number? + if (!int.TryParse(input, out betAmount)) + { + // No + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Sorry, I didn't understand. Please enter how much you would like to bet."); + // Continue looping + continue; + } + + // If the amount between 0 and their available money? + if (betAmount < 0 || betAmount > currentMoney) + { + // No + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"Please enter a bet amount between $0 and ${currentMoney}."); + // Continue looping + continue; + } + + // We have a valid bet, stop looping + break; + } while (true); + + // Return whatever the player entered + return betAmount; + } + + /// + /// Generate a new random card. + /// + /// Will return a value between 2 and 14, inclusive. + /// Values 2 to 10 are their face values. 11 represents a Jack, 12 is a Queen, 13 a King and 14 an Ace. + /// Even though this is a slightly offset sequence, it allows us to perform a simple greater-than/less-than + /// comparison with the card values, treating an Ace as a high card. + private int GetCard() + { + return Rnd.Next(2, 15); + } + + /// + /// Display the card number on screen, translating values 11 through to 14 into their named equivalents. + /// + /// + private void DisplayCard(int card) + { + string cardText; + switch (card) + { + case 11: + cardText = "Jack"; + break; + case 12: + cardText = "Queen"; + break; + case 13: + cardText = "King"; + break; + case 14: + cardText = "Ace"; + break; + default: + cardText = card.ToString(); + break; + } + + // Format as black text on a white background + Console.Write(" "); + Console.BackgroundColor = ConsoleColor.White; + Console.ForegroundColor = ConsoleColor.Black; + Console.Write($" {cardText} "); + Console.BackgroundColor = ConsoleColor.Black; + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(""); + } + + /// + /// Display instructions on how to play the game and wait for the player to press a key. + /// + private void DisplayIntroText() + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine("Acey Ducey Gard Game."); + Console.WriteLine("Creating Computing, Morristown, New Jersey."); + Console.WriteLine(""); + + Console.ForegroundColor = ConsoleColor.DarkGreen; + Console.WriteLine("Originally published in 1978 in the book 'Basic Computer Games' by David Ahl."); + Console.WriteLine("Modernised and converted to C# in 2021 by Adam Dawes (@AdamDawes575)."); + Console.WriteLine(""); + + Console.ForegroundColor = ConsoleColor.Gray; + Console.WriteLine("Acey Ducey is played in the following manner:"); + Console.WriteLine(""); + Console.WriteLine("The dealer (computer) deals two cards, face up."); + Console.WriteLine(""); + Console.WriteLine("You have an option to bet or pass, depending on whether or not you feel the next card will have a value between the"); + Console.WriteLine("first two."); + Console.WriteLine(""); + Console.WriteLine("If the card is between, you will win your stake, otherwise you will lose it. Ace is 'high' (higher than a King)."); + Console.WriteLine(""); + Console.WriteLine("If you want to pass, enter a bet amount of $0."); + Console.WriteLine(""); + + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine("Press any key start the game."); + Console.ReadKey(true); + + } + + /// + /// Prompt the player to try again, and wait for them to press Y or N. + /// + /// Returns true if the player wants to try again, false if they have finished playing. + private bool TryAgain() + { + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine("Would you like to try again? (Press 'Y' for yes or 'N' for no)"); + + Console.ForegroundColor = ConsoleColor.Yellow; + Console.Write("> "); + + char pressedKey; + // Keep looping until we get a recognised input + do + { + // Read a key, don't display it on screen + ConsoleKeyInfo key = Console.ReadKey(true); + // Convert to upper-case so we don't need to care about capitalisation + pressedKey = Char.ToUpper(key.KeyChar); + // Is this a key we recognise? If not, keep looping + } while (pressedKey != 'Y' && pressedKey != 'N'); + // Display the result on the screen + Console.WriteLine(pressedKey); + + // Return true if the player pressed 'Y', false for anything else. + return (pressedKey == 'Y'); + } + + } +} diff --git a/01 Acey Ducey/csharp/GameState.cs b/01 Acey Ducey/csharp/GameState.cs new file mode 100644 index 00000000..5f8ccbcf --- /dev/null +++ b/01 Acey Ducey/csharp/GameState.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AceyDucey +{ + /// + /// The GameState class keeps track of all the game variables while the game is being played + /// + internal class GameState + { + + /// + /// How much money does the player have at the moment? + /// + internal int Money { get; set; } + + /// + /// What's the highest amount of money they had at any point in the game? + /// + internal int MaxMoney { get; set; } + + /// + /// How many turns have they played? + /// + internal int TurnCount { get; set; } + + /// + /// Class constructor -- initialise all values to their defaults. + /// + internal GameState() + { + // Setting Money to 100 gives the player their starting balance. Changing this will alter how much they have to begin with. + Money = 100; + MaxMoney = Money; + TurnCount = 0; + } + } +} diff --git a/01 Acey Ducey/csharp/Program.cs b/01 Acey Ducey/csharp/Program.cs new file mode 100644 index 00000000..cf6052c9 --- /dev/null +++ b/01 Acey Ducey/csharp/Program.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading; + +namespace AceyDucey +{ + /// + /// The application's entry point + /// + class Program + { + + /// + /// This function will be called automatically when the application begins + /// + /// + static void Main(string[] args) + { + // Create an instance of our main Game class + Game game = new Game(); + + // Call its GameLoop function. This will play the game endlessly in a loop until the player chooses to quit. + game.GameLoop(); + } + + + } +} diff --git a/01 Acey Ducey/csharp/README.md b/01 Acey Ducey/csharp/README.md index 4daabb5c..6bb4c49e 100644 --- a/01 Acey Ducey/csharp/README.md +++ b/01 Acey Ducey/csharp/README.md @@ -1,3 +1,3 @@ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) -Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) +Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) by Adam Dawes (@AdamDawes575, https://adamdawes.com).