# 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.