pkcore 0.0.39

Prototype core poker library.
Documentation
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
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
# Outline of work

This is an outline of the work as it is done.

* Create pkcore lib
    * Set #![warn(clippy::pedantic)]
* EPIC: Display HandRank
    * Rank
        * lib:PKError
        * create enum
        * ::from(char)
            * Testing: TELL THE HEROES STORY
                * Tests using [rstest]https://crates.io/crates/rstest BRUTE FORCE
                * Readability of tests names (short, matching, clear)
                * Hub and spoke... take your time on the core.
                * Negative boundary conditions
        * ::from_str()
            * test neg scenarios #[allow(non_snake_case)]
    * Suit
        * create enum
        * ::from(char)
            * Tests using [rstest]https://crates.io/crates/rstest
        * ::from_str()
            * test neg scenarios #[allow(non_snake_case)]
        * Card
            * CKC Card Number consts
                * Intro to CKC Numbers
            * ::as_u32()
            * ::from<u32> (filter)
                * #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
                * Talk about brute force testing philosophy.
                * Add Rank.number(), .bits(), .prime(), and .shift8().
                    * strum::EnumIter && tests
            * card_consts
            * ::new
            * .is_blank()
            * .from_str()
                * Boundary conditions tests.
                * THE WONDERFUL ? operator
            * Detour on Testing as the Hero's Journey
                * tell the story
                * scannable
            * Move card numbers and make private
            * .to_string() which means doing it for Rank and Suit.
        * Rank
            * .to_string() thus .to_char()
        * Suit
            * .to_string() thus .to_char()
        * Card
            * .to_string()
        * Cards
            * [indexmap::IndexSet]https://github.com/bluss/indexmap
            * indexmap::set::Iter
            * .len() .is_empty()
            * .insert and .iter()
            * .to_string()
            * .sort() (adding Ord)
        * Card
            * .bit_string() -- Talk about expressive logging.
        * REFACTORING I don't expect you to copy out every part of the test code, although you are more than happy to. Think of this as a cooking show where I, your humble host, does most of the boring parts off camera so that you don't get bored. Voila! Losts of tests refactored!
            * CardNumber enum
            * into()
            * rstests: Consolidating  tests
                * The problem is that they aren't really testing for value Just number not number to actual card
        * Five INTRODUCING CACTUS KEV
        * Cards
            * implement FromStr so can be used by arrays like Five.
            * need get_index() to do Five.from_str()
        * Five
            * impl TryFrom<Cards>
            * now implement FromStr leveraging Cards.from_str()
            * .to_arr()
            * Get Five eval to return HandRank Number
                * .rank()
                    * .or_bits()
                    * .or_rank_bits()
                    * .and_bits()
                    * .is_flush()
                    * .rank()
                        * flushes
                        * unique
                        * the rest
                            * .multiply_primes()
                                * Card.get_prime() (Add to get_rank tests)
                            * REFACTORING: Section for pub fn then private fn
                            * not_unique()
                                * find_in_products()
                                    * refactoring from ckc (compare)
            * POSSIBLE DEFECT: Cards in different orders aren't equal
        * Cards
            * REFACTORING: Clippy found call to `str::trim` before `str::split_whitespace` cards.rs:72:20
        * Five
            * rank() basic tests
        * INTRODUCING HandRank struct
            * type aliases (HandRankValue, CactusKev)
                * CLIPPY: HandRankValue clippy::module_name_repetitions
            * hand_rank module (talk about separating files as old habit)
            * HandRankName
                * about strum::EnumIter;
                * refactoring HandRank.determine_name() to HandRankName::from (small test range... ranges of testing)
            * HandRankClass
                * refactoring HandRank.determine_class() to HandRankClass::from
                    * CLIPPY #[warn(clippy::too_many_lines)]"
                * AUDIBLE (waiting for HandRank MEGA TEST to capture all) (AUDIBLE = change from standard flow)
            * REFACTORING: impl Default to derive
            * HandRank::from(HandRankValue)
                * Favor traits over functions
                * PROCESS NOTE::: Step by step
                    * why I do small commits
                    * git muscle memory get into the habit of knowing the commands
                * REFACTORING name and class no longer public (remove need for ckc.is_a_valid_hand_rank())
                    * HARDENING (over thinking? Maybe)
                * TRAIT: SOK - I find boring names hard to remember
                * arrays::HandRanker
                    * remove Five::rank() replace with HandRanker
                    * REFACTOR: remove validated methods from Trait
            * Hand HandRanker BIG KAHUNA TEST
                * using //region
                * REFACTORING HandRankName  HandRankClass to Name and Class
        * DETOUR: Generating lookup tables
            * Is Straight Flush?
                * Is Straight?
                    * Let's add tests for all types of straights
                        * WHEEL???!!!!
                * Already have .is_flush()
                * .is_straight_flush()
                * INTRODUCING: IterTools
                    * Cards.combinations()
                    * Cards::from<Vec<Card>>
                    * Five::try_from<Vec<Card>>
                    * TEACHABLE: let rust show you what to return
        * Six
            * Before we get started lets update the repl so that we get more feedback
            * impl TryFrom<Cards>
            * Five.display
                * Cards::from(Card)
                * Cards::from(Five)
            * examples/repl.rs Update to use Cards
            * Cards.draw() reqs Cards.deck()
            * .hand_rank()
                * Let IDE drive with the TODO
                    * yay todo!()
                    * Understand the dynamics of teams, bend to the norms. Different people react differently to code styles. It's OK Be cool
                * adding .five_from_permutation to HandRanker
                    * Adding to trait because I know I will need it for Seven as well. NOTE: This is not strict TDD.
                    * REFACTORING: Originally a separate trait Permutator in ckc-rs. Decided to consolidate make cleaner.
                    * REFACTORING: Move to Trait
                * Five.sort() no method named `sort` found for struct `Five` in the current scope
                    * Adding to HandRanker trait
                    * Later refactoring to handle less then five cards (Another trait?)
                * And we're green!!!
                * More tests
                    * REFACTORING: Adding HandRanker.hand_rank_and_hand() to trait
                    * Fleshing out
                    * Deliberate using different forms of index strings
                        * Be careful with your patterns and assumptions. TDD can be a self-fulfilling prophecy divorced from reality
            * .sort()
        * TODO: Note on Five.sort() for wheels Ace on top
            * ASIDE: I am fiercely pro TODOs. Many people hate them. That's OK. However, this is my kingdom and I rule it with an iron fist! When you write your book, you too can be Absolute Ruler of your one person empire (Unless you have an editor 😉).
        * Seven
            * Seven holdem boards etc
            * BONUS CREDIT:
                * OK, if you are seriously hard core and want to go for bonus points (awarded via my GFY Cryptocurrency guaranteed to be worthless!!!) Try coding Seven yourself.
                    * What are its reqs? What's different about Six and Seven?
            * .from(array)
            * impl TryFrom<Cards>
            * Drive from implementing HandRanker
                * .five_from_permutation()
                    * Get not yet implemented error
                    * Fix error
                    * make green
                * .hand_rank_value_and_hand()
                    * Why AD 6S 4S AS 5D 3C 2S?
                    * paste over test from Six (which will fail for Seven)
                    * get rid of not yet implemented error
                    * make green
                    * Clippy
            * TODO: No assessors besides .to_arr() do I really need the others?
        * UPDATE repl to handle multiple card lengths
            * Print out HandRank info for best hand if five or more cards.
                * Update to look at Cards length and usher to the right struct
                * NOTE on driving through repl instead of tests
                    * move index out to variable. (Why is it called index?)
                        * check in repl again
                    * let cards = Cards::from_str(index).unwrap();
                        * check in repl again
                    * Add match for default _
                    * Add match for five
                        * impl TryFrom<Cards> for Five
                            * REFACTOR: move match from from_str to TryFrom
            * NEEDED: to_string() for Five, Six and Seven
                * impl Display for Five
                    * 5 => println!("{}", Five::try_from(cards)?),
                        * update main to return error
                            * update sig to fn main() -> Result<(), PKError> {
                                * must return Ok(())
                                * Verify `❯ cargo run --example repl -- -c "AS KS QS JS TS"`
                    * 6 => println!("Six: {}", Six::try_from(cards)?),
                        * impl Display for Six
                    * 7
                        * .to_arr()
                        * .display()
        * UPDATE REPL: Display HandRank
            * show() - Handle generic input for function
            * DEFECT: Make sure it sorts the best hand
                * Five.is_wheel()
                * Make sure it sorts wheels correctly
            * UPDATE: Make it display the original cards
                * Add Display to function's trait requirements
        * FEATURE COMPLETE!!!!
* ASIDE: Code Coverage
    * Through Clion
    * gcov etc...
* EPIC: GAME PLAY - Hold'Em play out hand
    * Two
        * Assessors
        * ::from_str()
            * TryFrom<Cards>
                * From<[Card; 2]>
    * Introducing example/calc
        * Clap Args: dealt, board
        * Display dealt and board
            * Have main return Result<(), PKError>
        * Introducing play::hands::Hands
            * Hands::try_from(Cards)
                * Two::new(card, card)
                    * HP
                    * Must be unique
                        * RF: Returns result
                        * Implements SOK to test for uniqueness
                            * Drive through SOK
                    * First card needs to be higher than second.
                        * Add to happy path test
                * Hands::from_str()
                * Hands::to_string()
                    * Two
                        * .to_string()
                            * .to_vec()
                            * REFACTOR: use Cards to display instead of code duplication
                                * Also refactor for other array types
                                    * .to_vec()
                                    * I LOVE DELETING CODE
            * THE FLOP
                * Show HandRank for each player.
                * INTRODUCING: Game
                    * Flop: arrays::three::Three
                        * DO IT!
                        * NOTE ON READABILITY: I deal with a lot of code. I want to scan it
                            * CODE SPEED READING
                        * `From<[Card; 3]>`
                        * `impl SOK for Three`
                        * `impl TryFrom<Cards> for Three`
                        * `impl Display for Three`
                    * INTRODUCING: Board
                        * The power of `pub` on struct fields.
                        * TryFrom<Cards> for Board
                            * too few
                            * too many
                            * three cards
                                * display
                                    * Default: "FLOP: ____ __, TURN:__, RIVER: __"
                                    * THE BOARD from THE HAND
                            * four cards
                                * getting clunky
                                * REFACTOR: Cards.draw_one from Option to Result
                            * five cards
                        * FromStr for Board
                    * Update calc to display Hands and Board
                        * REFACTOR: Game{Hands, Board}
                        * Game.to_string()
                            * TEST REFACTORING: `fn state() -> (Hands, Board, Game) {`
                        * `DEALT: [6♠ 6♥, 5♦ 5♣] FLOP: 9♣ 6♦ 5♥, TURN: 5♠, RIVER: 8♠`
        * PHASE TWO: Calc the Flop
            * THE PLAN:
                * Display HandRank for each hand
                * Display winning percentages
                * Display outs
            * PHASE 2.1: Display HandRank for each hand
                * Five::from_2and3()
                * calc: iterate through each player's hand and show it's `HandRank`.
                    * REFACTOR: Make `Game` struct fields public.
                    * Hands.iter()
                    * Game.five_at_flop()
                        * Hands.get()
                        * DEFECT: HandRank for Five::default()
                            * impl SOK for Five
                * DEFECT DETOUR: Sort Five by card frequency
                    * .map_by_rank()
                        * Card frequency flags
                            * Card.frequency_paired()
                            * Card.frequency_tripped()
                        * Cards.map_by_rank()
                            * impl indexmap::set::IntoIter for Cards (needs to be indexmap::set::IntoIter not std::vec::IntoIter)
                                * impl From<Vec<&Card>> for Cards
                            * Card.is_flagged_ ... (Used to filter cards)
                                * REFACTOR: Card.is_flagged()
                                    * REFACTOR: Delete is_flagged_ methods and just use .is_flagged()
                    * Cards.flag_
                    * Cards.frequency_weighted()
                        * Cards.add()
                            * Five test effect of adding weighted values on CKC eval
                                * Cards.dump()  debugging helper
                                    * DEFECT FOUND: Issue with Card.bit_string()
                    * CLOSE IT OUT
                        * Five #[test] sort__pair()
            * PHASE 2.2: Display winning percentages
                * THE PLAN:
                    * Determine all cards in hands and on board at flop.
                    * Get every remaining combination of turn and river cards.
                    * Get HandRank for each hand against possible board.
                    * Add up winning hand
                    * Determine the percentages
                * Introducing [Win Counter]https://github.com/ContractBridge/wincounter
                    * AUDIBLE: moved crate code to util::wincounter for now for easy updating.
                * Three all possible at flop
                    * Cards.deck_minus()
                        * Cards.get()
                            * Cards.shuffle() - Want this to harden the unit tests for deck_minus()
                                * util::RandomOrdering
                                    * Add [rand crate]https://crates.io/crates/rand
                    * REFACTOR: Hands.get() top return Option
                        * REFACTOR: Game.five_at_flop() to return Result<Five, PKError>
                * Calc trait - Don't think that I like that name, but it will do for now.
                    * .cards()
                        * impl Calc for Card, Two, Three, Five, Six, Seven, Hands
                    * REFACTOR: rename to Pile
                    * REFACTOR: add .to_vec to trait
                        * Delete Cards::from<Five> - No longer needed
                        * I really love this refactor. It simplifies all the communication between card collections.
                * Game.remaining_cards_at_flop()
                    * Game.play_out_flop() INTRODUCING PLAY OUT!!! WOOOOO!!!!! TPOS 1.1
                        * Game.case_seven()
                            * HandRank Eval
                                * impl Ord for HandRank
                                * STEP 1: Eval
                                    * ::new(hand_rank: HandRank, hand: Five)
                                    * impl From<Five> for Eval
                                    * impl PartialEq for Eval
                                        * impl Hash for Eval
                                    * impl From<Seven> for Eval - Why did I do Five first??!!
                                    * impl Display for Eval
                                    * introducing Logging!
                                        * [log crate]https://crates.io/crates/log
                                        * [env_logger]https://crates.io/crates/env_logger
                                    * .sort()
                                        * verify that Vec<Card> sorts as desired
                                        * verify that Vec<Five> sorts as desired
                                            * sort__vector_of_fives()
                                    * Now let's crunch some Wins
                                    * TPOS 1.2: PlayOut Trait (_wild hare_)
                                        * REFACTORING: Game.remaining_cards_at_flop() to Hands.remaining_after()
                                            * REFACTORING: Sick question? Can I add remaining() and remaining_after to the Pile struct?
                                                * The answer is yes :-)
                                        * WE ARE STILL RED??!! Let's implement PlayerWins
                                            * OK, we got it to build.
                                            * VERY IMPORTANT DANGER DANGER WILL ROBINSON
                                                * Commit before you do a major, structural refactoring!!!
                                                    * For now, we will have a private pof generic injection for game. Later on we may want to get fancier.
                                                    * NOTE: for now, I am playing without a unit test net. I am using the flow of the repl guide me. Later on, when I am done I will harden the code with tests, but for now we freefall. Weeeee!!!!
                                        * OK, now let's clippy this MFer.
                                            * There are few greater highs for me then deleting code after a refactoring. Deleting code is the highest calling of a programmer. Learn to treasure these moments. This is the way.
                                                * Deleting Game.case_seven()
                                        * REFACTORING: Introducing the analysis module
                                            * REFACTORING: Adding combinations_after() and  enumerate_after() to Pile.
                                            * REFACTORING: PlayOut.play_out_flop() Hands just needs a reference.
                                            * RF: Hands.realize_case_at_flop()
                                            * Three::from_str()
                                            * DEFECT ALERT: The Five stored in case is frequency rated for sorting so doesn't match a raw Five, even though they are the same hand. We need to strip those flags before storing them in case.
                                                * Adding .clean() to Pile trait.
                                                    * The beautiful todo!() macro
                                                        * Implement for Card
                                                            * Card::FREQUENCY_MASK_FILTER
                                                        * Five.clean()
                                                            * Add `assert_eq!(hand.sort().clean(), five);` to Five hand_rank mega test
                                                            * Added .clean() to Seven.hand_rank_value_and_hand() hand return
                                                            * Removed unneeded .strip() and .clean() from hand_rank_case_tests::from__seven() test
                                * STEP 2: CaseEval
                                    * .push()
                                    * INTRODUCING: pub(crate) TestData mod
                                        * Hands::from(Vec<Two>)
                                        * Refactoring Hands test data to use TestData.
                                        * Adding constants to Two
                                    * Added util::wincounter from crate for easier updating
                                    * CaseEval.win_count()
                                        * Win::from(index)
                                        * TODO RF: Refactor `Count` as a `struct Count(u16)`.
                                        * DEFECT ALERT: Copied the wrong function in the test data.
                                        * Test #3: A TIE
                                            * Win::or()
                                * STEP 3: CaseEvals
                                    * examples/calc command() function to display command
                                    * impl Pile for Board
                                    * deprecate Game .play_out_flop() .pof() & .remaining_cards_at_flop()
                                * Step 4: Results
                                    * REFACTORING: util::Util::calculate_percentage()
                                    * TestData::wins_the_hand()
                                    * REFACTORING: Results struct to include case and player count.
                                    * .wins_total()
                                    * .wins_and_ties()
                                    * REFACTORING: .wins_total() to use .wins_and_ties()
                                    * .wins_and_ties_percentages()
                                        * removed Wins::percent_calculator() since dupe of Util::calculate_percentage()
                                        * Fixed Results::from_wins() to include case_count and _player_count.
                                    * .wins_total_percentage()
                                    * .player_to_string()
                                    * impl Display
                        * Three: The nuts in order
                            * TheNuts(Vec<Eval>)
                                * impl From<Vec<Eval>> for TheNuts
                                * .to_vec()
                                * .sort()
                                    * Two KT constants (for The Fold)
                                    * REFACTOR: renamed to Hands to HoleCards
                                    * HoleCards.three_into_fives(&self, three: Three) -> Vec<Five>
                                    * HoleCards.three_into_evals(&self, three: Three) -> Vec<Eval>
                                        * impl From<&Five> for Eval
                                    * TestData::evals_the_fold()
                            * Pile.the_nuts()
                                * impl From<Vec<Card>> for Two
                                * REFACTORING to HashMap
                                    * SIDETRACK: impl Pile for Cards
                                        * Pile.contains_blank() as replacement for SOK trait
                                        * REFACTORING: Pile.is_dealt() to replace SOK.salright()
                                    * Five overriding `Eq`
                                        * CANCELLED: This experiment failed.
                                    * SIDETRACK: Bard
                                        * impl BitOr for Bard
                                        * impl BitAnd for Bard
                                        * BitXor, BitAndAssign, BitOrAssign, BitXorAssign
                                        * DOUBLE SIDETRACK: Added bitwise todo!s to Cards.
                                * Nutty.get()
                                    * Evals
                                    * examples/evals_per_class.rs
                                        * HandRanker.eval()
                                        * EvalsPerClass in TheNuts' rustdoc
                                * REFACTOR: Pile.the_nuts() to .possible_evals()
                            * Done!
                                * REFACTOR: Moved possible_evals() test from TheNuts to Three
                        * Two: .possible_evals()
                            * impl From<Vec<Card>> for Three
        * PHASE THREE: Calc the Turn
            * examples.calc add display of the nuts at flop.
                * REFACTOR: move the_hand() from game tests to `TestData`. (Note the clippy warning from the refactoring. Nice catch 📎!)
            * Game.possible_evals_at_flop()
            * Game.possible_evals_at_turn()
                * !.is_dealt()
                * Four
                    * impl From<Vec<Card>> for Four
                    * impl Pile for Four
            * Game.flop_and_turn()
            * Four::from_turn()
            * REFACTORING: Move PlayerWins::seven_at_flop() to Seven::from_case_at_flop()
            * Seven::from_case_at_turn()
            * PlayerWins.case_evals_turn()
                * Calc.display_odds()
                * Game.five_at_turn()
            * PHASE 3.1: Outs
                * REFACTOR: possible_evals to evals
                * REFACTOR: Game.evals_at_turn() to .the_nuts_at_turn()
                * REFACTOR: HandRank struct to own mod
                * REFACTOR: All hand_rank mods to analysis
                * SPIKE: examples/outs.rs
                    * REFACTOR: Move display odds methods to Game
                    * REFACTOR: Move Game.player_wins_at_flop() to PlayerWins::at_flop()
                    * REFACTOR: Move Game.player_wins_at_turn() to PlayerWins::at_turn()
                * Outs.touch()
                * Outs.add()
                    * REFACTORING
                * Outs.len_for_player()
                * Outs.len_longest()
                * Outs.longest_player()
                * Outs.add_from_player_flag()
                * CaseEval adding case Cards field
                    * .cards()
                    * .card()
                    * .cards_is_empty() & .cards_len()
                * Outs.add_from_case_eval()
                * Game.case_eval_at_turn()
                    * Cards::from<&Card>
                    * REFACTORING: Game.case_evals_at_turn() moved from PlayerWins.case_evals_flop()
                        * REFACTORING: Game.turn_case_evals() & Game.turn_case_eval()
                    * REFACTORING: All Game methods to phase (flop, turn, river) first naming
                    * REFACTORING: Game turn and flop wins
                        * CaseEvals.wins()
                    * Game.turn_calculations()
                    * Game.flop_calculations()
                * Game.turn_display_odds() check that turn is dealt
                    * DEFECT: discrepancy with fudd
                        * REFACTORING: Updated Results.player_to_string()
                        * RESOLUTION: Game.turn_remaining()
                        * REFACTORING: Game.turn_cards()
                * REFACTORING: Change to take in reference: Outs::from(&case_evals)
                * FINISHED Outs!
        * PHASE FOUR: Game.river_display_results()
            * TAKE 1: impl From<Board> for Five NOT NEEDED
            * TAKE 2: Seven::from_case_at_river() ABORT!
            * TAKE 3: HoleCards.river_case_eval()
                * Seven.from_case_and_board()
                    * Meditation: Pass primitives by reference or by value?
                        * TODO: Align around passing by reference or value for primitives.
                * Game.river_display_results()
                    * CaseEval.winner()
                        * REFACTORING to return PlayerFlag instead of a single winning index.
                        * ERROR: Inverting winner call to use existing code
                    * Game.river_case_eval()
        * DEFECT: Outs displayed wrong: `cargo run --example calc -- -d "A♠ K♥ 8♦ 6♣" -b "A♣ 8♥ 7♥ 9♠ 5♠" -n`
            * FIXED
        * TODO TD: Write real performance tests.
        * PHASE FIVE: Concurrency
            * REFACTOR: Turn
                * REFACTOR: Add .the_nuts() to Pile trait and have .evals() use it to return evals.
                * HARDENING: Pile trait .the_nuts() tests for arrays
                    * HARDENING: Added getters to TheNuts against underlying evals array.
                        * TheNuts.get()
            * REFACTOR: play/stages
                * Flop (renamed FlopEval)
                    * .gen_case_evals()
                    * CaseEval
                        * .from_holdem_at_flop()
                            * impl TryFrom<&[Card]> for Two
                                * REFACTORING: impl TryFrom<Cards> for Two
                                * Tests - Negative Boundary Conditions:
                                    * .try_from__card_slice__empty_slice()
                                    * .try_from__card_slice__one_card()
                                    * .try_from__card_slice__three_cards()
                                    * .try_from__card_slice__first_card_blank()
                                        * BAD PASS!!! 😱
                                        * impl TryFrom<Card> for Card
                                            * FAIL
                                        * TAKE TWO: Card::filter()
                                        * PASS!
                                    * .try_from__card_slice__second_card_blank()
                                    * .try_from__card_slice__both_cards_blank()
                                    * REFACTORING: rstest for blank tests
                                    * REFACTORING PART DEUX: Mega Consolidation
                                        * first add error param to existing three cases
                                        * add remaining as failures (Don't change error case)
                                        * Make em pass
                                        * the cleanup
                            * REFACTOR: sig to return Result
                            * First test: happy path using The Hand
                            * BUGFIX: Two::display() for blank cards
                            * Finished tests with negative boundary conditions
                    * CaseEvals
                        * Two::from_from<Vec<Card>>
                        * REFACTORING: Deleted Game.flop_case_eval() and flop_case_evals() moved to own structs
                    * REFACTOR: Renamed FlopEval
                    * REFACTOR: Eliminate Game.flop_display_the_nuts() move to Evals.display()
                    * REFACTOR: Eliminate Game.display_evals()
                    * Added test for CaseEvals::new()
                    * impl TryFrom<Game> for FlopEval
                        * Added PKError::NotDealt
                    * Removed dupe of eval_for_player: eval_for_hand
                    * Finished Display
                * TurnEval
            * Deconstructing Calc: DEALING WITH calc PERFORMANCE
                * Created examples/fixed and examples/long
                    * log is going to work out in minute detail the flop calculations
                    * Added Deck from fudd::types::poker_deck
                * VICTORY!!! CaseEvals::from_holdem_at_flop_mpsc()
                    * finally figured out how to use concurrency to make this faster.
                    * We got it down to under 1 second.
        * PHASE SIX: Pre flop DUN DUN DUNNNNNNN
            * 6.1 - Construct Bards from Card
            * 6.2 - Cards from Bard
                * 6.2.1 - Card try_from Bard
            * TODO: Store analysis Bard results in memory
    * Created [pkterm]https://github.com/ImperialBower/pkterm
    * EPIC SEVEN: Transposition
        * Shift Suit trait
            * `impl SuitShift for Suit`
            * `impl SuitShift for Card`
            * `impl SuitShift for Two`
            * HeadsUp
                * Spike: Storage using [Sled]https://github.com/spacejam/sled/tree/main
                    * First relive BCM CSV fun
                        * CSV without Bard. Pure Cards.
                            * `SevenEval`: Need to store Evals with the original `Seven`.
                                * Cards.remaining() (Trying to get only bcm repl example to work here)
                                    * HA!!! Already exists in Pile trait. Wooo!!!!
                                * Cards.into_twos() - Copying over bcrepl functionality
                                    * Cards.divisible_by
                            * examples/csv_card.rs
                            * Serialize `Card` using symbols
                            * analysis/store/heads_up/Row
                                * DEFECT #BAD_TWOS
                                    * Cards.as_twos() fixed
                                * headsup::PreflopRowHash
                                * headsup::HUP static trait
                                    * HUP::two_to_index()
                                    * ________________________
        * GETTING REAL: STORAGE
            * Generate bcm.csv using Serde `examples/generate_bcm.rs`
            * analysis::store::bcm::binary_card_map::BCM
                * `impl From<Vev<Card>> for Bard`
            * analysis::store::bcm::index_card_map::ICM
            * Storing data in the sled embedded database
                * examples/generate_sled.rs
            * Storing data in SQLite
                * impl From<u64> for Bard
                * Got bcm storage to work. WOOO!!!!
                    * TODO: rusqlite in memory tests.
                * THE MAIN EVENT: Heads up preflop calc.
                    * hupcache example
                    * SortedHeadsUp refactoring.
                        * PKError::NotEnoughHands
                        * PKError::TooManyHands
                    * HUPResult
                        * sqlite::Sqlable Trait
                        * SortedHeadsUp::higher_as_bard()
                            * impl From<Two> for Bard
                            * TestData::the_hand_as_hup_result()
                            * impl TryFrom<Bard> for Two
                            * REFACTORING: bcm csv storage
                                * SevenFiveBCM::DEFAULT_PKCORE_75BCM_CSV_PATH
                                * SevenFiveBCM::get_csv_filepath()
                                * TODO: bcm csv feature flag
                        * impl Pile for SortedHeadsUp
                        * SortedHeadsUp::all_possible() -> std::collections::HashSet<SortedHeadsUp>
                            * NOTE renamed unique()
                        * The Blueprint
                            * SortedHeadsUp::wins() returning default for now
                            * TODO: Convert SortedHeadsUp and Wins into a HUPResult.
                            * TODO: Store them in our DB.
                            * TODO: Once we verified that our DB plays nice, we can wire in our BC_RANK_HASHMAP megacache and do the real work.
                        * HUPResult::from_sorted_heads_up(shu: SortedHeadsUp, wins: Wins)
                            * SortedHeadsUp.higher_as_bard()
                            * SortedHeadsUp.lower_as_bard()
                        * impl SuitShift for SortedHeadsUp
                            * SuitShift.shifts()
                            * Added transpositions to examples/hup.rs
                        * impl FromStr for SortedHeadsUp
                        * SortedHeadsUp::generate_csv()
                        * Migrate bcrepl methods to SortedHeadsUp
                        * Shifty shifts and other_shifts
                        * TODO TD: The great bcrepl fudd investigation.
                        * SortedHeadsUp.distinct()
                            * SortedHeadsUp.remove_shifts()
                            * Refactoring distinct()
                        * impl From<SortedHeadsUp> for HUPResult
                        * examples/preflop.rs let's go!
                            * impl SuitShift for HUPResult
                                * impl From<HUPResult> for SortedHeadsUp
                                * TODO TD: SuitShift.opposite() don't trust it.
                            * preflop - take count
                            * impl Shifty for HUPResult {}
                            * inserting test
                            * need Sqlable.exists()
                                * HUPResult::exists()
                            * Added data/sample_hups.db for testing our HUPResult sql.
                        * HUPResult to csv
                            * hup_dump.rs
                            * HUPResult::read_db
                        * Refactoring impl From<SortedHeadsUp> for HUPResult
                            * TestData shu_hup_alignment() test.
                    * tests directory for heavy tests.
                    * Bring in the Fudd
                        * Refactor hup_check
                        * PKError::SqlError
                * The return of bitvec
                    * impl Binary for Bard
                    * impl Display for Bard
                    * Bard.guided()
                    * impl IntoIterator for Cards
                    * impl SuitShift for Cards
                    * SortedHeadsUpSuitBinary
                * Masks
                    * SuitMask
                    * Texture
                    * RankMask
                    * Masked
                        * Two.invert_suits()
                        * SortedHeadsUp.invert_suits()
                        * .type_one_shifts()
                        * .type_six_shifts()
                            * impl Shifty for Masked
                            * MASKED_UNIQUE_TYPEs
                            * SORTED_HEADS_UP_UNIQUE_TYPEs
                        * Masked::distinct()
                * Back to storing distinct
                    * util::terminal::receive_usize()
                    * archive old examples
                    * Cleanup Masked type tests and functions
                        * ~~type_six_shifts()~~
                        * ~~type_one_shifts()~~
                        * Masked.shifts_check()
                        * distinct_diff.csv
                    * examples/insert_distinct
                    * examples/audit
                        * DEFECT: TYPE6 audit fail
                        * REFACTORING: created arrays::matchups::masks module
                        * FIXED with new SuitTextures
                            * examples/strip.rs to get DB with just clean HUPS
                            * examples/pf.rs
                            * DEFECT: Type X shift defect
                    * gaps detour
                * THE MAIN EVENT REDUX
                    * arrays::hole_cards::holdem
    * EPIC _____: Bets
        * Added in sample data from pluribus, etc.
        * Introducing: The Casino Module
            * cashier submodule
            * tables submodule
            * players submodule
            * Adding [uuid]https://crates.io/crates/uuid crate
            * Player struct
                * using [random_name_generator]https://crates.io/crates/random_name_generator for test player names
                * Chips struct
                    * .bet
                    * .all_in()
                    * impl Add for Chips
            * YAML feature
                * [nubibus]https://github.com/ImperialBower/nubibus Pluribus log parser spike
                    * Two::from_pluribus
                    * pub trait Nubibus
                        * refactored Two::from_pluribus to trait
                        * Refactored Nubibus trait to return Result
                    * impl Nubibus for Three
                    * pub struct Pluribus
                        * impl Nubibus for Board
                            * DEFECT: Card::from_str("QQ") doesn't return `PKError::InvalidIndex`
                    * pluribus::actions::Actions
                        * Actions::parse_round()
                        * examples/pluribus acts spike
                        * DEFECT: tripping when the play ends before the flop RESOLVED.
                    * Seat Struct
                        * impl Sub for Chips
                        * CLIPPY UPDATE (rustup update to 1.73.0): added clippy::iter_without_into_iter and clippy::should_implement_trait to allow for now until I can figure it out.
                        * Seat.bet()
                        * impl Display for Seat
                        * Seat.fold
                        * TODO: Tracing/Logging for Pluribus etc
                        * REFACTORING: Seat.seat_number from Cell(u8) to u8
                        * seat::SeatSnapshot
                        * play::positions::Position6Max
                            * Refactored Seat number to Position using enum
                            * Updated Bint crate with BintCell
                            * Lots of work on Nubibus
                                * Added ActionTypes: check sb and bb
                                * Moving actions to queues inside the Nubibus struct
                                * NOTE: Experimenting with copilot generation
                                * Update to PhaseHoldem added PhaseHoldemTracker
                                * Added Position6MaxPointer
                                    * added .next() and increment()
                                    * Refactoring: removed BintCell not needed
                                * Refactoring Nubibus: Removing BintCell Adding Position6MaxPointer
                                * Refactoring Nubibus: Isolating individual acts with `act()`
                                * Adding Action ledger
                                    * Adding Seat.desc()
                                * .pop() to walk through steps
                                    * .seat_check() to validate .pop()
                                        * DEFECT: .seat_state() off by one FIXED
                                * PROBlEM: How do I know a round of play is over?
                                    * POSSIBLE SOLUTION: play::action::Action
                                        * Done!
                                * Add play::action::Action to Nubibus
                                    * Renamed play::action::Action to play::action::ActionTracker
                                    * ActionTracker.next_round()
                                    * Added ActionTracker to Nubibus
            * Two UPDATES
                * Two::combos()
                * Two::filter_pairs()
                * arrays::combos::twos::Twos
                * REFACTOR: Ranges to return Twos
            * Ranges: Finished basic range macro... now need to work on combining them
                * DEFECT: hopefully fixed https://github.com/rust-lang/rust/pull/52234
                * FEATURE: Twos +
                    * Twos pairs +
                    * Twos::from_str()
                        * Twos::parse_individual_range()
                        * Twos::to_string()
                    * REFACTOR: Twos into Twos(HashSet<Two>)
    * EPIC TWO Interlude
        * Testing command line and web services
    * HOUSECLEANING:
        * Bumped versions
        * Updated deprecated `IndexSet::remove` to `swap_remove`.
    * PKWEB
        * Added Two::get_letter_index()
        * Added SortedHeadsUp::get_letter_index()
    * FEATURE: OMAHA
      * impl Ord properly for Eval
      * Adding omaha permutations to Four
      * TODO: Refactor game analysis from array structs
      * Four::omaha_high()
      * 8 or better
        * Cards.collapse()
          * moved to Pile trait
        * Rank.to_eight_or_better_lo_bit
      * BACK TO OMAHA
        * games::omaha::OmahaHigh
          * impl Display for OmahaHigh 
          * impl FromStr for OmahaHigh
            * impl FromStr for Four
              * impl TryFrom<Cards> for Four
          * impl TryFrom<Cards> for OmahaHigh
          * ASIDE: Renamed PKError::InvalidIndex to InvalidCardIndex
          * made Five internal array pub(crate) visible
          * impl Pile for OmahaHigh
          * OmahaHigh.is_valid
      * FEATURE: Razz (v0.0.13)
        * Add bit flag consts to rank
        * Ranks struct to manage hand rank evals.
          * Ranks::sum_or()
          * Ranks::count_ones()
        * Pile::ranks()
        * razz::California for all Razz hand ranks
        * five::hands::Hands a frequency ranked hand collection
        * rank added rank bit flags and methods for Razz hand calcs
        * Added Ranks struct to hold collections of Ranks
        * Added to arrays::HandRanker
          * razz_hand_rank()
          * razz_hand_rank_and_hand()
          * razz_hand_rank_value_and_hand()
          * All of these methods added to the Five, Six, and Seven structs implementations
      * FEATURE: Ranger
        * Created Ranger example 
        * Created HCSymbols 
        * Added Terminal::receive_range()
        * Created arrays::ranges modules. 
          * Moved range into it
        * Created ranked::Combo struct
        * Added PKError::InvalidComboIndex
        * FAILURE: Tried to create a pkrange crate, but got lost in macro hell and had to abandon.
        * Combo struct THE PLAN:
          * Combo constants
          * Combo strings to Combo constants
          * Combo constants to range! macros
          * Combo constructor from string (_i._e. QQ+, AKs-98s, AQJs+)
          * ...
          * Profit!
        * Finished parsing of sub ranges, aka `AKs-98s`
        * FEATURE: Added support for `A♠ A♥,A♦ K♦` and `A♠ A♥ - A♦ K♦` style index strings to `Cards::from_str()`
        * Removed HCSymbols for Combos
        * Added Solver and gto example
        * FEATURE: Compute Combo Blocker percentage 
          * Added From<Two> for Combo
          * Added ComboCount struct to hole available Two hands per Combo
          * Added better ComboPairs.display() function
        * Renaming Solver to vs::Versus
          * Removed Board from Solver since not used yet
      * FEATURE: Casino Table
        * Created CardsCell instead of using cardpack.rs' version.
        * Creating log::TableEvent
          * set_button and move_button methods
          * Upgraded bint to 0.1.9 to fix 0 boundary defect
          * Upgraded bint to 0.1.11 to make it cooler
          * Added cc! and cards! macros
          * Added support for Pile with CardsCell
          * Added CardsCell.force_draw 
          * Added From<Vec<Cards>> to CardsCell
          * Added uuid to table
          * Added add() to Pile
            * Experimenting with letting CoPilot implement. Weeeeee
      * FEATURE: Table Manager
        * The Plan:
          * Read in a vector of Pluribus hands. 
            * ✅ Added Util::read_lines() and Pluribus::read_in_log()
          * Splice the dealt cards into a deck so that they can be dealt back out.
          * Drive through one hand from the Pluribus struct. 
          * Create From To Pluribus validation
          * PLURIBUS example
            * REFACTORING: Remove Table.phase() let it be calculated by the table state
      * FEATURE: Dealer
        * Create a struct that manages the action for a Table struct, routing actions from players.
          * Initial stab done with Dealer struct and dealer_repl example. 
          * DEFECT: Current Dealer game loop requires a human deal to trigger play events. This needs to be automated.
            * Added PlayerState::Ready, used to indicate that the player is ready to play the next hand.
            * Updated seat player functions to start the player struct with the state of Out. Won't be able to play until they specifically switch their state to Ready.
            * Add ready action to Dealer. 
        * ISSUE: Isolate split pots.
          * Types:
            * The players ... 
          * ISSUE: Pots where player goes all in and another player goes all in as well, the player isn't having their excess chips returned to them.