└── src ├── 01-Setup └── readme.md ├── 02-Simple-Object-Test └── answers │ ├── Card.cs │ ├── CardSuit.cs │ ├── CardValue.cs │ └── Tests │ └── CardTests.cs ├── 03-Hand └── answers │ ├── first-pass │ ├── Card.cs │ ├── CardSuit.cs │ ├── CardValue.cs │ ├── Hand.cs │ ├── Rank.cs │ └── Tests │ │ ├── CardTests.cs │ │ └── HandTests.cs │ └── refactored │ ├── Card.cs │ ├── CardSuit.cs │ ├── CardValue.cs │ ├── Hand.cs │ ├── Rank.cs │ └── Tests │ ├── CardTests.cs │ └── HandTests.cs ├── 04-Pairs └── answers │ ├── first-pass │ ├── Card.cs │ ├── CardSuit.cs │ ├── CardValue.cs │ ├── Hand.cs │ ├── Rank.cs │ └── Tests │ │ ├── CardTests.cs │ │ └── HandTests.cs │ └── refactored │ ├── Card.cs │ ├── CardSuit.cs │ ├── CardValue.cs │ ├── EvalExtensions.cs │ ├── Hand.cs │ ├── Rank.cs │ └── Tests │ ├── CardTests.cs │ └── HandTests.cs ├── 05-Sequences └── answers │ ├── first-pass │ ├── Card.cs │ ├── CardSuit.cs │ ├── CardValue.cs │ ├── EvalExtensions.cs │ ├── Hand.cs │ ├── Rank.cs │ └── Tests │ │ ├── CardTests.cs │ │ └── HandTests.cs │ └── refactored │ ├── Card.cs │ ├── CardSuit.cs │ ├── CardValue.cs │ ├── EvalExtensions.cs │ ├── Hand.cs │ ├── Rank.cs │ └── Tests │ ├── CardTests.cs │ └── HandTests.cs └── 06-Functional-refactor └── answers ├── Card.cs ├── CardSuit.cs ├── CardValue.cs ├── EvalExtensions.cs ├── FiveCardPokerScorer.cs ├── Hand.cs ├── Rank.cs ├── Ranker.cs └── Tests ├── CardTests.cs ├── HandTests.cs └── ScoreTests.cs /src/01-Setup/readme.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | - File > New Project > Class Library 4 | - Delete Class1.cs 5 | 6 | ## Install Unit Testing Framework 7 | 8 | - Add xUnit and the xUnit test runner 9 | - Open the package manager console, in quick launch, type: package manager console 10 | - In the package manager console run: 11 | - `Install-Package xunit` 12 | - `Install-Package xunit.runner.visualstudio` 13 | - `Install-Package FluentAssertions` -------------------------------------------------------------------------------- /src/02-Simple-Object-Test/answers/Card.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public class Card 4 | { 5 | public Card(CardValue value, CardSuit suit) 6 | { 7 | Value = value; 8 | Suit = suit; 9 | } 10 | 11 | public CardValue Value { get; } 12 | public CardSuit Suit { get; } 13 | 14 | public override string ToString() => $"{Value} of {Suit}"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/02-Simple-Object-Test/answers/CardSuit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace CsharpPoker 8 | { 9 | public enum CardSuit { Spades, Diamonds, Clubs, Hearts } 10 | } -------------------------------------------------------------------------------- /src/02-Simple-Object-Test/answers/CardValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace CsharpPoker 8 | { 9 | 10 | public enum CardValue { Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace } 11 | } 12 | -------------------------------------------------------------------------------- /src/02-Simple-Object-Test/answers/Tests/CardTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Xunit; 8 | using Xunit.Abstractions; 9 | 10 | namespace CsharpPoker 11 | { 12 | public class CardTests 13 | { 14 | 15 | [Fact] 16 | public void CanCreateCardWithValue() 17 | { 18 | 19 | var card = new Card(CardValue.Ace, CardSuit.Spades); 20 | 21 | Assert.Equal(CardSuit.Spades, card.Suit); 22 | Assert.Equal(CardValue.Ace, card.Value); 23 | } 24 | 25 | [Fact] 26 | public void CanCreateDescribeCard() 27 | { 28 | 29 | var card = new Card(CardValue.Ace, CardSuit.Spades); 30 | 31 | Assert.Equal("Ace of Spades", card.ToString()); 32 | 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/03-Hand/answers/first-pass/Card.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public class Card 4 | { 5 | public Card(CardValue value, CardSuit suit) 6 | { 7 | Suit = suit; 8 | Value = value; 9 | } 10 | 11 | public CardSuit Suit { get; } 12 | public CardValue Value { get; } 13 | 14 | public override string ToString() 15 | { 16 | return $"{Value} of {Suit}"; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/03-Hand/answers/first-pass/CardSuit.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum CardSuit { Spades, Diamonds, Clubs, Hearts } 4 | } -------------------------------------------------------------------------------- /src/03-Hand/answers/first-pass/CardValue.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | 4 | public enum CardValue { Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace } 5 | } 6 | -------------------------------------------------------------------------------- /src/03-Hand/answers/first-pass/Hand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Concurrent; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using CsharpPoker; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class Hand 12 | { 13 | // private readonly setter for cards, initialize a new List 14 | private readonly List cards = new List(); 15 | 16 | // To limit mutation of the Cards property, an IEnumerable is returned instead of List 17 | // Cards is mutable but only by calling Draw 18 | public IEnumerable Cards { get { return cards; } } 19 | 20 | public void Draw(Card card) 21 | { 22 | cards.Add(card); 23 | } 24 | 25 | // A LINQ Aggregate is used to find the HighCard 26 | public Card HighCard() 27 | { 28 | return cards.Aggregate((highCard, nextCard) => nextCard.Value > highCard.Value ? nextCard : highCard); 29 | 30 | // Imperative programming works too, but is very lengthy and introduces variables 31 | 32 | // Card highCard = Cards[0]; 33 | // foreach (var nextCard in Cards) 34 | // { 35 | // if (nextCard.Value > highCard.Value) 36 | // { 37 | // highCard = nextCard; 38 | // } 39 | // } 40 | // return highCard; 41 | 42 | // OrderBy is also valid, but could use more resources than Aggregate 43 | //return cards.OrderBy(c => c.Value).Last(); 44 | } 45 | 46 | // A Return Early pattern is used to call each hand ranking function 47 | public HandRank GetHandRank() 48 | { 49 | if (HasRoyalFlush()) return HandRank.RoyalFlush; 50 | if (HasFlush()) return HandRank.Flush; 51 | return HandRank.HighCard; 52 | } 53 | 54 | // A LINQ All method combined with First can check if all suits are the same value 55 | private bool HasFlush() 56 | { 57 | return cards.All(c => cards.First().Suit == c.Suit); 58 | } 59 | 60 | // A LINQ All method can determine if all cards are greater than Nine or [Ten, Jack, Queen, King, Ace ] 61 | public bool HasRoyalFlush() 62 | { 63 | return HasFlush() && cards.All(c => c.Value > CardValue.Nine); 64 | } 65 | 66 | } 67 | } -------------------------------------------------------------------------------- /src/03-Hand/answers/first-pass/Rank.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum HandRank 4 | { 5 | HighCard, 6 | Pair, 7 | TwoPair, 8 | ThreeOfAKind, 9 | Straight, 10 | Flush, 11 | FullHouse, 12 | FourOfAKind, 13 | StraightFlush, 14 | RoyalFlush 15 | } 16 | } -------------------------------------------------------------------------------- /src/03-Hand/answers/first-pass/Tests/CardTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace CsharpPoker 4 | { 5 | public class CardTests 6 | { 7 | 8 | [Fact] 9 | public void CanCreateCardWithValue() { 10 | 11 | var card = new Card(CardValue.Ace, CardSuit.Spades); 12 | 13 | Assert.Equal(CardSuit.Spades, card.Suit); 14 | Assert.Equal(CardValue.Ace, card.Value); 15 | } 16 | 17 | [Fact] 18 | public void CanCreateDescribeCard() 19 | { 20 | 21 | var card = new Card(CardValue.Ace, CardSuit.Spades); 22 | 23 | Assert.Equal("Ace of Spades", card.ToString()); 24 | 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/03-Hand/answers/first-pass/Tests/HandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | 8 | namespace CsharpPoker 9 | { 10 | public class HandTests 11 | { 12 | [Fact] 13 | public void CanCreateHand() 14 | { 15 | var hand = new Hand(); 16 | Assert.Equal(hand.Cards.Any(), false); 17 | } 18 | 19 | [Fact] 20 | public void CanHandDrawCard() 21 | { 22 | var card = new Card(CardValue.Ace, CardSuit.Spades); 23 | var hand = new Hand(); 24 | 25 | hand.Draw(card); 26 | 27 | Assert.Equal(hand.Cards.First(), card); 28 | } 29 | 30 | [Fact] 31 | public void CanGetHighCard() 32 | { 33 | var hand = new Hand(); 34 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 35 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 36 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 37 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 38 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 39 | Assert.Equal(CardValue.King, hand.HighCard().Value); 40 | } 41 | 42 | [Fact] 43 | public void CanScoreHighCard() 44 | { 45 | var hand = new Hand(); 46 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 47 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 48 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 49 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 50 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 51 | Assert.Equal(HandRank.HighCard, hand.GetHandRank()); 52 | } 53 | 54 | [Fact] 55 | public void CanScoreFlush() 56 | { 57 | var hand = new Hand(); 58 | hand.Draw(new Card(CardValue.Two, CardSuit.Spades)); 59 | hand.Draw(new Card(CardValue.Three, CardSuit.Spades)); 60 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 61 | hand.Draw(new Card(CardValue.Five, CardSuit.Spades)); 62 | hand.Draw(new Card(CardValue.Six, CardSuit.Spades)); 63 | Assert.Equal(HandRank.Flush, hand.GetHandRank()); 64 | } 65 | [Fact] 66 | public void CanScoreRoyalFlush() 67 | { 68 | var hand = new Hand(); 69 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 70 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 71 | hand.Draw(new Card(CardValue.Queen, CardSuit.Spades)); 72 | hand.Draw(new Card(CardValue.King, CardSuit.Spades)); 73 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 74 | Assert.Equal(HandRank.RoyalFlush, hand.GetHandRank()); 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/03-Hand/answers/refactored/Card.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public class Card 4 | { 5 | public Card(CardValue value, CardSuit suit) 6 | { 7 | Suit = suit; 8 | Value = value; 9 | } 10 | 11 | public CardSuit Suit { get; } 12 | public CardValue Value { get; } 13 | 14 | public override string ToString() => $"{Value} of {Suit}"; 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/03-Hand/answers/refactored/CardSuit.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum CardSuit { Spades, Diamonds, Clubs, Hearts } 4 | } -------------------------------------------------------------------------------- /src/03-Hand/answers/refactored/CardValue.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | 4 | public enum CardValue { Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace } 5 | } 6 | -------------------------------------------------------------------------------- /src/03-Hand/answers/refactored/Hand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Concurrent; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using CsharpPoker; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class Hand 12 | { 13 | private readonly List cards = new List(); 14 | 15 | // simplified to an expression-bodied member 16 | public IEnumerable Cards => cards; 17 | 18 | // simplified to an expression-bodied member 19 | public void Draw(Card card) => cards.Add(card); 20 | 21 | // simplified to an expression-bodied member 22 | public Card HighCard() => cards.Aggregate((result, nextCard) => result.Value > nextCard.Value ? result : nextCard); 23 | 24 | // The return early pattern can be replaced with tenary operators 25 | // then shortended to an expression-bodied member 26 | public HandRank GetHandRank() => 27 | HasRoyalFlush() ? HandRank.RoyalFlush : 28 | HasFlush() ? HandRank.Flush : 29 | HandRank.HighCard; 30 | 31 | // simplified to an expression-bodied member 32 | private bool HasFlush() => cards.All(c => cards.First().Suit == c.Suit); 33 | 34 | // simplified to an expression-bodied member 35 | public bool HasRoyalFlush() => HasFlush() && cards.All(c => c.Value > CardValue.Nine); 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/03-Hand/answers/refactored/Rank.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum HandRank 4 | { 5 | HighCard, 6 | Pair, 7 | TwoPair, 8 | ThreeOfAKind, 9 | Straight, 10 | Flush, 11 | FullHouse, 12 | FourOfAKind, 13 | StraightFlush, 14 | RoyalFlush 15 | } 16 | } -------------------------------------------------------------------------------- /src/03-Hand/answers/refactored/Tests/CardTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace CsharpPoker 4 | { 5 | public class CardTests 6 | { 7 | 8 | [Fact] 9 | public void CanCreateCardWithValue() { 10 | 11 | var card = new Card(CardValue.Ace, CardSuit.Spades); 12 | 13 | Assert.Equal(CardSuit.Spades, card.Suit); 14 | Assert.Equal(CardValue.Ace, card.Value); 15 | } 16 | 17 | [Fact] 18 | public void CanCreateDescribeCard() 19 | { 20 | 21 | var card = new Card(CardValue.Ace, CardSuit.Spades); 22 | 23 | Assert.Equal("Ace of Spades", card.ToString()); 24 | 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/03-Hand/answers/refactored/Tests/HandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using FluentAssertions; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class HandTests 12 | { 13 | [Fact] 14 | public void CanCreateHand() 15 | { 16 | var hand = new Hand(); 17 | hand.Cards.Any().Should().BeFalse(); 18 | } 19 | 20 | [Fact] 21 | public void CanHandDrawCard() 22 | { 23 | var card = new Card(CardValue.Ace, CardSuit.Spades); 24 | var hand = new Hand(); 25 | 26 | hand.Draw(card); 27 | 28 | hand.Cards.First().Should().Be(card); 29 | } 30 | 31 | [Fact] 32 | public void CanGetHighCard() 33 | { 34 | var hand = new Hand(); 35 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 36 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 37 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 38 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 39 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 40 | hand.HighCard().Value.Should().Be(CardValue.King); 41 | } 42 | 43 | [Fact] 44 | public void CanScoreHighCard() 45 | { 46 | var hand = new Hand(); 47 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 48 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 49 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 50 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 51 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 52 | hand.GetHandRank().Should().Be(HandRank.HighCard); 53 | } 54 | 55 | [Fact] 56 | public void CanScoreFlush() 57 | { 58 | var hand = new Hand(); 59 | hand.Draw(new Card(CardValue.Two, CardSuit.Spades)); 60 | hand.Draw(new Card(CardValue.Three, CardSuit.Spades)); 61 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 62 | hand.Draw(new Card(CardValue.Five, CardSuit.Spades)); 63 | hand.Draw(new Card(CardValue.Six, CardSuit.Spades)); 64 | hand.GetHandRank().Should().Be(HandRank.Flush); 65 | } 66 | [Fact] 67 | public void CanScoreRoyalFlush() 68 | { 69 | var hand = new Hand(); 70 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 71 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 72 | hand.Draw(new Card(CardValue.Queen, CardSuit.Spades)); 73 | hand.Draw(new Card(CardValue.King, CardSuit.Spades)); 74 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 75 | hand.GetHandRank().Should().Be(HandRank.RoyalFlush); 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/04-Pairs/answers/first-pass/Card.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public class Card 4 | { 5 | public Card(CardValue value, CardSuit suit) 6 | { 7 | Suit = suit; 8 | Value = value; 9 | } 10 | 11 | public CardSuit Suit { get; } 12 | public CardValue Value { get; } 13 | 14 | public override string ToString() => $"{Value} of {Suit}"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/04-Pairs/answers/first-pass/CardSuit.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum CardSuit { Spades, Diamonds, Clubs, Hearts } 4 | } -------------------------------------------------------------------------------- /src/04-Pairs/answers/first-pass/CardValue.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | 4 | public enum CardValue { Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace } 5 | } 6 | -------------------------------------------------------------------------------- /src/04-Pairs/answers/first-pass/Hand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Concurrent; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using CsharpPoker; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class Hand 12 | { 13 | private readonly List cards = new List(); 14 | 15 | public IEnumerable Cards { get { return cards; } } 16 | 17 | public void Draw(Card card) => cards.Add(card); 18 | 19 | public Card HighCard() => cards.Aggregate((result, nextCard) => result.Value > nextCard.Value ? result : nextCard); 20 | 21 | public HandRank GetHandRank() => 22 | HasRoyalFlush() ? HandRank.RoyalFlush : 23 | HasFlush() ? HandRank.Flush : 24 | HasFullHouse() ? HandRank.FullHouse : 25 | HasFourOfAKind() ? HandRank.FourOfAKind : 26 | HasThreeOfAKind() ? HandRank.ThreeOfAKind : 27 | HasPair() ? HandRank.Pair : 28 | HandRank.HighCard; 29 | 30 | private bool HasFlush() => cards.All(c => cards.First().Suit == c.Suit); 31 | 32 | private bool HasRoyalFlush() => HasFlush() && cards.All(c => c.Value > CardValue.Nine); 33 | 34 | // The Any LINQ method validates that there are dictionary items with a specified pair count value. 35 | private bool HasOfAKind(int num) => GetPairs(cards).Any(c => c.Value == num); 36 | 37 | private bool HasPair() => HasOfAKind(2); 38 | private bool HasThreeOfAKind() => HasOfAKind(3); 39 | private bool HasFourOfAKind() => HasOfAKind(4); 40 | 41 | private bool HasFullHouse() => HasThreeOfAKind() && HasPair(); 42 | 43 | /* Since a collection of pairs needs to be built, a mutable collection must be temporarily created. 44 | Because the scope is limited, risk for side-effect is minimal. In addition, the ConcurrentDictionary's AddOrUpdate method is Thread Safe. 45 | While the ConcurrentDictionary mutates state via AddOrUpdate, it does use a functional principal called a higher order function. 46 | Higher order functions are simply a function that accepts or returns another function. 47 | The AddOrUpdate method accepts a function which delegates how the item is updated. 48 | */ 49 | private IEnumerable> GetPairs(IEnumerable cards) 50 | { 51 | var dict = new ConcurrentDictionary(); 52 | foreach (var card in cards) 53 | { 54 | // Add the value to the dictionary, or increase the count 55 | dict.AddOrUpdate(card.Value, 1, (cardValue, quantity) => ++quantity); 56 | } 57 | return dict; 58 | } 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /src/04-Pairs/answers/first-pass/Rank.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum HandRank 4 | { 5 | HighCard, 6 | Pair, 7 | TwoPair, 8 | ThreeOfAKind, 9 | Straight, 10 | Flush, 11 | FullHouse, 12 | FourOfAKind, 13 | StraightFlush, 14 | RoyalFlush 15 | } 16 | } -------------------------------------------------------------------------------- /src/04-Pairs/answers/first-pass/Tests/CardTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace CsharpPoker 4 | { 5 | public class CardTests 6 | { 7 | 8 | [Fact] 9 | public void CanCreateCardWithValue() { 10 | 11 | var card = new Card(CardValue.Ace, CardSuit.Spades); 12 | 13 | Assert.Equal(CardSuit.Spades, card.Suit); 14 | Assert.Equal(CardValue.Ace, card.Value); 15 | } 16 | 17 | [Fact] 18 | public void CanCreateDescribeCard() 19 | { 20 | 21 | var card = new Card(CardValue.Ace, CardSuit.Spades); 22 | 23 | Assert.Equal("Ace of Spades", card.ToString()); 24 | 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/04-Pairs/answers/first-pass/Tests/HandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using FluentAssertions; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class HandTests 12 | { 13 | [Fact] 14 | public void CanCreateHand() 15 | { 16 | var hand = new Hand(); 17 | hand.Cards.Any().Should().BeFalse(); 18 | } 19 | 20 | [Fact] 21 | public void CanHandDrawCard() 22 | { 23 | var card = new Card(CardValue.Ace, CardSuit.Spades); 24 | var hand = new Hand(); 25 | 26 | hand.Draw(card); 27 | 28 | hand.Cards.First().Should().Be(card); 29 | } 30 | 31 | [Fact] 32 | public void CanGetHighCard() 33 | { 34 | var hand = new Hand(); 35 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 36 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 37 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 38 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 39 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 40 | hand.HighCard().Value.Should().Be(CardValue.King); 41 | } 42 | 43 | [Fact] 44 | public void CanScoreHighCard() 45 | { 46 | var hand = new Hand(); 47 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 48 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 49 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 50 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 51 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 52 | hand.GetHandRank().Should().Be(HandRank.HighCard); 53 | } 54 | 55 | [Fact] 56 | public void CanScoreFlush() 57 | { 58 | var hand = new Hand(); 59 | hand.Draw(new Card(CardValue.Two, CardSuit.Spades)); 60 | hand.Draw(new Card(CardValue.Three, CardSuit.Spades)); 61 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 62 | hand.Draw(new Card(CardValue.Five, CardSuit.Spades)); 63 | hand.Draw(new Card(CardValue.Six, CardSuit.Spades)); 64 | hand.GetHandRank().Should().Be(HandRank.Flush); 65 | } 66 | [Fact] 67 | public void CanScoreRoyalFlush() 68 | { 69 | var hand = new Hand(); 70 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 71 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 72 | hand.Draw(new Card(CardValue.Queen, CardSuit.Spades)); 73 | hand.Draw(new Card(CardValue.King, CardSuit.Spades)); 74 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 75 | hand.GetHandRank().Should().Be(HandRank.RoyalFlush); 76 | } 77 | [Fact] 78 | public void CanScorePair() 79 | { 80 | var hand = new Hand(); 81 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 82 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 83 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 84 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 85 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 86 | hand.GetHandRank().Should().Be(HandRank.Pair); 87 | } 88 | 89 | [Fact] 90 | public void CanScoreThreeOfAKind() 91 | { 92 | var hand = new Hand(); 93 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 94 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 95 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 96 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 97 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 98 | hand.GetHandRank().Should().Be(HandRank.ThreeOfAKind); 99 | } 100 | [Fact] 101 | public void CanScoreFourOfAKind() 102 | { 103 | var hand = new Hand(); 104 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 105 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 106 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 107 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 108 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 109 | hand.GetHandRank().Should().Be(HandRank.FourOfAKind); 110 | 111 | } 112 | [Fact] 113 | public void CanScoreFullHouse() 114 | { 115 | var hand = new Hand(); 116 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 117 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 118 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 119 | hand.Draw(new Card(CardValue.Jack, CardSuit.Hearts)); 120 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 121 | hand.GetHandRank().Should().Be(HandRank.FullHouse); 122 | 123 | } 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/04-Pairs/answers/refactored/Card.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public class Card 4 | { 5 | public Card(CardValue value, CardSuit suit) 6 | { 7 | Suit = suit; 8 | Value = value; 9 | } 10 | 11 | public CardSuit Suit { get; } 12 | public CardValue Value { get; } 13 | 14 | public override string ToString() => $"{Value} of {Suit}"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/04-Pairs/answers/refactored/CardSuit.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum CardSuit { Spades, Diamonds, Clubs, Hearts } 4 | } -------------------------------------------------------------------------------- /src/04-Pairs/answers/refactored/CardValue.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | 4 | public enum CardValue { Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace } 5 | } 6 | -------------------------------------------------------------------------------- /src/04-Pairs/answers/refactored/EvalExtensions.cs: -------------------------------------------------------------------------------- 1 | using CsharpPoker; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | 5 | public static class EvalExtensions { 6 | 7 | // Extension methods are an easy way to create a pipeline. 8 | // The ToPairs extension method is a specialized mapping method to convert from IEnumerable to IEnumerable> (cards, to counted pairs) 9 | // LINQ is a great example of how effective extension methods can be. 10 | public static IEnumerable> ToPairs(this IEnumerable cards) 11 | { 12 | var dict = new ConcurrentDictionary(); 13 | foreach (var card in cards) 14 | { 15 | dict.AddOrUpdate(card.Value, 1, (cardValue, quantity) => ++quantity); 16 | } 17 | return dict; 18 | } 19 | } -------------------------------------------------------------------------------- /src/04-Pairs/answers/refactored/Hand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Concurrent; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using CsharpPoker; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class Hand 12 | { 13 | private readonly List cards = new List(); 14 | 15 | public IEnumerable Cards { get { return cards; } } 16 | 17 | public void Draw(Card card) => cards.Add(card); 18 | 19 | public Card HighCard() => cards.Aggregate((result, nextCard) => result.Value > nextCard.Value ? result : nextCard); 20 | 21 | public HandRank GetHandRank() => 22 | HasRoyalFlush() ? HandRank.RoyalFlush : 23 | HasFlush() ? HandRank.Flush : 24 | HasFullHouse() ? HandRank.FullHouse : 25 | HasFourOfAKind() ? HandRank.FourOfAKind : 26 | HasThreeOfAKind() ? HandRank.ThreeOfAKind : 27 | HasPair() ? HandRank.Pair : 28 | HandRank.HighCard; 29 | 30 | private bool HasFlush() => cards.All(c => cards.First().Suit == c.Suit); 31 | 32 | private bool HasRoyalFlush() => HasFlush() && cards.All(c => c.Value > CardValue.Nine); 33 | 34 | // The ToPairs extension method maps a collection of cards, to a collection of pairs. 35 | private bool HasOfAKind(int num) => cards.ToPairs().Any(c => c.Value == num); 36 | 37 | private bool HasPair() => HasOfAKind(2); 38 | private bool HasThreeOfAKind() => HasOfAKind(3); 39 | private bool HasFourOfAKind() => HasOfAKind(4); 40 | 41 | private bool HasFullHouse() => HasThreeOfAKind() && HasPair(); 42 | 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /src/04-Pairs/answers/refactored/Rank.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum HandRank 4 | { 5 | HighCard, 6 | Pair, 7 | TwoPair, 8 | ThreeOfAKind, 9 | Straight, 10 | Flush, 11 | FullHouse, 12 | FourOfAKind, 13 | StraightFlush, 14 | RoyalFlush 15 | } 16 | } -------------------------------------------------------------------------------- /src/04-Pairs/answers/refactored/Tests/CardTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace CsharpPoker 4 | { 5 | public class CardTests 6 | { 7 | 8 | [Fact] 9 | public void CanCreateCardWithValue() { 10 | 11 | var card = new Card(CardValue.Ace, CardSuit.Spades); 12 | 13 | Assert.Equal(CardSuit.Spades, card.Suit); 14 | Assert.Equal(CardValue.Ace, card.Value); 15 | } 16 | 17 | [Fact] 18 | public void CanCreateDescribeCard() 19 | { 20 | 21 | var card = new Card(CardValue.Ace, CardSuit.Spades); 22 | 23 | Assert.Equal("Ace of Spades", card.ToString()); 24 | 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/04-Pairs/answers/refactored/Tests/HandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using FluentAssertions; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class HandTests 12 | { 13 | [Fact] 14 | public void CanCreateHand() 15 | { 16 | var hand = new Hand(); 17 | hand.Cards.Any().Should().BeFalse(); 18 | } 19 | 20 | [Fact] 21 | public void CanHandDrawCard() 22 | { 23 | var card = new Card(CardValue.Ace, CardSuit.Spades); 24 | var hand = new Hand(); 25 | 26 | hand.Draw(card); 27 | hand.Cards.First().Should().Be(card); 28 | } 29 | 30 | [Fact] 31 | public void CanGetHighCard() 32 | { 33 | var hand = new Hand(); 34 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 35 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 36 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 37 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 38 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 39 | 40 | hand.HighCard().Value.Should().Be(CardValue.King); 41 | } 42 | 43 | [Fact] 44 | public void CanScoreHighCard() 45 | { 46 | var hand = new Hand(); 47 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 48 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 49 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 50 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 51 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 52 | 53 | hand.GetHandRank().Should().Be(HandRank.HighCard); 54 | } 55 | 56 | [Fact] 57 | public void CanScoreFlush() 58 | { 59 | var hand = new Hand(); 60 | hand.Draw(new Card(CardValue.Two, CardSuit.Spades)); 61 | hand.Draw(new Card(CardValue.Three, CardSuit.Spades)); 62 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 63 | hand.Draw(new Card(CardValue.Five, CardSuit.Spades)); 64 | hand.Draw(new Card(CardValue.Six, CardSuit.Spades)); 65 | 66 | hand.GetHandRank().Should().Be(HandRank.Flush); 67 | } 68 | [Fact] 69 | public void CanScoreRoyalFlush() 70 | { 71 | var hand = new Hand(); 72 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 73 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 74 | hand.Draw(new Card(CardValue.Queen, CardSuit.Spades)); 75 | hand.Draw(new Card(CardValue.King, CardSuit.Spades)); 76 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 77 | 78 | hand.GetHandRank().Should().Be(HandRank.RoyalFlush); 79 | } 80 | 81 | [Fact] 82 | public void CanScorePair() 83 | { 84 | var hand = new Hand(); 85 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 86 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 87 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 88 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 89 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 90 | 91 | hand.GetHandRank().Should().Be(HandRank.Pair); 92 | 93 | } 94 | 95 | [Fact] 96 | public void CanScoreThreeOfAKind() 97 | { 98 | var hand = new Hand(); 99 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 100 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 101 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 102 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 103 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 104 | 105 | hand.GetHandRank().Should().Be(HandRank.ThreeOfAKind); 106 | 107 | } 108 | 109 | [Fact] 110 | public void CanScoreFourOfAKind() 111 | { 112 | var hand = new Hand(); 113 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 114 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 115 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 116 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 117 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 118 | 119 | hand.GetHandRank().Should().Be(HandRank.FourOfAKind); 120 | 121 | } 122 | [Fact] 123 | public void CanScoreFullHouse() 124 | { 125 | var hand = new Hand(); 126 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 127 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 128 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 129 | hand.Draw(new Card(CardValue.Jack, CardSuit.Hearts)); 130 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 131 | 132 | hand.GetHandRank().Should().Be(HandRank.FullHouse); 133 | } 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/05-Sequences/answers/first-pass/Card.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public class Card 4 | { 5 | public Card(CardValue value, CardSuit suit) 6 | { 7 | Suit = suit; 8 | Value = value; 9 | } 10 | 11 | public CardSuit Suit { get; } 12 | public CardValue Value { get; } 13 | 14 | public override string ToString() => $"{Value} of {Suit}"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/05-Sequences/answers/first-pass/CardSuit.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum CardSuit { Spades, Diamonds, Clubs, Hearts } 4 | } -------------------------------------------------------------------------------- /src/05-Sequences/answers/first-pass/CardValue.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | 4 | public enum CardValue { Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace } 5 | } 6 | -------------------------------------------------------------------------------- /src/05-Sequences/answers/first-pass/EvalExtensions.cs: -------------------------------------------------------------------------------- 1 | using CsharpPoker; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | 5 | public static class EvalExtensions { 6 | 7 | public static IEnumerable> ToPairs(this IEnumerable cards) 8 | { 9 | var dict = new ConcurrentDictionary(); 10 | foreach (var card in cards) 11 | { 12 | dict.AddOrUpdate(card.Value, 1, (cardValue, quantity) => ++quantity); 13 | } 14 | return dict; 15 | } 16 | } -------------------------------------------------------------------------------- /src/05-Sequences/answers/first-pass/Hand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Concurrent; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using CsharpPoker; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class Hand 12 | { 13 | private readonly List cards = new List(); 14 | 15 | public IEnumerable Cards { get { return cards; } } 16 | 17 | public void Draw(Card card) => cards.Add(card); 18 | 19 | public Card HighCard() => cards.Aggregate((result, nextCard) => result.Value > nextCard.Value ? result : nextCard); 20 | 21 | public HandRank GetHandRank() => 22 | HasRoyalFlush() ? HandRank.RoyalFlush : 23 | HasStraightFlush() ? HandRank.StraightFlush : 24 | HasStraight() ? HandRank.Straight : 25 | HasFlush() ? HandRank.Flush : 26 | HasFullHouse() ? HandRank.FullHouse : 27 | HasFourOfAKind() ? HandRank.FourOfAKind : 28 | HasThreeOfAKind() ? HandRank.ThreeOfAKind : 29 | HasPair() ? HandRank.Pair : 30 | HandRank.HighCard; 31 | 32 | private bool HasFlush() => cards.All(c => cards.First().Suit == c.Suit); 33 | 34 | private bool HasRoyalFlush() => HasFlush() && cards.All(c => c.Value > CardValue.Nine); 35 | 36 | private bool HasOfAKind(int num) => cards.ToPairs().Any(c => c.Value == num); 37 | 38 | private bool HasPair() => HasOfAKind(2); 39 | private bool HasThreeOfAKind() => HasOfAKind(3); 40 | private bool HasFourOfAKind() => HasOfAKind(4); 41 | 42 | private bool HasFullHouse() => HasThreeOfAKind() && HasPair(); 43 | 44 | private bool HasStraight() => cards.OrderBy(card => card.Value).Zip(cards.OrderBy(card => card.Value).Skip(1), (n, next) => n.Value + 1 == next.Value).All(value => value ); 45 | 46 | private bool HasStraightFlush() => HasStraight() && HasFlush(); 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /src/05-Sequences/answers/first-pass/Rank.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum HandRank 4 | { 5 | HighCard, 6 | Pair, 7 | TwoPair, 8 | ThreeOfAKind, 9 | Straight, 10 | Flush, 11 | FullHouse, 12 | FourOfAKind, 13 | StraightFlush, 14 | RoyalFlush 15 | } 16 | } -------------------------------------------------------------------------------- /src/05-Sequences/answers/first-pass/Tests/CardTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace CsharpPoker 4 | { 5 | public class CardTests 6 | { 7 | 8 | [Fact] 9 | public void CanCreateCardWithValue() { 10 | 11 | var card = new Card(CardValue.Ace, CardSuit.Spades); 12 | 13 | Assert.Equal(CardSuit.Spades, card.Suit); 14 | Assert.Equal(CardValue.Ace, card.Value); 15 | } 16 | 17 | [Fact] 18 | public void CanCreateDescribeCard() 19 | { 20 | 21 | var card = new Card(CardValue.Ace, CardSuit.Spades); 22 | 23 | Assert.Equal("Ace of Spades", card.ToString()); 24 | 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/05-Sequences/answers/first-pass/Tests/HandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using FluentAssertions; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class HandTests 12 | { 13 | [Fact] 14 | public void CanCreateHand() 15 | { 16 | var hand = new Hand(); 17 | hand.Cards.Any().Should().BeFalse(); 18 | } 19 | 20 | [Fact] 21 | public void CanHandDrawCard() 22 | { 23 | var card = new Card(CardValue.Ace, CardSuit.Spades); 24 | var hand = new Hand(); 25 | 26 | hand.Draw(card); 27 | hand.Cards.First().Should().Be(card); 28 | } 29 | 30 | [Fact] 31 | public void CanGetHighCard() 32 | { 33 | var hand = new Hand(); 34 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 35 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 36 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 37 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 38 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 39 | 40 | hand.HighCard().Value.Should().Be(CardValue.King); 41 | } 42 | 43 | [Fact] 44 | public void CanScoreHighCard() 45 | { 46 | var hand = new Hand(); 47 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 48 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 49 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 50 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 51 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 52 | 53 | hand.GetHandRank().Should().Be(HandRank.HighCard); 54 | } 55 | 56 | [Fact] 57 | public void CanScoreFlush() 58 | { 59 | var hand = new Hand(); 60 | hand.Draw(new Card(CardValue.Two, CardSuit.Spades)); 61 | hand.Draw(new Card(CardValue.Three, CardSuit.Spades)); 62 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 63 | hand.Draw(new Card(CardValue.Five, CardSuit.Spades)); 64 | hand.Draw(new Card(CardValue.Six, CardSuit.Spades)); 65 | 66 | hand.GetHandRank().Should().Be(HandRank.Flush); 67 | } 68 | [Fact] 69 | public void CanScoreStraightFlush() 70 | { 71 | var hand = new Hand(); 72 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 73 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 74 | hand.Draw(new Card(CardValue.Eight, CardSuit.Spades)); 75 | hand.Draw(new Card(CardValue.Six, CardSuit.Spades)); 76 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 77 | 78 | hand.GetHandRank().Should().Be(HandRank.StraightFlush); 79 | } 80 | 81 | [Fact] 82 | public void CanScoreRoyalFlush() 83 | { 84 | var hand = new Hand(); 85 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 86 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 87 | hand.Draw(new Card(CardValue.Queen, CardSuit.Spades)); 88 | hand.Draw(new Card(CardValue.King, CardSuit.Spades)); 89 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 90 | 91 | hand.GetHandRank().Should().Be(HandRank.RoyalFlush); 92 | } 93 | 94 | [Fact] 95 | public void CanScorePair() 96 | { 97 | var hand = new Hand(); 98 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 99 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 100 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 101 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 102 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 103 | 104 | hand.GetHandRank().Should().Be(HandRank.Pair); 105 | 106 | } 107 | 108 | [Fact] 109 | public void CanScoreThreeOfAKind() 110 | { 111 | var hand = new Hand(); 112 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 113 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 114 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 115 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 116 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 117 | 118 | hand.GetHandRank().Should().Be(HandRank.ThreeOfAKind); 119 | 120 | } 121 | 122 | [Fact] 123 | public void CanScoreFourOfAKind() 124 | { 125 | var hand = new Hand(); 126 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 127 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 128 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 129 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 130 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 131 | 132 | hand.GetHandRank().Should().Be(HandRank.FourOfAKind); 133 | 134 | } 135 | [Fact] 136 | public void CanScoreFullHouse() 137 | { 138 | var hand = new Hand(); 139 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 140 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 141 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 142 | hand.Draw(new Card(CardValue.Jack, CardSuit.Hearts)); 143 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 144 | 145 | hand.GetHandRank().Should().Be(HandRank.FullHouse); 146 | } 147 | 148 | [Fact] 149 | public void CanScoreStraight() 150 | { 151 | var hand = new Hand(); 152 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 153 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 154 | hand.Draw(new Card(CardValue.Queen, CardSuit.Spades)); 155 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 156 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 157 | 158 | hand.GetHandRank().Should().Be(HandRank.Straight); 159 | } 160 | 161 | [Fact] 162 | public void CanScoreStraightUnordered() 163 | { 164 | var hand = new Hand(); 165 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 166 | hand.Draw(new Card(CardValue.Queen, CardSuit.Clubs)); 167 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 168 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 169 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 170 | 171 | hand.GetHandRank().Should().Be(HandRank.Straight); 172 | } 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /src/05-Sequences/answers/refactored/Card.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public class Card 4 | { 5 | public Card(CardValue value, CardSuit suit) 6 | { 7 | Suit = suit; 8 | Value = value; 9 | } 10 | 11 | public CardSuit Suit { get; } 12 | public CardValue Value { get; } 13 | 14 | public override string ToString() => $"{Value} of {Suit}"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/05-Sequences/answers/refactored/CardSuit.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum CardSuit { Spades, Diamonds, Clubs, Hearts } 4 | } -------------------------------------------------------------------------------- /src/05-Sequences/answers/refactored/CardValue.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | 4 | public enum CardValue { Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace } 5 | } 6 | -------------------------------------------------------------------------------- /src/05-Sequences/answers/refactored/EvalExtensions.cs: -------------------------------------------------------------------------------- 1 | using CsharpPoker; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | public static class EvalExtensions { 8 | 9 | public static IEnumerable> ToPairs(this IEnumerable cards) 10 | { 11 | var dict = new ConcurrentDictionary(); 12 | foreach (var card in cards) 13 | { 14 | dict.AddOrUpdate(card.Value, 1, (cardValue, quantity) => ++quantity); 15 | } 16 | return dict; 17 | 18 | } 19 | 20 | public static IEnumerable SelectConsecutive(this IEnumerable source, Func selector) 21 | { 22 | int index = -1; 23 | foreach (TSource element in source.Take(source.Count() - 1)) 24 | { 25 | checked { index++; } 26 | yield return selector(element, source.ElementAt(index + 1)); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/05-Sequences/answers/refactored/Hand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Concurrent; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using CsharpPoker; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class Hand 12 | { 13 | private readonly List cards = new List(); 14 | 15 | public IEnumerable Cards { get { return cards; } } 16 | 17 | public void Draw(Card card) => cards.Add(card); 18 | 19 | public Card HighCard() => cards.Aggregate((result, nextCard) => result.Value > nextCard.Value ? result : nextCard); 20 | 21 | public HandRank GetHandRank() => 22 | HasRoyalFlush() ? HandRank.RoyalFlush : 23 | HasStraightFlush() ? HandRank.StraightFlush : 24 | HasStraight() ? HandRank.Straight : 25 | HasFlush() ? HandRank.Flush : 26 | HasFullHouse() ? HandRank.FullHouse : 27 | HasFourOfAKind() ? HandRank.FourOfAKind : 28 | HasThreeOfAKind() ? HandRank.ThreeOfAKind : 29 | HasPair() ? HandRank.Pair : 30 | HandRank.HighCard; 31 | 32 | private bool HasFlush() => cards.All(c => cards.First().Suit == c.Suit); 33 | 34 | private bool HasRoyalFlush() => HasFlush() && cards.All(c => c.Value > CardValue.Nine); 35 | 36 | private bool HasOfAKind(int num) => cards.ToPairs().Any(c => c.Value == num); 37 | 38 | private bool HasPair() => HasOfAKind(2); 39 | private bool HasThreeOfAKind() => HasOfAKind(3); 40 | private bool HasFourOfAKind() => HasOfAKind(4); 41 | 42 | private bool HasFullHouse() => HasThreeOfAKind() && HasPair(); 43 | 44 | private bool HasStraight() => //cards.OrderBy(card => card.Value).Zip(cards.OrderBy(card => card.Value).Skip(1), (n, next) => n.Value + 1 == next.Value).All(value => value ); 45 | cards.OrderBy(card => card.Value).SelectConsecutive((n, next) => n.Value + 1 == next.Value).All(value => value); 46 | 47 | private bool HasStraightFlush() => HasStraight() && HasFlush(); 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /src/05-Sequences/answers/refactored/Rank.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum HandRank 4 | { 5 | HighCard, 6 | Pair, 7 | TwoPair, 8 | ThreeOfAKind, 9 | Straight, 10 | Flush, 11 | FullHouse, 12 | FourOfAKind, 13 | StraightFlush, 14 | RoyalFlush 15 | } 16 | } -------------------------------------------------------------------------------- /src/05-Sequences/answers/refactored/Tests/CardTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace CsharpPoker 4 | { 5 | public class CardTests 6 | { 7 | 8 | [Fact] 9 | public void CanCreateCardWithValue() { 10 | 11 | var card = new Card(CardValue.Ace, CardSuit.Spades); 12 | 13 | Assert.Equal(CardSuit.Spades, card.Suit); 14 | Assert.Equal(CardValue.Ace, card.Value); 15 | } 16 | 17 | [Fact] 18 | public void CanCreateDescribeCard() 19 | { 20 | 21 | var card = new Card(CardValue.Ace, CardSuit.Spades); 22 | 23 | Assert.Equal("Ace of Spades", card.ToString()); 24 | 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/05-Sequences/answers/refactored/Tests/HandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using FluentAssertions; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class HandTests 12 | { 13 | [Fact] 14 | public void CanCreateHand() 15 | { 16 | var hand = new Hand(); 17 | hand.Cards.Any().Should().BeFalse(); 18 | } 19 | 20 | [Fact] 21 | public void CanHandDrawCard() 22 | { 23 | var card = new Card(CardValue.Ace, CardSuit.Spades); 24 | var hand = new Hand(); 25 | 26 | hand.Draw(card); 27 | hand.Cards.First().Should().Be(card); 28 | } 29 | 30 | [Fact] 31 | public void CanGetHighCard() 32 | { 33 | var hand = new Hand(); 34 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 35 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 36 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 37 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 38 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 39 | 40 | hand.HighCard().Value.Should().Be(CardValue.King); 41 | } 42 | 43 | [Fact] 44 | public void CanScoreHighCard() 45 | { 46 | var hand = new Hand(); 47 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 48 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 49 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 50 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 51 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 52 | 53 | hand.GetHandRank().Should().Be(HandRank.HighCard); 54 | } 55 | 56 | [Fact] 57 | public void CanScoreFlush() 58 | { 59 | var hand = new Hand(); 60 | hand.Draw(new Card(CardValue.Two, CardSuit.Spades)); 61 | hand.Draw(new Card(CardValue.Three, CardSuit.Spades)); 62 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 63 | hand.Draw(new Card(CardValue.Five, CardSuit.Spades)); 64 | hand.Draw(new Card(CardValue.Six, CardSuit.Spades)); 65 | 66 | hand.GetHandRank().Should().Be(HandRank.Flush); 67 | } 68 | 69 | [Fact] 70 | public void CanScoreRoyalFlush() 71 | { 72 | var hand = new Hand(); 73 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 74 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 75 | hand.Draw(new Card(CardValue.Queen, CardSuit.Spades)); 76 | hand.Draw(new Card(CardValue.King, CardSuit.Spades)); 77 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 78 | 79 | hand.GetHandRank().Should().Be(HandRank.RoyalFlush); 80 | } 81 | 82 | 83 | [Fact] 84 | public void CanScorePair() 85 | { 86 | var hand = new Hand(); 87 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 88 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 89 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 90 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 91 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 92 | 93 | hand.GetHandRank().Should().Be(HandRank.Pair); 94 | 95 | } 96 | 97 | [Fact] 98 | public void CanScoreThreeOfAKind() 99 | { 100 | var hand = new Hand(); 101 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 102 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 103 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 104 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 105 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 106 | 107 | hand.GetHandRank().Should().Be(HandRank.ThreeOfAKind); 108 | 109 | } 110 | 111 | [Fact] 112 | public void CanScoreFourOfAKind() 113 | { 114 | var hand = new Hand(); 115 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 116 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 117 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 118 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 119 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 120 | 121 | hand.GetHandRank().Should().Be(HandRank.FourOfAKind); 122 | 123 | } 124 | [Fact] 125 | public void CanScoreFullHouse() 126 | { 127 | var hand = new Hand(); 128 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 129 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 130 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 131 | hand.Draw(new Card(CardValue.Jack, CardSuit.Hearts)); 132 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 133 | 134 | hand.GetHandRank().Should().Be(HandRank.FullHouse); 135 | } 136 | 137 | [Fact] 138 | public void CanScoreStraight() 139 | { 140 | var hand = new Hand(); 141 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 142 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 143 | hand.Draw(new Card(CardValue.Queen, CardSuit.Spades)); 144 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 145 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 146 | 147 | hand.GetHandRank().Should().Be(HandRank.Straight); 148 | } 149 | 150 | [Fact] 151 | public void CanScoreStraightUnordered() 152 | { 153 | var hand = new Hand(); 154 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 155 | hand.Draw(new Card(CardValue.Queen, CardSuit.Clubs)); 156 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 157 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 158 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 159 | 160 | hand.GetHandRank().Should().Be(HandRank.Straight); 161 | } 162 | 163 | [Fact] 164 | public void CanScoreStraightFlush() 165 | { 166 | var hand = new Hand(); 167 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 168 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 169 | hand.Draw(new Card(CardValue.Eight, CardSuit.Spades)); 170 | hand.Draw(new Card(CardValue.Six, CardSuit.Spades)); 171 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 172 | 173 | hand.GetHandRank().Should().Be(HandRank.StraightFlush); 174 | } 175 | 176 | 177 | 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /src/06-Functional-refactor/answers/Card.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public class Card 4 | { 5 | public Card(CardValue value, CardSuit suit) 6 | { 7 | Suit = suit; 8 | Value = value; 9 | } 10 | 11 | public CardSuit Suit { get; } 12 | public CardValue Value { get; } 13 | 14 | public override string ToString() => $"{Value} of {Suit}"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/06-Functional-refactor/answers/CardSuit.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | public enum CardSuit { Spades, Diamonds, Clubs, Hearts } 4 | } -------------------------------------------------------------------------------- /src/06-Functional-refactor/answers/CardValue.cs: -------------------------------------------------------------------------------- 1 | namespace CsharpPoker 2 | { 3 | 4 | public enum CardValue { Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace } 5 | } 6 | -------------------------------------------------------------------------------- /src/06-Functional-refactor/answers/EvalExtensions.cs: -------------------------------------------------------------------------------- 1 | using CsharpPoker; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | public static class EvalExtensions 8 | { 9 | 10 | public static IEnumerable> ToPairs(this IEnumerable cards) 11 | { 12 | var dict = new ConcurrentDictionary(); 13 | foreach (var card in cards) 14 | { 15 | dict.AddOrUpdate(card.Value, 1, (cardValue, quantity) => ++quantity); 16 | } 17 | return dict; 18 | 19 | } 20 | 21 | public static IEnumerable SelectConsecutive(this IEnumerable source, Func selector) 22 | { 23 | int index = -1; 24 | foreach (TSource element in source.Take(source.Count() - 1)) 25 | { 26 | checked { index++; } 27 | yield return selector(element, source.ElementAt(index + 1)); 28 | } 29 | } 30 | 31 | public static HandRank Score(this Hand hand) => new FiveCardPokerScorer().GetScore(hand); 32 | 33 | } -------------------------------------------------------------------------------- /src/06-Functional-refactor/answers/FiveCardPokerScorer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace CsharpPoker 6 | { 7 | public class FiveCardPokerScorer 8 | { 9 | public HandRank GetScore(Hand hand) => Rankings().First(rule => rule.eval(hand.Cards)).rank; 10 | 11 | private List<(Func, bool> eval, HandRank rank)> Rankings() => 12 | new List<(Func, bool> eval, HandRank rank)> 13 | { 14 | (cards => HasRoyalFlush(cards), HandRank.RoyalFlush), 15 | (cards => HasStraightFlush(cards), HandRank.StraightFlush), 16 | (cards => HasFourOfAKind(cards), HandRank.FourOfAKind), 17 | (cards => HasFullHouse(cards), HandRank.FullHouse), 18 | (cards => HasFlush(cards), HandRank.Flush), 19 | (cards => HasStraight(cards), HandRank.Straight), 20 | (cards => HasThreeOfAKind(cards), HandRank.ThreeOfAKind), 21 | (cards => HasPair(cards), HandRank.Pair), 22 | (cards => true, HandRank.HighCard), 23 | }; 24 | 25 | // For C# 6 use a specialized class instead of System.ValueTuple 26 | // private List Rankings() => 27 | // new List 28 | // { 29 | // new Ranker(cards => HasRoyalFlush(cards), HandRank.RoyalFlush), 30 | // new Ranker(cards => HasStraightFlush(cards), HandRank.StraightFlush), 31 | // new Ranker(cards => HasFourOfAKind(cards), HandRank.FourOfAKind), 32 | // new Ranker(cards => HasFullHouse(cards), HandRank.FullHouse), 33 | // new Ranker(cards => HasFlush(cards), HandRank.Flush), 34 | // new Ranker(cards => HasStraight(cards), HandRank.Straight), 35 | // new Ranker(cards => HasThreeOfAKind(cards), HandRank.ThreeOfAKind), 36 | // new Ranker(cards => HasPair(cards), HandRank.Pair), 37 | // new Ranker(cards => true, HandRank.HighCard), 38 | // }; 39 | 40 | public Card HighCard(IEnumerable cards) => cards.Aggregate((result, nextCard) => result.Value > nextCard.Value ? result : nextCard); 41 | 42 | private bool HasFlush(IEnumerable cards) => cards.All(c => cards.First().Suit == c.Suit); 43 | 44 | private bool HasRoyalFlush(IEnumerable cards) => HasFlush(cards) && cards.All(c => c.Value > CardValue.Nine); 45 | 46 | private bool HasOfAKind(IEnumerable cards, int num) => cards.ToPairs().Any(c => c.Value == num); 47 | 48 | private bool HasPair(IEnumerable cards) => HasOfAKind(cards, 2); 49 | private bool HasThreeOfAKind(IEnumerable cards) => HasOfAKind(cards, 3); 50 | private bool HasFourOfAKind(IEnumerable cards) => HasOfAKind(cards, 4); 51 | 52 | private bool HasFullHouse(IEnumerable cards) => HasThreeOfAKind(cards) && HasPair(cards); 53 | 54 | private bool HasStraight(IEnumerable cards) => cards.OrderBy(card => card.Value).SelectConsecutive((n, next) => n.Value + 1 == next.Value).All(value => value); 55 | 56 | private bool HasStraightFlush(IEnumerable cards) => HasStraight(cards) && HasFlush(cards); 57 | } 58 | } -------------------------------------------------------------------------------- /src/06-Functional-refactor/answers/Hand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Concurrent; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using CsharpPoker; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class Hand 12 | { 13 | private readonly List cards = new List(); 14 | 15 | public IEnumerable Cards { get { return cards; } } 16 | 17 | public void Draw(Card card) => cards.Add(card); 18 | 19 | public Card HighCard() => cards.Aggregate((result, nextCard) => result.Value > nextCard.Value ? result : nextCard); 20 | 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /src/06-Functional-refactor/answers/Rank.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace CsharpPoker 6 | { 7 | public enum HandRank 8 | { 9 | HighCard, 10 | Pair, 11 | TwoPair, 12 | ThreeOfAKind, 13 | Straight, 14 | Flush, 15 | FullHouse, 16 | FourOfAKind, 17 | StraightFlush, 18 | RoyalFlush 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /src/06-Functional-refactor/answers/Ranker.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This class is only needed for C# 6.0 where System.ValueTuple is not available. 4 | 5 | Projects using .NET Standard 2.0 may omit this class. 6 | 7 | */ 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | 13 | namespace CsharpPoker 14 | { 15 | public class Ranker 16 | { 17 | public Ranker(Func, bool> eval, HandRank rank) 18 | { 19 | Eval = eval; 20 | Rank = rank; 21 | } 22 | 23 | public Func, bool> Eval { get; } 24 | 25 | public HandRank Rank { get; } 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /src/06-Functional-refactor/answers/Tests/CardTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace CsharpPoker 4 | { 5 | public class CardTests 6 | { 7 | 8 | [Fact] 9 | public void CanCreateCardWithValue() { 10 | 11 | var card = new Card(CardValue.Ace, CardSuit.Spades); 12 | 13 | Assert.Equal(CardSuit.Spades, card.Suit); 14 | Assert.Equal(CardValue.Ace, card.Value); 15 | } 16 | 17 | [Fact] 18 | public void CanCreateDescribeCard() 19 | { 20 | 21 | var card = new Card(CardValue.Ace, CardSuit.Spades); 22 | 23 | Assert.Equal("Ace of Spades", card.ToString()); 24 | 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/06-Functional-refactor/answers/Tests/HandTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using FluentAssertions; 8 | 9 | namespace CsharpPoker 10 | { 11 | public class HandTests 12 | { 13 | [Fact] 14 | public void CanCreateHand() 15 | { 16 | var hand = new Hand(); 17 | hand.Cards.Any().Should().BeFalse(); 18 | } 19 | 20 | [Fact] 21 | public void CanHandDrawCard() 22 | { 23 | var card = new Card(CardValue.Ace, CardSuit.Spades); 24 | var hand = new Hand(); 25 | 26 | hand.Draw(card); 27 | hand.Cards.First().Should().Be(card); 28 | } 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/06-Functional-refactor/answers/Tests/ScoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using FluentAssertions; 8 | 9 | namespace CsharpPoker.Tests 10 | { 11 | public class ScoreTests 12 | { 13 | [Fact] 14 | public void CanGetHighCard() 15 | { 16 | var hand = new Hand(); 17 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 18 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 19 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 20 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 21 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 22 | 23 | hand.HighCard().Value.Should().Be(CardValue.King); 24 | } 25 | 26 | [Fact] 27 | public void CanScoreHighCard() 28 | { 29 | var hand = new Hand(); 30 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 31 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 32 | hand.Draw(new Card(CardValue.Five, CardSuit.Hearts)); 33 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 34 | hand.Draw(new Card(CardValue.Two, CardSuit.Hearts)); 35 | 36 | hand.Score() 37 | .Should() 38 | .Be(HandRank.HighCard); 39 | } 40 | 41 | [Fact] 42 | public void CanScoreFlush() 43 | { 44 | var hand = new Hand(); 45 | hand.Draw(new Card(CardValue.Two, CardSuit.Spades)); 46 | hand.Draw(new Card(CardValue.Three, CardSuit.Spades)); 47 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 48 | hand.Draw(new Card(CardValue.Five, CardSuit.Spades)); 49 | hand.Draw(new Card(CardValue.Six, CardSuit.Spades)); 50 | 51 | hand.Score() 52 | .Should() 53 | .Be(HandRank.Flush); 54 | } 55 | [Fact] 56 | public void CanScoreRoyalFlush() 57 | { 58 | var hand = new Hand(); 59 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 60 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 61 | hand.Draw(new Card(CardValue.Queen, CardSuit.Spades)); 62 | hand.Draw(new Card(CardValue.King, CardSuit.Spades)); 63 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 64 | 65 | hand.Score() 66 | .Should() 67 | .Be(HandRank.RoyalFlush); 68 | } 69 | 70 | [Fact] 71 | public void CanScorePair() 72 | { 73 | var hand = new Hand(); 74 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 75 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 76 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 77 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 78 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 79 | 80 | hand.Score() 81 | .Should() 82 | .Be(HandRank.Pair); 83 | 84 | } 85 | 86 | [Fact] 87 | public void CanScoreThreeOfAKind() 88 | { 89 | var hand = new Hand(); 90 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 91 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 92 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 93 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 94 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 95 | 96 | hand.Score() 97 | .Should() 98 | .Be(HandRank.ThreeOfAKind); 99 | 100 | } 101 | 102 | [Fact] 103 | public void CanScoreFourOfAKind() 104 | { 105 | var hand = new Hand(); 106 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 107 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 108 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 109 | hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts)); 110 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 111 | 112 | hand.Score() 113 | .Should() 114 | .Be(HandRank.FourOfAKind); 115 | 116 | } 117 | [Fact] 118 | public void CanScoreFullHouse() 119 | { 120 | var hand = new Hand(); 121 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 122 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 123 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 124 | hand.Draw(new Card(CardValue.Jack, CardSuit.Hearts)); 125 | hand.Draw(new Card(CardValue.Ten, CardSuit.Spades)); 126 | 127 | hand.Score() 128 | .Should() 129 | .Be(HandRank.FullHouse); 130 | } 131 | 132 | [Fact] 133 | public void CanScoreStraight() 134 | { 135 | var hand = new Hand(); 136 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 137 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 138 | hand.Draw(new Card(CardValue.Queen, CardSuit.Spades)); 139 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 140 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 141 | 142 | hand.Score() 143 | .Should() 144 | .Be(HandRank.Straight); 145 | } 146 | 147 | [Fact] 148 | public void CanScoreStraightUnordered() 149 | { 150 | var hand = new Hand(); 151 | hand.Draw(new Card(CardValue.Ace, CardSuit.Spades)); 152 | hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs)); 153 | hand.Draw(new Card(CardValue.Jack, CardSuit.Spades)); 154 | hand.Draw(new Card(CardValue.Queen, CardSuit.Spades)); 155 | hand.Draw(new Card(CardValue.King, CardSuit.Hearts)); 156 | 157 | hand.Score() 158 | .Should() 159 | .Be(HandRank.Straight); 160 | } 161 | 162 | [Fact] 163 | public void CanScoreStraightFlush() 164 | { 165 | var hand = new Hand(); 166 | hand.Draw(new Card(CardValue.Five, CardSuit.Spades)); 167 | hand.Draw(new Card(CardValue.Six, CardSuit.Spades)); 168 | hand.Draw(new Card(CardValue.Seven, CardSuit.Spades)); 169 | hand.Draw(new Card(CardValue.Eight, CardSuit.Spades)); 170 | hand.Draw(new Card(CardValue.Nine, CardSuit.Spades)); 171 | 172 | hand.Score() 173 | .Should() 174 | .Be(HandRank.StraightFlush); 175 | } 176 | 177 | } 178 | } 179 | --------------------------------------------------------------------------------