-
Notifications
You must be signed in to change notification settings - Fork 18
/
index.html
752 lines (661 loc) · 34.4 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Going all in with Functional C#</title>
<meta charset="utf-8">
<meta name="description" content="A functional C# kata">
<meta name="author" content="Ed Charbeneau">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<div id="container">
<div id="header">
<a href="#" class="menu header-btn" id="toggle-toc"></a>
<h1>Going all in with Functional C#</h1>
<a href="https://github.com/EdCharbeneau/csharp-functional-workshop-instructions" class="github header-btn"></a>
</div>
<div id="content-container">
<div id="toc">
<div class="toc-heading">Table of Contents</div>
<div id="toc-padding"></div>
</div>
<div id="book">
<div class="chapter">
<h2 id="introduction">Introduction</h2>
<p>Welcome to All In With Functional C#.</p>
<h3 id="prerequisites">Prerequisites</h3>
<p>Visual Studio 2015 and C# 6.0 will do, however for the best experience use Visual Studio 2017 and C# 7.x</p>
<p>A basic knowledge of the C# language is recommended.</p>
<h3 id="what-you-re-building">What You're Building</h3>
<p>In this workshop you will learn about functional C# by building a poker scoring game using C# 7.x features. You'll learn what features in C# 7.x support functional programming. The pros/cons of functional vs. imperative programming will be explored to understand the strengths of each style.</p>
<p>Key topics and takeaways:</p>
<ul>
<li>Immutable Types</li>
<li>Basic LINQ concepts</li>
<li>Advanced LINQ concepts (yield)</li>
<li>Func Delegates</li>
<li>Expression Bodied Members</li>
<li>Extension methods and pipe-lining</li>
<li>Thread Safe Collections</li>
<li><strong>new</strong> System.ValueTuple</li>
</ul>
<h3 id="materials">Materials</h3>
<p>In addition to this guide, a full presentation file and completed code can be found under the following folders in the repository.</p>
<ul>
<li>Main repository: <a href="https://github.com/EdCharbeneau/csharp-functional-workshop-instructions">https://github.com/EdCharbeneau/csharp-functional-workshop-instructions</a></li>
<li>Presentations: <a href="https://github.com/EdCharbeneau/csharp-functional-workshop-instructions/tree/gh-pages/files/00-docs">\files\00-docs</a></li>
<li>Completed code (answers): <a href="https://github.com/EdCharbeneau/csharp-functional-workshop-instructions/tree/gh-pages/files">files\01-06</a></li>
</ul>
</div>
<hr>
<div class="chapter">
<h2 id="getting-up-and-running">Getting Up and Running</h2>
<p>In this chapter you're setting up the basics for unit testing.</p>
<h3 id="create-an-xunit-test-project-vs2017">Create an xUnit Test Project - VS2017</h3>
<p>Start by creating a new Class Library project. You'll use this project throughout the rest of this tutorial to build your application.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Create a new project<br></h4>
<p>Click <strong>File > New Project > xUnit Test Project (.NET Core)</strong></p>
<p>Name the project <strong>CsharpPoker</strong></p>
<p><img src="images/chapter1/new-xunit-project.jpg" alt=""></p>
<p>Delete UnitTest1.cs</p>
<p>Using the package manager console, run the following commands</p>
<pre><code>PM> Install-Package FluentAssertions
</code></pre><p>Alternatively, you can use the package manager GUI</p>
<p>The next steps are for Visual Studio 2015 users. You may advance to step 2.0.</p>
<div class="exercise-end"></div>
<h3 id="vs2015-only-create-a-new-class-library-project">VS2015 ONLY - Create a New Class Library Project</h3>
<p>Start by creating a new Class Library project. You'll use this project throughout the rest of this tutorial to build your application.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Create a new project<br></h4>
<p>Click <strong>File > New Project > Class Library</strong></p>
<p>Name the project <strong>CsharpPoker</strong></p>
<p>Delete Class1.cs</p>
<div class="exercise-end"></div>
<h3 id="vs2015-only-install-the-xunit-unit-testing-framework">VS2015 ONLY - Install the xUnit, unit testing framework</h3>
<p>With the new project created, it's time to start building unit tests. For this guide, we are using xUnit. xUnit is a commonly used unit testing framework in .NET.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Install xUnit
</h4>
<p>Open the package manager console, in quick launch, type: package manager console</p>
<p>Using the package manager console, run the following commands</p>
<pre><code>PM> Install-Package xunit
PM> Install-Package xunit.runner.visualstudio
PM> Install-Package FluentAssertions
</code></pre><p>Alternatively, you can use the package manager GUI</p>
<div class="exercise-end"></div>
</div>
<hr>
<div class="chapter">
<h2 id="creating-a-unit-test">Creating a unit test</h2>
<p>In this chapter you'll learn how to create simple unit tests with xUnit.</p>
<h3 id="create-a-card-test">Create a Card test</h3>
<p>Throughout this workshop you'll be using poker cards to learn new functional language features. In this exercise you'll create a new folder to hold unit tests and write your first unit test for a poker card. To keep things simple, in this workshop you'll use one project for all of your code. In a real-world app you would probably use a separate test project.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Create a Card test
</h4>
<p>Add a folder named Tests to your project.</p>
<p>In /Tests Add an empty class file named CardTests.cs</p>
<p>Make sure the CardTests class is defined as <strong>public</strong> so the test runner can see the test class.</p>
<pre><code>public class CardTests
</code></pre><p>In CardTests.cs add a new test method named CanCreateCard. Test methods in xUnit use the <code>[Fact]</code> attribute. You may need to import xUnit with a <code>using</code> statement, highlight Fact and press ctrl+. (control period), the press enter.</p>
<pre><code>[Fact]
public void CanCreateCard() { }
</code></pre><p>In the CanCreateCard method write a test that confirms that a new Card object can be created.</p>
<pre><code>var card = new Card();
Assert.NotNull(card);
</code></pre><p>There is no class defined yet, so you can consider this the first failing test. In the next exercise you'll create a Card object to satisfy this test.</p>
<div class="exercise-end"></div>
<h3 id="create-a-card-object">Create a Card object</h3>
<p>In this exercise you'll create a card object.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Create a Card object
</h4>
<p>In the root folder of your project, create a Card class named Card.cs.</p>
<p>Make the card class public.</p>
<pre><code>public class Card {}
</code></pre><p>This should be enough to satisfy the first unit test. Build the project (ctrl+shift+b), open Test Explorer and click <strong>Run All</strong>. You should receive a green check-mark next to the CanCreateCard test.</p>
<p><img src="images/chapter2/test-explorer-pass.jpg" alt=""></p>
<p>If the Test Explorer window is not visible, type Test Explorer in the quick launch box.</p>
<p><img src="images/chapter2/test-explorer.jpg" alt=""></p>
<div class="exercise-end"></div>
<h3 id="complete-the-card-object">Complete the Card object</h3>
<p>A card must be able to represent a Suit and Value to be useful. In the next exercise you'll add some properties to the card and tests to insure a card has a Suit and Value when it is created.</p>
<h4 class="exercise-start">
<b>Exercise</b>: description
</h4>
<p>Give the card properties to hold a CardValue and CardSuit</p>
<pre><code>public class Card
{
public CardValue Value { get; set; }
public CardSuit Suit { get; set; }
}
</code></pre><p>Create enums for CardValue and CardSuit</p>
<pre><code> // CardValue.cs
public enum CardValue
{
Two = 2,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
Jack,
Queen,
King,
Ace
}
// CardSuit.cs
public enum CardSuit
{
Spades,
Diamonds,
Clubs,
Hearts
}
</code></pre><p>Change the CanCreateCard test so that a Card has a value when it is created, rename the test to CanCreateCardWithValue. Test to make sure the properties are NotNull.</p>
<pre><code> [Fact]
public void CanCreateCardWithValue() {
var card = new Card();
Assert.NotNull(card.Suit);
Assert.NotNull(card.Value);
}
</code></pre><p>Note that the test will pass even when no value was assigned. This is because enums have a default value.</p>
<p>To insure that a value is intentionally set, add a constructor to the Card that requires a Suit and Value.</p>
<pre><code>public class Card
{
public Card(CardValue value, CardSuit suit)
{
Value = value;
Suit = suit;
}
public CardValue Value { get; set; }
public CardSuit Suit { get; set; }
}
</code></pre><p>Change the assertion so that it checks for a predetermined Suit and Value.</p>
<pre><code>var card = new Card(CardValue.Ace, CardSuit.Clubs);
Assert.Equal(CardSuit.Clubs, card.Suit);
Assert.Equal(CardValue.Ace, card.Value);
</code></pre><p>Now when a Card is created it must have its properties set to a value. In the next exercise you'll refactor the Card so that it is immutable, meaning that its properties cannot be changed once the object is created.</p>
<p>Re-run the test to verify that the test passes.</p>
<p>If you're using <strong>Visual Studio 2017 Enterprise edition</strong>. You can save time by enabling Live Unit Testing.</p>
<p><img src="images/chapter2/enable-live-ut.jpg" alt=""></p>
<div class="exercise-end"></div>
<h3 id="describing-a-card">Describing a Card</h3>
<p>One positive aspect of C# programming is that both functional and OOP styles of programming are not mutually exclusive. In this exercise you'll override the inherited ToString method of the Card object and use it to describe the Card's values.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Override the inherited ToString method
</h4>
<p>Create a new test CanDescribeCard, this test should test a Card with the values of <code>CardValue.Ace</code> and <code>CardSuit.Spades</code>. The ToString method should return <code>"Ace of Spades"</code>.</p>
<pre><code>[Fact]
public void CanDescribeCard()
{
var card = new Card(CardValue.Ace, CardSuit.Spades);
Assert.Equal("Ace of Spades", card.ToString());
}
</code></pre><p>Run the test to verify that it fails.</p>
<p>Next, update the Card object to make the test pass. Override the ToString method on the Card class and utilize the string interpolation syntax to write out the Card's description.</p>
<pre><code> public override string ToString()
{
return $"{Value} of {Suit}";
}
</code></pre><p>Re-run the test to verify that the test passes.</p>
<div class="exercise-end"></div>
<h3 id="describing-a-card-refactor">Describing a Card, refactor</h3>
<p>The ToString method is a simple, single line of code that returns a value. In this exercise you'll use an expression-bodied member to reduce the amount of code further into a concise expression.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Convert ToString to an expression-bodied member
</h4>
<p>Find the ToString method of the Card class. Remove the braces {} from the method, and replace the <code>return</code> statement with a lambda arrow =>. The arrow implies that the method will return a value, and the result is a much simpler syntax. </p>
<p>This will reduce the method to a simple one line statement. </p>
<pre><code> public override string ToString() => $"{Value} of {Suit}";
</code></pre><p>Re-run the test to verify that the test passes.</p>
<div class="exercise-end"></div>
<h3 id="immutable-card-object">Immutable Card object</h3>
<p>In functional programming immutable objects are used to reduce complexity and avoid unintended changes in state. An immutable object's state cannot be modified after it is created, lowering the risk of side-effects.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Modify the Card to make it immutable
</h4>
<p>In C# 6 or higher, backing fields and explicit readonly property declarations are not needed. By simply removing the <code>set</code> operator from a property will make it a read-only property.</p>
<p>In the Card class, remove the <code>set</code> declarations from each property.</p>
<pre><code>public class Card
{
public Card(CardValue value, CardSuit suit)
{
Value = value;
Suit = suit;
}
public CardValue Value { get; }
public CardSuit Suit { get; }
public override string ToString() => $"{Value} of {Suit}";
}
</code></pre><p>Now the properties can only be set during the object's initialization.</p>
<p>Re-run the test to verify that the test passes.</p>
<div class="exercise-end"></div>
<p>In this chapter you created a simple object and used some functional aspects of C#. By using expression bodied-members, the ToString method was reduced to a single expression. The Card class was made immutable by using the objects constructor and removing the <code>set</code> declarations from the object. In the Card object, both OOP and functional programming were used in a single class.</p>
<p>This chapter also outlined the basics of writing and running unit tests. Now that you're comfortable with running unit tests, you will not be instructed to run tests after this point, instead feel free to run them as needed throughout the remainder of the next chapters.</p>
</div>
<hr>
<div class="chapter">
<h2 id="a-poker-hand">A poker hand</h2>
<p>In the previous chapter you created a simple object and used some functional aspects of C#.</p>
<p>In this chapter you'll start with imperative and OOP style programming. Throughout the examples you'll be asked to refactor using functional programming. This will help identify where to find balance between the two styles. As the this guide progresses, instructions will become less detailed so that you can explore your on your own.</p>
<p>Now that you're comfortable with running unit tests, you will not be instructed to run tests after this point, instead feel free to run them as needed. <strong>For example, after every code change.</strong></p>
<h3 id="create-a-hand">Create a Hand</h3>
<p>The poker hand will be used throughout the workshop. The Hand will represent a player's hand of cards. For this workshop a five card hand will be scored using imperative and functional programming.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Create the HandTests
</h4>
<p>Create a test class named HandTests in the /Tests folder. Make sure HandTests is public.</p>
<p>Use the following tests to create a Hand class that has a Cards property and a Draw method.</p>
<pre><code> [Fact]
public void CanCreateHand()
{
var hand = new Hand();
Assert.False(hand.Cards.Any());
}
[Fact]
public void CanHandDrawCard()
{
var card = new Card(CardValue.Ace, CardSuit.Spades);
var hand = new Hand();
hand.Draw(card);
Assert.Equal(hand.Cards.First(), card);
}
</code></pre><p>Create a Hand class that satisfies the tests.</p>
<pre><code>public class Hand
{
public Hand()
{
Cards = new List<Card>();
}
public List<Card> Cards { get;}
public void Draw(Card card)
{
Cards.Add(card);
}
}
</code></pre><p>The Hand class will hold a players hand of cards. In the next exercises you will be scoring the Hand of cards from the Cards property in the Hand class.</p>
<div class="exercise-end"></div>
<h3 id="getting-the-high-card">Getting the high card</h3>
<p>In a game where all players hands are a equal in rank, the winner is decided by comparing the highest card in their hands. Add a HighCard method to the Hand that returns the highest CardValue in the hand. </p>
<h4 class="exercise-start">
<b>Exercise</b>: Score the high card
</h4>
<p>Below is a test method you can use to validate your HighCard method.</p>
<pre><code> [Fact]
public void CanGetHighCard()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Seven, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs));
hand.Draw(new Card(CardValue.Five, CardSuit.Hearts));
hand.Draw(new Card(CardValue.King, CardSuit.Hearts));
hand.Draw(new Card(CardValue.Two, CardSuit.Hearts));
Assert.Equal(CardValue.King, hand.HighCard().Value);
}
</code></pre><p>When this test passes, move on to the next exercise.</p>
<div class="exercise-end"></div>
<h3 id="hand-rankings">Hand rankings</h3>
<p>In the previous exercise, you determined the high card. Now add a HandRank GetHandRank method that will return the Hand's HandRank.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Score the high card
</h4>
<p>Add the HandRank enum to your project.</p>
<pre><code>public enum HandRank
{
HighCard,
Pair,
TwoPair,
ThreeOfAKind,
Straight,
Flush,
FullHouse,
FourOfAKind,
StraightFlush,
RoyalFlush
}
</code></pre><p>Use the following tests to create a GetHandRank method on the Hand object that will return the correct HandRank for the test. Only score the Ranks below, you'll be refactoring as the workshop progresses and scoring additional HandRanks as you learn about OOP and functional programming.</p>
<pre><code> [Fact]
public void CanScoreHighCard()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Seven, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs));
hand.Draw(new Card(CardValue.Five, CardSuit.Hearts));
hand.Draw(new Card(CardValue.King, CardSuit.Hearts));
hand.Draw(new Card(CardValue.Two, CardSuit.Hearts));
Assert.Equal(HandRank.HighCard, hand.GetHandRank());
}
[Fact]
public void CanScoreFlush()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Two, CardSuit.Spades));
hand.Draw(new Card(CardValue.Three, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ace, CardSuit.Spades));
hand.Draw(new Card(CardValue.Five, CardSuit.Spades));
hand.Draw(new Card(CardValue.Six, CardSuit.Spades));
Assert.Equal(HandRank.Flush, hand.GetHandRank());
}
[Fact]
public void CanScoreRoyalFlush()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Ten, CardSuit.Spades));
hand.Draw(new Card(CardValue.Jack, CardSuit.Spades));
hand.Draw(new Card(CardValue.Queen, CardSuit.Spades));
hand.Draw(new Card(CardValue.King, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ace, CardSuit.Spades));
Assert.Equal(HandRank.RoyalFlush, hand.GetHandRank());
}
</code></pre><p>When this test passes, move on to the next exercise.</p>
<div class="exercise-end"></div>
<h3 id="hand-rankings-answers">Hand rankings, answers</h3>
<p>Now that the HighCard, Flush, and RoyalFlush hand ranks have been scored, review the answers to see how the code can be written using functional programming.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Answers for HighCard, Flush, RoyalFlush
</h4>
<p>Open open the folder /chapter3/answers/first-pass</p>
<p>Open Hand.cs and review the comments</p>
<div class="exercise-end"></div>
<h3 id="hand-rankings-refactored">Hand rankings, refactored</h3>
<div class="exercise-end"></div>
<p>Now that the HighCard, Flush, and RoyalFlush hand ranks have been scored, review the answers to see how the code can be refactored using functional programming.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Answers for HighCard, Flush, RoyalFlush
</h4>
<p>Open open the folder /chapter3/answers/refactored</p>
<p>Open Hand.cs and review the comments</p>
<p>Refactor your own code and make sure all of your tests pass.</p>
<div class="exercise-end"></div>
<h3 id="hand-tests-refactored">Hand Tests refactored</h3>
<p>Now it's time to refactor the tests using method chains. Pipelines are often found in functional programming languages. Pipelines allow functions to be chained or composed to produce easily maintainable and readable code.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Using Fluent Assertions
</h4>
<p>Open /Tests/HandTests.cs</p>
<p>Add a reference to FluentAssertions.</p>
<pre><code>using FluentAssertions;
</code></pre><p>Modify the Assert statement to use the FluentAssertions chain instead. To do this, start with the value that will be tested and continue with the method Should().Be(expectedValue)</p>
<pre><code>Assert.Equal(CardValue.King, hand.HighCard().Value);
</code></pre><p>becomes</p>
<pre><code>hand.HighCard().Value.Should().Be(CardValue.King);
</code></pre><p>Below is a example of the completed CanGetHighCard test</p>
<pre><code>[Fact]
public void CanGetHighCard()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Seven, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs));
hand.Draw(new Card(CardValue.Five, CardSuit.Hearts));
hand.Draw(new Card(CardValue.King, CardSuit.Hearts));
hand.Draw(new Card(CardValue.Two, CardSuit.Hearts));
hand.HighCard().Value.Should().Be(CardValue.King);
}
</code></pre><p>Refactor all tests to use Fluent Assertions.</p>
<div class="exercise-end"></div>
</div>
<hr>
<div class="chapter">
<h2 id="finding-pairs">Finding Pairs</h2>
<p>In the previous chapter you solved a few hand ranks using LINQ and refactored tests with method chains with the help of FluentAssertions.</p>
<p>In this chapter you'll continue with imperative and OOP style programming. Throughout the examples you'll be asked to refactor using functional programming. Later in this chapter, you'll learn about working with concurrency in C#.</p>
<p>Feel free to run tests as needed. <strong>For example, after every code change.</strong></p>
<h3 id="scoring-pairs">Scoring Pairs</h3>
<p>Continue to update the poker hand used throughout the workshop.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Score pairs (of a kind) hand ranks
</h4>
<pre><code> [Fact]
public void CanScorePair()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs));
hand.Draw(new Card(CardValue.Jack, CardSuit.Spades));
hand.Draw(new Card(CardValue.Nine, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts));
hand.Draw(new Card(CardValue.Ace, CardSuit.Spades));
hand.GetHandRank().Should().Be(HandRank.Pair);
}
[Fact]
public void CanScoreThreeOfAKind()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs));
hand.Draw(new Card(CardValue.Jack, CardSuit.Spades));
hand.Draw(new Card(CardValue.Nine, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts));
hand.Draw(new Card(CardValue.Ten, CardSuit.Spades));
hand.GetHandRank().Should().Be(HandRank.ThreeOfAKind);
}
[Fact]
public void CanScoreFourOfAKind()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs));
hand.Draw(new Card(CardValue.Jack, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ten, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts));
hand.Draw(new Card(CardValue.Ten, CardSuit.Spades));
hand.GetHandRank().Should().Be(HandRank.FourOfAKind);
}
[Fact]
public void CanScoreFullHouse()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs));
hand.Draw(new Card(CardValue.Jack, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ten, CardSuit.Spades));
hand.Draw(new Card(CardValue.Jack, CardSuit.Hearts));
hand.Draw(new Card(CardValue.Ten, CardSuit.Spades));
hand.GetHandRank().Should().Be(HandRank.FullHouse);
}
</code></pre><p>When this tests pass, move on to the next exercise.</p>
<div class="exercise-end"></div>
<h3 id="hand-rankings-answers">Hand rankings, answers</h3>
<p>Now that the Pairs (of a kind) hand ranks have been scored, review the answers to see how the code can be written using functional programming.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Answers for Pairs (of a kind), FullHouse
</h4>
<p>Open open the folder files/04-Pairs/answers/first-pass</p>
<p>Open Hand.cs and review the comments</p>
<div class="exercise-end"></div>
<h3 id="hand-rankings-refactored">Hand rankings, refactored</h3>
<p>Now that the Pairs (of a kind) hand ranks have been scored, review the answers to see how the code can be refactored using functional programming.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Answers for Pairs (of a kind), FullHouse
</h4>
<p>Open open the folder files/04-Pairs/answers/refactored</p>
<p>Open Hand.cs and review the comments
Open EvalExtensions.cs and review the comments</p>
<div class="exercise-end"></div>
</div>
<hr>
<div class="chapter">
<h2 id="finding-sequences">Finding Sequences</h2>
<p>In the previous chapter you solved more hand ranks using higher order functions and refactored with an extension method to create a method chain.</p>
<p>In this chapter you'll continue with imperative and OOP style programming. Throughout the examples you'll be asked to refactor using functional programming. Later in this chapter, you'll learn about working with the Yield operator in C#.</p>
<p>Feel free to run tests as needed. <strong>For example, after every code change.</strong></p>
<h3 id="scoring-a-straight">Scoring a Straight</h3>
<p>Continue to update the poker hand used throughout the workshop.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Score a Straight
</h4>
<pre><code> [Fact]
public void CanScoreStraight()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs));
hand.Draw(new Card(CardValue.Jack, CardSuit.Spades));
hand.Draw(new Card(CardValue.Queen, CardSuit.Spades));
hand.Draw(new Card(CardValue.King, CardSuit.Hearts));
hand.Draw(new Card(CardValue.Ace, CardSuit.Spades));
hand.GetHandRank().Should().Be(HandRank.Straight);
}
[Fact]
public void CanScoreStraightUnordered()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Ace, CardSuit.Spades));
hand.Draw(new Card(CardValue.Queen, CardSuit.Clubs));
hand.Draw(new Card(CardValue.Jack, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ten, CardSuit.Spades));
hand.Draw(new Card(CardValue.King, CardSuit.Hearts));
hand.GetHandRank().Should().Be(HandRank.Straight);
}
</code></pre><p>When this tests pass, move on to the next exercise.</p>
<div class="exercise-end"></div>
<h3 id="hand-rankings-answers">Hand rankings, answers</h3>
<p>Now that the Straight hand rank has been scored, review the answers to see how the code can be written using functional programming.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Answer for Straight
</h4>
<p>Open open the folder files/05-Sequences/answers/first-pass</p>
<p>Open Hand.cs and review the comments</p>
<div class="exercise-end"></div>
<h3 id="hand-rankings-refactored">Hand rankings, refactored</h3>
<p>Now that the Straight hand rank has been scored, review the answers to see how the code can be refactored using functional programming.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Answers for Straight
</h4>
<p>Open open the folder files/05-Sequences/answers/refactored</p>
<p>Open Hand.cs and review the comments
Open EvalExtensions.cs and review the comments</p>
<div class="exercise-end"></div>
</div>
<hr>
<div class="chapter">
<h2 id="functional-refactor">Functional Refactor</h2>
<p>In the previous chapter you solved more hand ranks using LINQ and refactored with the yield keyword to create a custom LINQ-like extension method.</p>
<p>In this chapter you'll refactor to use a more functional approach to scoring a hand.</p>
<p>Feel free to run tests as needed. <strong>For example, after every code change.</strong></p>
<h3 id="data-and-behavior">Data and Behavior</h3>
<p>Up until now we have combined our Hand object with the behavior of scoring. Ideally these concepts should not be mixed. Because there is no state to be concerned about, the scoring functions can be moved with relative ease.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Refactor with Pure Functions
</h4>
<p>Create a new public static class named FiveCardPokerScorer</p>
<p>Open Hand.cs and move the scoring functionality to FiveCardPokerScorer.cs. Because the functionality will be external to the data, the funcitons will need to be modified to accept the data as a parameter. In addtion, this will bring the application closer in-line with functional programming because the new functions are considered "<a href="https://en.wikipedia.org/wiki/Pure_function">pure functions</a>".</p>
<p>Example:</p>
<pre><code>public static class FiveCardPokerScorer
{
public static Card HighCard(IEnumerable<Card> cards) => cards.Aggregate((highCard, nextCard) => nextCard.Value > highCard.Value ? nextCard : highCard);
private static bool HasFlush(IEnumerable<Card> cards) => cards.All(c => cards.First().Suit == c.Suit);
}
</code></pre><p>Create a new test class in /Tests named FiveCardPokerScorerTests</p>
<p>Move the corresponding tests from HandTests to FiveCardPokerScorerTests.</p>
<pre><code>[Fact]
public void CanGetHighCard()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Seven, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ten, CardSuit.Clubs));
hand.Draw(new Card(CardValue.Five, CardSuit.Hearts));
hand.Draw(new Card(CardValue.King, CardSuit.Hearts));
hand.Draw(new Card(CardValue.Two, CardSuit.Hearts));
FiveCardPokerScorer.HighCard(hand.Cards).Value.Should().Be(CardValue.King);
}
</code></pre><h3 id="functions-as-data-vs2017-c-7-1-">Functions as Data - VS2017 C# 7.1+</h3>
<div class="exercise-end"></div>
<p>If you're using VS2015 C# 6.0 skip this exercise.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Refactor GetHandRank with Tuples
</h4>
<p>Create a new public class named Ranker</p>
<p>Open Hand.cs and create a new private method named Rankings that generates a collection of Tuples. Use the following code as a starting point, fill in the remaining items</p>
<pre><code>private List<(Func<IEnumerable<Card>, bool> eval, HandRank rank)> Rankings() =>
new List<(Func<IEnumerable<Card>, bool> eval, HandRank rank)>
{
// more ranks here
};
</code></pre><p>Find the GetHandRank method and remove the expression after the =></p>
<p>The result should be:</p>
<pre><code>public static HandRank GetHandRank() =>
</code></pre><p>After the => write a new expression that uses the Rankings that evaluates the hand rank.</p>
<p>Hint: think LINQ</p>
<div class="exercise-end"></div>
<h3 id="functions-as-data-vs2015-c-6">Functions as Data VS2015 C# 6</h3>
<p>If you're using VS2017 C# 7.x skip this exercise.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Refactor GetHandRank VS2015 C# 6.0 (aka No Tuples)
</h4>
<p>Create a new public class named Ranker</p>
<p>The Ranker an immutable class that will hold a delegate responsible for evaluating cards and a corresponding hand rank.</p>
<pre><code>public class Ranker
{
public Ranker(Func<IEnumerable<Card>, bool> eval, HandRank rank)
{
Eval = eval;
Rank = rank;
}
public Func<IEnumerable<Card>, bool> Eval { get; }
public HandRank Rank { get; }
}
</code></pre><p>Open Hand.cs and create a new private method named Rankings that generates a collection of Ranker.</p>
<p>Use the following code as a starting point, fill in the remaining Ranker items</p>
<pre><code>private List<Ranker> Rankings() => new List<Ranker>
{
new Ranker(cards => HasRoyalFlush(), HandRank.RoyalFlush),
// more ranks here
};
</code></pre><p>Find the GetHandRank method and remove the expression after the =></p>
<p>The result should be:</p>
<pre><code>public static HandRank GetHandRank() =>
</code></pre><p>After the => write a new expression that uses the List<Ranker> from Rankings() that evaluates the hand rank.</p>
<p>Hint: think LINQ</p>
<div class="exercise-end"></div>
<h3 id="flexibility">Flexibility</h3>
<p>The newest version of GetHandRank can be more flexible than the previous versions. Because it is a collection, it can easily be added to and operated on using LINQ.</p>
<h4 class="exercise-start">
<b>Exercise</b>: Adding new hand ranks
</h4>
<p>Adding new hand ranks is easy. Simply add a new Ranker to the Rankings collection and the GetHandRank method will do the rest.</p>
<p>Make the GetHandRank method even more robust by allowing Ranks to be added in any order.</p>
<p>Find the GetHandRank method and order the Rankings by descending order by their Strength</p>
<pre><code>// With Tuples
.OrderByDescending(rule => rule.rank)
// Without Tuples
.OrderByDescending(rule => rule.Rank)
</code></pre><p>Next, solve the following unit test</p>
<pre><code> [Fact]
public void CanScoreTwoPair()
{
var hand = new Hand();
hand.Draw(new Card(CardValue.Ace, CardSuit.Clubs));
hand.Draw(new Card(CardValue.Ten, CardSuit.Spades));
hand.Draw(new Card(CardValue.Nine, CardSuit.Spades));
hand.Draw(new Card(CardValue.Ten, CardSuit.Hearts));
hand.Draw(new Card(CardValue.Ace, CardSuit.Spades));
hand.GetHandRank().Should().Be(HandRank.TwoPair);
}
</code></pre><div class="exercise-end"></div>
</div>
<hr>
<div class="chapter">
<h2 id="takeaways">Takeaways</h2>
<ol>
<li>Are you already using aspects of functional programming in your daily activities?</li>
<li>Will you use more functional programming in your code?</li>
<li>Is OOP obsolete? Or is there a balance that can be found between imperative and functional programing?</li>
<li>Is functional code like LINQ easier, or harder to read, maintain, and manage?</li>
</ol>
<h3 id="next-steps">Next Steps</h3>
<h4 class="exercise-start">
<b>Exercise</b>: Continue on your own
</h4>
<p>Continue building the project in your own direction. Use what you have learned to add multiple hands and players to a game and score a poker game.</p>
<p>Add support for other poker games types, with new rules or more cards. See how loosely coupled you can make the rules/ranks from the cards and hands.</p>
<div class="exercise-end"></div>
</div>
</div>
</div>
</div>
<script src="scripts/built.js"></script>
</body>
</html>