I found I'm not a huge fan of the video. I don't tend to put a lot of time and effort into each post; it's an opportunity for me to share research and experimentation with the world; as well as give myself a searchable brain. :)
The video took WAY too much time. I figure if I'm going to be trying that again; I'll put it off and it'll have a negative impact to actually getting blogs done.
I'm doing the blogging (for ~year now) for me. I want to be doing it; and I need to be careful to not do things that will interfere with getting it to happen.
I switched to a weekly schedule, I'm not doing videos... It's a work in progress; I like what's happening - but ... No more video's for now.
Which is unfortunate for those interested in my PokerScoreing bit - because this is part two - and not a video. :)
Today I'm implementing the scoring of the different poker hands. With the variant that Suits have weight. Hopefully this doesn't make it more complicated for me. :)
My first test is
[TestMethod]
public void ShouldReturnAceOfSpadesHigherThanOtherNonHand()
{
//Arrange
IHand aceOfSpadeHigh = AceOfSpaceHigh;
IHand aceOfClubHigh = AceOfClubsHigh;
//Act
bool winning = AceOfSpaceHigh.Beats(AceOfClubsHigh);
//Assert
winning.Should().BeTrue();
}
I want to make sure that an Ace of Spades high beats an Ace Of Clubs high.
Which... No. That's not what I want to do. How do I know it's an ace high? That's my first task. Creating the Scoring of A hand.
This leads to a simply check of
[TestMethod]
public void ShouldReturnAceOfSpadesHigh()
{
//Arrange
//Act
IScore score = AceOfSpaceHigh.HandScore();
//Assert
score.Should().Be(Score.AceSpadeHigh);
}
I'll do a similar for each of the individual cards.
It's a long list of things; but it's what a score could be. Though... Could a hand every be a TWO high? ... I think the lowest single card high you could have would be a 6. To be lower; you'd have 5 cards; 1-4 +1... what's the remaining one? ... Anything but a 6 would give you a run or two of a kind.
Because it's gonna be kinda boring; I'm gonna add all the Score.{RANK}{SUIT}High
statics and then the tests. Lot of duplication and copy and paste; but that's OK for tests. Each test will break for one, and only one, reason.
But I'll only go down to 6 for the 'high' scores.
Except Ace, or 1, is high high. So that makes the lowest single high card a 7.
OK. All the tests inplace to ensure that the high card is returned.
In addition I found that I needed to make sure that each rank was processed so that an Ace of Clubs was scored higher than a Two of Spades.
Tests were put in place for that as well.
At this point I have a giant method;
private IScore ScoreSingleCard()
{
if (_cards.Contains(Deck.Spades.Ace)) return Score.AceSpadeHigh;
if (_cards.Contains(Deck.Hearts.Ace)) return Score.AceHeartHigh;
if (_cards.Contains(Deck.Diamonds.Ace)) return Score.AceDiamondHigh;
if (_cards.Contains(Deck.Clubs.Ace)) return Score.AceClubHigh;
if (_cards.Contains(Deck.Spades.King)) return Score.KingSpadeHigh;
if (_cards.Contains(Deck.Hearts.King)) return Score.KingHeartHigh;
if (_cards.Contains(Deck.Diamonds.King)) return Score.KingDiamondHigh;
if (_cards.Contains(Deck.Clubs.King)) return Score.KingClubHigh;
if (_cards.Contains(Deck.Spades.Queen)) return Score.QueenSpadeHigh;
if (_cards.Contains(Deck.Hearts.Queen)) return Score.QueenHeartHigh;
if (_cards.Contains(Deck.Diamonds.Queen)) return Score.QueenDiamondHigh;
if (_cards.Contains(Deck.Clubs.Queen)) return Score.QueenClubHigh;
if (_cards.Contains(Deck.Spades.Jack)) return Score.JackSpadeHigh;
if (_cards.Contains(Deck.Hearts.Jack)) return Score.JackHeartHigh;
if (_cards.Contains(Deck.Diamonds.Jack)) return Score.JackDiamondHigh;
if (_cards.Contains(Deck.Clubs.Jack)) return Score.JackClubHigh;
if (_cards.Contains(Deck.Spades.Ten)) return Score.TenSpadeHigh;
if (_cards.Contains(Deck.Hearts.Ten)) return Score.TenHeartHigh;
if (_cards.Contains(Deck.Diamonds.Ten)) return Score.TenDiamondHigh;
if (_cards.Contains(Deck.Clubs.Ten)) return Score.TenClubHigh;
if (_cards.Contains(Deck.Spades.Nine)) return Score.NineSpadeHigh;
if (_cards.Contains(Deck.Hearts.Nine)) return Score.NineHeartHigh;
if (_cards.Contains(Deck.Diamonds.Nine)) return Score.NineDiamondHigh;
if (_cards.Contains(Deck.Clubs.Nine)) return Score.NineClubHigh;
if (_cards.Contains(Deck.Spades.Eight)) return Score.EightSpadeHigh;
if (_cards.Contains(Deck.Hearts.Eight)) return Score.EightHeartHigh;
if (_cards.Contains(Deck.Diamonds.Eight)) return Score.EightDiamondHigh;
if (_cards.Contains(Deck.Clubs.Eight)) return Score.EightClubHigh;
if (_cards.Contains(Deck.Spades.Seven)) return Score.SevenSpadeHigh;
if (_cards.Contains(Deck.Hearts.Seven)) return Score.SevenHeartHigh;
if (_cards.Contains(Deck.Diamonds.Seven)) return Score.SevenDiamondHigh;
if (_cards.Contains(Deck.Clubs.Seven)) return Score.SevenClubHigh;
throw new NotImplementedException("Working on it");
}
This will need to be broken down into a chain. I'll stick with a chain as it has temporal coupling. The order matters.
But do I want that right now...
I've been pondering this and a couple other points; and it struck me that I don't need to quite chain this. That's A LOT of almost identical components.
What this can do; is an ordered pair; checking one and then returning the result.
This has created the SingleCardScorer
class
public class SingleCardHigh : IScorer
{
private readonly List<KeyValuePair<ICard, IScore>> _singleCardScores;
public SingleCardHigh() : this(new List<KeyValuePair<ICard, IScore>>
{
new KeyValuePair<ICard, IScore>(Deck.Spades.Ace, Score.AceSpadeHigh),
new KeyValuePair<ICard, IScore>(Deck.Hearts.Ace, Score.AceHeartHigh),
new KeyValuePair<ICard, IScore>(Deck.Diamonds.Ace, Score.AceDiamondHigh),
new KeyValuePair<ICard, IScore>(Deck.Clubs.Ace, Score.AceClubHigh),
new KeyValuePair<ICard, IScore>(Deck.Spades.King, Score.KingSpadeHigh),
new KeyValuePair<ICard, IScore>(Deck.Hearts.King, Score.KingHeartHigh),
new KeyValuePair<ICard, IScore>(Deck.Diamonds.King, Score.KingDiamondHigh),
new KeyValuePair<ICard, IScore>(Deck.Clubs.King, Score.KingClubHigh),
new KeyValuePair<ICard, IScore>(Deck.Spades.Queen, Score.QueenSpadeHigh),
new KeyValuePair<ICard, IScore>(Deck.Hearts.Queen, Score.QueenHeartHigh),
new KeyValuePair<ICard, IScore>(Deck.Diamonds.Queen, Score.QueenDiamondHigh),
new KeyValuePair<ICard, IScore>(Deck.Clubs.Queen, Score.QueenClubHigh),
new KeyValuePair<ICard, IScore>(Deck.Spades.Jack, Score.JackSpadeHigh),
new KeyValuePair<ICard, IScore>(Deck.Hearts.Jack, Score.JackHeartHigh),
new KeyValuePair<ICard, IScore>(Deck.Diamonds.Jack, Score.JackDiamondHigh),
new KeyValuePair<ICard, IScore>(Deck.Clubs.Jack, Score.JackClubHigh),
new KeyValuePair<ICard, IScore>(Deck.Spades.Ten, Score.TenSpadeHigh),
new KeyValuePair<ICard, IScore>(Deck.Hearts.Ten, Score.TenHeartHigh),
new KeyValuePair<ICard, IScore>(Deck.Diamonds.Ten, Score.TenDiamondHigh),
new KeyValuePair<ICard, IScore>(Deck.Clubs.Ten, Score.TenClubHigh),
new KeyValuePair<ICard, IScore>(Deck.Spades.Nine, Score.NineSpadeHigh),
new KeyValuePair<ICard, IScore>(Deck.Hearts.Nine, Score.NineHeartHigh),
new KeyValuePair<ICard, IScore>(Deck.Diamonds.Nine, Score.NineDiamondHigh),
new KeyValuePair<ICard, IScore>(Deck.Clubs.Nine, Score.NineClubHigh),
new KeyValuePair<ICard, IScore>(Deck.Spades.Eight, Score.EightSpadeHigh),
new KeyValuePair<ICard, IScore>(Deck.Hearts.Eight, Score.EightHeartHigh),
new KeyValuePair<ICard, IScore>(Deck.Diamonds.Eight, Score.EightDiamondHigh),
new KeyValuePair<ICard, IScore>(Deck.Clubs.Eight, Score.EightClubHigh),
new KeyValuePair<ICard, IScore>(Deck.Spades.Seven, Score.SevenSpadeHigh),
new KeyValuePair<ICard, IScore>(Deck.Hearts.Seven, Score.SevenHeartHigh),
new KeyValuePair<ICard, IScore>(Deck.Diamonds.Seven, Score.SevenDiamondHigh),
new KeyValuePair<ICard, IScore>(Deck.Clubs.Seven, Score.SevenClubHigh),
})
{ }
public SingleCardHigh(List<KeyValuePair<ICard, IScore>> list) => _singleCardScores = list;
public IScore HandScore(ICard[] cards)
{
foreach (KeyValuePair<ICard, IScore> singleCardScore in _singleCardScores)
{
if (cards.Contains(singleCardScore.Key)) return singleCardScore.Value;
}
throw new NotImplementedException("Ya broke it");
}
}
Do I LIKE it... ehh; not as much as I could. I think it should be broken into a chain. But not sure the form yet. I don't think a normal chain. Then I'm passing the internals around. The cards of the hand.
But if I pass the hand down the chain; with a "Do you have X?" method. While I dislike it... I think it's better.
I like what I came out with. It's kinda big and dumb as a chain. Long.
I'm not a huge fan of this long of a chain. It is the correct decision process... just looks wrong.
I've moved onto looking at how I'd find Pairs. I'm not sure I like how I'm identifying the "Score". There's going to be WAY too many objects for each of these.
- PairTwoSpadeHigh
- PairTwoHeartHigh
- PairTwoDiamondHigh
Repeat for every additional rank 13*3 = 39. Plus the 34 for singles.
- ThreeOfTwoSpadeHigh
- ThreeOfTwoSpadeDiamond
Repeat for every additional rank 13*2 = 26. This puts up to 99 different score objects. gag It's getting very bloated.
I'm feeling that I should have a score object that takes a Score (Single, Pair, ThreeOf), plus a Card. This would be the "high card".
These two combined would give us the result. It's simpler. Then I just need to ask the Hand for the HighCard; or to build a score object given a score slug.
I like this a lot better.
OK; plan for next time - Nuke everything we did today Code. Then do it better!