Moved UI stuff into its own class.

Minor changes, commenting and tidying up the other classes.
This commit is contained in:
rbamforth
2021-03-26 22:51:28 +00:00
parent 0d35b103b0
commit b4655d5777
4 changed files with 145 additions and 99 deletions

View File

@@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace War
{
// These enums define the card's suit and rank.
public enum Suit
{
none = 0,
clubs,
diamonds,
hearts,
@@ -15,7 +16,6 @@ namespace War
public enum Rank
{
none = 0,
// Skip 1 because ace is high.
two = 2,
three,
@@ -32,25 +32,25 @@ namespace War
ace
}
// TODO Testing
// A class to represent a playing card.
public class Card
{
// A card is an immutable object (i.e. it can't be changed) so its suit
// and rank value are readonly; they can only be set in the constructor.
private readonly Suit suit;
private readonly Rank rank;
private static Dictionary<Suit, string> suitNames = new Dictionary<Suit, string>()
// These dictionaries are used to convert a suit or rank value into a string.
private readonly Dictionary<Suit, string> suitNames = new Dictionary<Suit, string>()
{
{ Suit.none, "N"},
{ Suit.clubs, "C"},
{ Suit.diamonds, "D"},
{ Suit.hearts, "H"},
{ Suit.spades, "S"},
};
private static Dictionary<Rank, string> rankNames = new Dictionary<Rank, string>()
private readonly Dictionary<Rank, string> rankNames = new Dictionary<Rank, string>()
{
{ Rank.none, "0"},
{ Rank.two, "2"},
{ Rank.three, "3"},
{ Rank.four, "4"},
@@ -66,18 +66,30 @@ namespace War
{ Rank.ace, "A"},
};
public Card(Suit suit, Rank rank) // immutable
public Card(Suit suit, Rank rank)
{
this.suit = suit;
this.rank = rank;
}
// would normally consider suit and rank but in this case we only want to compare rank.
// Relational Operator Overloading.
//
// You would normally expect the relational operators to consider both the suit and the
// rank of a card, but in this program suit doesn't matter so we define the operators to just
// compare rank.
// When adding relational operators we would normally include == and != but they are not
// relevant to this program so haven't been defined. Note that if they were defined we
// should also override the Equals() and GetHashCode() methods. See, for example:
// http://www.blackwasp.co.uk/CSharpRelationalOverload.aspx
// If the == and != operators were defined they would look like this:
//
//public static bool operator ==(Card lhs, Card rhs)
//{
// return lhs.rank == rhs.rank;
//}
//
//public static bool operator !=(Card lhs, Card rhs)
//{
// return !(lhs == rhs);
@@ -105,10 +117,12 @@ namespace War
public override string ToString()
{
return $"{suitNames[suit]}-{rankNames[rank]}"; // string interpolation
// N.B. We are using string interpolation to create the card name.
return $"{suitNames[suit]}-{rankNames[rank]}";
}
}
// A class to represent a deck of cards.
public class Deck
{
public const int deckSize = 52;
@@ -117,6 +131,7 @@ namespace War
public Deck()
{
// Populate theDeck with all the cards in order.
int i = 0;
for (Suit suit = Suit.clubs; suit <= Suit.spades; suit++)
{
@@ -128,14 +143,21 @@ namespace War
}
}
// Return the card at a particular position in the deck.
// N.B. As this is such a short method, we make it an
// expression-body method.
public Card GetCard(int i) => theDeck[i];
// Shuffle the cards, this uses the modern version of the
// Fisher-Yates shuffle, see:
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
public void Shuffle()
{
// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
var rand = new Random();
// Iterate backwards through the deck.
for (int i = deckSize - 1; i >= 1; i--)
{
var rand = new Random();
int j = rand.Next(0, i);
// Swap the cards at i and j

View File

@@ -1,58 +1,11 @@
using System;
namespace War
namespace War
{
public class Intro
{
public void WriteIntro()
{
Console.WriteLine(" WAR");
Console.WriteLine(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("THIS IS THE CARD GAME OF WAR. EACH CARD IS GIVEN BY SUIT-#");
Console.WriteLine("AS S-7 FOR SPADE 7. ");
if (AskQuestion("DO YOU WANT DIRECTIONS? "))
{
Console.WriteLine("THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD");
Console.WriteLine("(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO");
Console.WriteLine("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.");
}
Console.WriteLine();
Console.WriteLine();
}
public bool AskQuestion(string question)
{
while (true)
{
Console.Write(question);
string result = Console.ReadLine();
if (result.ToLower()[0] == 'y')
{
return true;
}
else /*if (result.ToLower() == "no")*/
{
return false;
}
Console.WriteLine("YES OR NO, PLEASE.");
}
}
}
class Program
{
static void Main(string[] args)
{
var intro = new Intro();
intro.WriteIntro();
var ui = new UserInterface();
ui.WriteIntro();
var deck = new Deck();
deck.Shuffle();
@@ -63,38 +16,20 @@ namespace War
for (int i = 0; i < Deck.deckSize; i += 2)
{
// Play the next hand.
var yourCard = deck.GetCard(i);
var computersCard = deck.GetCard(i + 1);
Console.WriteLine($"YOU: {yourCard} COMPUTER: {computersCard}");
if (yourCard < computersCard)
{
computersScore++;
Console.WriteLine($"THE COMPUTER WINS!!! YOU HAVE {yourScore} AND THE COMPUTER HAS {computersScore}");
}
else if (yourCard > computersCard)
{
yourScore++;
Console.WriteLine($"YOU WIN. YOU HAVE {yourScore} AND THE COMPUTER HAS {computersScore}");
}
else
{
Console.WriteLine("TIE. NO SCORE CHANGE");
}
ui.WriteAResult(yourCard, computersCard, ref computersScore, ref yourScore);
if (!intro.AskQuestion("DO YOU WANT TO CONTINUE? "))
if (!ui.AskAQuestion("DO YOU WANT TO CONTINUE? "))
{
usedAllCards = false;
break;
}
}
if (usedAllCards)
{
Console.WriteLine("WE HAVE RUN OUT OF CARDS.");
}
Console.WriteLine($"FINAL SCORE: YOU: {yourScore} THE COMPUTER: {computersScore}");
Console.WriteLine("THANKS FOR PLAYING. IT WAS FUN.");
ui.WriteClosingRemarks(usedAllCards, yourScore, computersScore);
}
}
}

View File

@@ -0,0 +1,83 @@
using System;
namespace War
{
// This class displays all the text that the user sees when playing the game.
// It also handles asking the user a yes/no question and returning their answer.
public class UserInterface
{
public void WriteIntro()
{
Console.WriteLine(" WAR");
Console.WriteLine(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
Console.WriteLine();
Console.WriteLine("THIS IS THE CARD GAME OF WAR. EACH CARD IS GIVEN BY SUIT-#");
Console.Write("AS S-7 FOR SPADE 7. ");
if (AskAQuestion("DO YOU WANT DIRECTIONS? "))
{
Console.WriteLine("THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD");
Console.WriteLine("(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO");
Console.WriteLine("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.");
}
Console.WriteLine();
Console.WriteLine();
}
public void WriteAResult(Card yourCard, Card computersCard, ref int computersScore, ref int yourScore)
{
Console.WriteLine($"YOU: {yourCard} COMPUTER: {computersCard}");
if (yourCard < computersCard)
{
computersScore++;
Console.WriteLine($"THE COMPUTER WINS!!! YOU HAVE {yourScore} AND THE COMPUTER HAS {computersScore}");
}
else if (yourCard > computersCard)
{
yourScore++;
Console.WriteLine($"YOU WIN. YOU HAVE {yourScore} AND THE COMPUTER HAS {computersScore}");
}
else
{
Console.WriteLine("TIE. NO SCORE CHANGE");
}
}
public bool AskAQuestion(string question)
{
// Repeat asking the question until the user answers "YES" or "NO".
while (true)
{
Console.Write(question);
string result = Console.ReadLine();
if (result.ToLower() == "yes")
{
Console.WriteLine();
return true;
}
else if (result.ToLower() == "no")
{
Console.WriteLine();
return false;
}
Console.WriteLine("YES OR NO, PLEASE.");
}
}
public void WriteClosingRemarks(bool usedAllCards, int yourScore, int computersScore)
{
if (usedAllCards)
{
Console.WriteLine("WE HAVE RUN OUT OF CARDS.");
}
Console.WriteLine($"FINAL SCORE: YOU: {yourScore} THE COMPUTER: {computersScore}");
Console.WriteLine("THANKS FOR PLAYING. IT WAS FUN.");
}
}
}

View File

@@ -14,6 +14,8 @@ namespace WarTester
private Card c3 = new Card(Suit.diamonds, Rank.ten);
private Card c4 = new Card(Suit.diamonds, Rank.ten);
// Test the relational operators.
[TestMethod]
public void LessThanIsValid()
{
@@ -77,10 +79,10 @@ namespace WarTester
[TestMethod]
public void ToStringIsValid()
{
string s1 = c1.ToString();
string s2 = c3.ToString();
string s3 = new Card(Suit.hearts, Rank.queen).ToString();
string s4 = new Card(Suit.spades, Rank.ace).ToString();
var s1 = c1.ToString();
var s2 = c3.ToString();
var s3 = new Card(Suit.hearts, Rank.queen).ToString();
var s4 = new Card(Suit.spades, Rank.ace).ToString();
Assert.IsTrue(s1 == "C-2", "s1 invalid");
Assert.IsTrue(s2 == "D-10", "s2 invalid");
@@ -92,7 +94,10 @@ namespace WarTester
[TestClass]
public class DeckTest
{
private string ConcatenateDeck(Deck d)
private readonly string cardNamesInOrder = "C-2C-3C-4C-5C-6C-7C-8C-9C-10C-JC-QC-KC-AD-2D-3D-4D-5D-6D-7D-8D-9D-10D-JD-QD-KD-AH-2H-3H-4H-5H-6H-7H-8H-9H-10H-JH-QH-KH-AS-2S-3S-4S-5S-6S-7S-8S-9S-10S-JS-QS-KS-A";
//Helper method. Adds the names of all the cards together into a single string.
private string ConcatenateTheDeck(Deck d)
{
StringBuilder sb = new StringBuilder();
@@ -107,20 +112,21 @@ namespace WarTester
[TestMethod]
public void InitialDeckContainsCardsInOrder()
{
Deck d1 = new Deck();
string allTheCards = ConcatenateDeck(d1);
Deck d = new Deck();
string allTheCards = ConcatenateTheDeck(d);
Assert.IsTrue(allTheCards == "C-2C-3C-4C-5C-6C-7C-8C-9C-10C-JC-QC-KC-AD-2D-3D-4D-5D-6D-7D-8D-9D-10D-JD-QD-KD-AH-2H-3H-4H-5H-6H-7H-8H-9H-10H-JH-QH-KH-AS-2S-3S-4S-5S-6S-7S-8S-9S-10S-JS-QS-KS-A");
Assert.IsTrue(allTheCards == cardNamesInOrder);
}
[TestMethod]
public void ShufflingChangesDeck()
{
Deck d1 = new Deck();
d1.Shuffle();
string allTheCards = ConcatenateDeck(d1);
// I'm not sure how to test that shuffling has worked other than to check that the cards aren't in the initial order.
Deck d = new Deck();
d.Shuffle();
string allTheCards = ConcatenateTheDeck(d);
Assert.IsTrue(allTheCards != "C-2C-3C-4C-5C-6C-7C-8C-9C-10C-JC-QC-KC-AD-2D-3D-4D-5D-6D-7D-8D-9D-10D-JD-QD-KD-AH-2H-3H-4H-5H-6H-7H-8H-9H-10H-JH-QH-KH-AS-2S-3S-4S-5S-6S-7S-8S-9S-10S-JS-QS-KS-A");
Assert.IsTrue(allTheCards != cardNamesInOrder);
}
}
}