mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-27 05:03:27 -08:00
31
94 War/csharp/War/War.sln
Normal file
31
94 War/csharp/War/War.sln
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31112.23
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "War", "War\War.csproj", "{C13BE0FA-D8F7-4CA7-A95D-DA03A9DE8950}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WarTester", "WarTester\WarTester.csproj", "{B539F618-EE83-486C-9A6D-404E998BED2D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C13BE0FA-D8F7-4CA7-A95D-DA03A9DE8950}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C13BE0FA-D8F7-4CA7-A95D-DA03A9DE8950}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C13BE0FA-D8F7-4CA7-A95D-DA03A9DE8950}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C13BE0FA-D8F7-4CA7-A95D-DA03A9DE8950}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B539F618-EE83-486C-9A6D-404E998BED2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B539F618-EE83-486C-9A6D-404E998BED2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B539F618-EE83-486C-9A6D-404E998BED2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B539F618-EE83-486C-9A6D-404E998BED2D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3FA273C6-089E-4F3D-8DC0-F9143D1CDF3A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
170
94 War/csharp/War/War/Cards.cs
Normal file
170
94 War/csharp/War/War/Cards.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
|
||||
namespace War
|
||||
{
|
||||
// These enums define the card's suit and rank.
|
||||
public enum Suit
|
||||
{
|
||||
clubs,
|
||||
diamonds,
|
||||
hearts,
|
||||
spades
|
||||
}
|
||||
|
||||
public enum Rank
|
||||
{
|
||||
// Skip 1 because ace is high.
|
||||
two = 2,
|
||||
three,
|
||||
four,
|
||||
five,
|
||||
six,
|
||||
seven,
|
||||
eight,
|
||||
nine,
|
||||
ten,
|
||||
jack,
|
||||
queen,
|
||||
king,
|
||||
ace
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// 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.clubs, "C"},
|
||||
{ Suit.diamonds, "D"},
|
||||
{ Suit.hearts, "H"},
|
||||
{ Suit.spades, "S"},
|
||||
};
|
||||
|
||||
private readonly Dictionary<Rank, string> rankNames = new Dictionary<Rank, string>()
|
||||
{
|
||||
{ Rank.two, "2"},
|
||||
{ Rank.three, "3"},
|
||||
{ Rank.four, "4"},
|
||||
{ Rank.five, "5"},
|
||||
{ Rank.six, "6"},
|
||||
{ Rank.seven, "7"},
|
||||
{ Rank.eight, "8"},
|
||||
{ Rank.nine, "9"},
|
||||
{ Rank.ten, "10"},
|
||||
{ Rank.jack, "J"},
|
||||
{ Rank.queen, "Q"},
|
||||
{ Rank.king, "K"},
|
||||
{ Rank.ace, "A"},
|
||||
};
|
||||
|
||||
public Card(Suit suit, Rank rank)
|
||||
{
|
||||
this.suit = suit;
|
||||
this.rank = 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);
|
||||
//}
|
||||
|
||||
public static bool operator <(Card lhs, Card rhs)
|
||||
{
|
||||
return lhs.rank < rhs.rank;
|
||||
}
|
||||
|
||||
public static bool operator >(Card lhs, Card rhs)
|
||||
{
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
public static bool operator <=(Card lhs, Card rhs)
|
||||
{
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
public static bool operator >=(Card lhs, Card rhs)
|
||||
{
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// 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;
|
||||
|
||||
private Card[] theDeck = new Card[deckSize];
|
||||
|
||||
public Deck()
|
||||
{
|
||||
// Populate theDeck with all the cards in order.
|
||||
int i = 0;
|
||||
for (Suit suit = Suit.clubs; suit <= Suit.spades; suit++)
|
||||
{
|
||||
for (Rank rank = Rank.two; rank <= Rank.ace; rank++)
|
||||
{
|
||||
theDeck[i] = new Card(suit, rank);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
{
|
||||
var rand = new Random();
|
||||
|
||||
// Iterate backwards through the deck.
|
||||
for (int i = deckSize - 1; i >= 1; i--)
|
||||
{
|
||||
int j = rand.Next(0, i);
|
||||
|
||||
// Swap the cards at i and j
|
||||
Card temp = theDeck[j];
|
||||
theDeck[j] = theDeck[i];
|
||||
theDeck[i] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
94 War/csharp/War/War/Program.cs
Normal file
35
94 War/csharp/War/War/Program.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
namespace War
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var ui = new UserInterface();
|
||||
ui.WriteIntro();
|
||||
|
||||
var deck = new Deck();
|
||||
deck.Shuffle();
|
||||
|
||||
int yourScore = 0;
|
||||
int computersScore = 0;
|
||||
bool usedAllCards = true;
|
||||
|
||||
for (int i = 0; i < Deck.deckSize; i += 2)
|
||||
{
|
||||
// Play the next hand.
|
||||
var yourCard = deck.GetCard(i);
|
||||
var computersCard = deck.GetCard(i + 1);
|
||||
|
||||
ui.WriteAResult(yourCard, computersCard, ref computersScore, ref yourScore);
|
||||
|
||||
if (!ui.AskAQuestion("DO YOU WANT TO CONTINUE? "))
|
||||
{
|
||||
usedAllCards = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ui.WriteClosingRemarks(usedAllCards, yourScore, computersScore);
|
||||
}
|
||||
}
|
||||
}
|
||||
83
94 War/csharp/War/War/UserInterface.cs
Normal file
83
94 War/csharp/War/War/UserInterface.cs
Normal 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
8
94 War/csharp/War/War/War.csproj
Normal file
8
94 War/csharp/War/War/War.csproj
Normal file
@@ -0,0 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
132
94 War/csharp/War/WarTester/Tests.cs
Normal file
132
94 War/csharp/War/WarTester/Tests.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System.Text;
|
||||
using War;
|
||||
|
||||
|
||||
|
||||
namespace WarTester
|
||||
{
|
||||
[TestClass]
|
||||
public class CardTest
|
||||
{
|
||||
private Card c1 = new Card(Suit.clubs, Rank.two);
|
||||
private Card c2 = new Card(Suit.clubs, Rank.ten);
|
||||
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()
|
||||
{
|
||||
Assert.IsTrue(c1 < c2, "c1 < c2"); // Same suit, different rank.
|
||||
Assert.IsFalse(c2 < c1, "c2 < c1"); // Same suit, different rank.
|
||||
|
||||
Assert.IsFalse(c3 < c4, "c3 < c4"); // Same suit, same rank.
|
||||
|
||||
Assert.IsTrue(c1 < c3, "c1 < c3"); // Different suit, different rank.
|
||||
Assert.IsFalse(c3 < c1, "c3 < c1"); // Different suit, different rank.
|
||||
|
||||
Assert.IsFalse(c2 < c4, "c2 < c4"); // Different suit, same rank.
|
||||
Assert.IsFalse(c4 < c2, "c4 < c2"); // Different suit, same rank.
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GreaterThanIsValid()
|
||||
{
|
||||
Assert.IsFalse(c1 > c2, "c1 > c2"); // Same suit, different rank.
|
||||
Assert.IsTrue(c2 > c1, "c2 > c1"); // Same suit, different rank.
|
||||
|
||||
Assert.IsFalse(c3 > c4, "c3 > c4"); // Same suit, same rank.
|
||||
|
||||
Assert.IsFalse(c1 > c3, "c1 > c3"); // Different suit, different rank.
|
||||
Assert.IsTrue(c3 > c1, "c3 > c1"); // Different suit, different rank.
|
||||
|
||||
Assert.IsFalse(c2 > c4, "c2 > c4"); // Different suit, same rank.
|
||||
Assert.IsFalse(c4 > c2, "c4 > c2"); // Different suit, same rank.
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LessThanEqualsIsValid()
|
||||
{
|
||||
Assert.IsTrue(c1 <= c2, "c1 <= c2"); // Same suit, different rank.
|
||||
Assert.IsFalse(c2 <= c1, "c2 <= c1"); // Same suit, different rank.
|
||||
|
||||
Assert.IsTrue(c3 <= c4, "c3 <= c4"); // Same suit, same rank.
|
||||
|
||||
Assert.IsTrue(c1 <= c3, "c1 <= c3"); // Different suit, different rank.
|
||||
Assert.IsFalse(c3 <= c1, "c3 <= c1"); // Different suit, different rank.
|
||||
|
||||
Assert.IsTrue(c2 <= c4, "c2 <= c4"); // Different suit, same rank.
|
||||
Assert.IsTrue(c4 <= c2, "c4 <= c2"); // Different suit, same rank.
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GreaterThanEqualsIsValid()
|
||||
{
|
||||
Assert.IsFalse(c1 >= c2, "c1 >= c2"); // Same suit, different rank.
|
||||
Assert.IsTrue(c2 >= c1, "c2 >= c1"); // Same suit, different rank.
|
||||
|
||||
Assert.IsTrue(c3 >= c4, "c3 >= c4"); // Same suit, same rank.
|
||||
|
||||
Assert.IsFalse(c1 >= c3, "c1 >= c3"); // Different suit, different rank.
|
||||
Assert.IsTrue(c3 >= c1, "c3 >= c1"); // Different suit, different rank.
|
||||
|
||||
Assert.IsTrue(c2 >= c4, "c2 >= c4"); // Different suit, same rank.
|
||||
Assert.IsTrue(c4 >= c2, "c4 >= c2"); // Different suit, same rank.
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ToStringIsValid()
|
||||
{
|
||||
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");
|
||||
Assert.IsTrue(s3 == "H-Q", "s3 invalid");
|
||||
Assert.IsTrue(s4 == "S-A", "s4 invalid");
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class DeckTest
|
||||
{
|
||||
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();
|
||||
|
||||
for (int i = 0; i < Deck.deckSize; i++)
|
||||
{
|
||||
sb.Append(d.GetCard(i));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void InitialDeckContainsCardsInOrder()
|
||||
{
|
||||
Deck d = new Deck();
|
||||
string allTheCards = ConcatenateTheDeck(d);
|
||||
|
||||
Assert.IsTrue(allTheCards == cardNamesInOrder);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShufflingChangesDeck()
|
||||
{
|
||||
// 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 != cardNamesInOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
94 War/csharp/War/WarTester/WarTester.csproj
Normal file
29
94 War/csharp/War/WarTester/WarTester.csproj
Normal file
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<Optimize>true</Optimize>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.3" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\War\War.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user