Skip to content

πŸ‚‘ Solving Project Euler Problem #54, which involves analyzing and comparing poker hands to determine the number of hands Player 1 wins.

Notifications You must be signed in to change notification settings

ahlem-phantom/poker-hands-euler-problem-54

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

23 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Poker Hands: Solving Euler Problem #54

Table of Contents
  1. About The Project
  2. Project Implementation

About The Project

This project is my unique take on solving Problem #54 from Project Euler. It is a Poker card game.
So basically we have a file (which can be found in the resources folder) containing 1000 lines == 1000 rounds.

Each round is composed of 10 cards that should be divided into 2 hands related to 2 players. Each player should have 5 cards.

A card is represented by a string composed of two characters:

  • The first character denotes the card’s rank (e.g., β€œ5” for Five, β€œA” for Ace).

  • The second character represents the card’s suit (e.g., β€œS” for Spades, β€œH” for Hearts).

A hand in poker consists of five cards. For instance, the following string represents a hand: 8C TS KC 9H 4S

This hand includes:

  • 8S ==> Eight of Spades

  • TS ==> Two of Spades

  • KC ==> King of Clubs

  • 9H ==> Nine of Hearts

  • 4S ==> Four of Spades

Suits:

Name Symbol Character Value
Spade β™  S
Heart β™₯ H
Club ♣ C
Diamond ♦ D

Tab.1: Suits Symbol Representation.

Ranks:

Name Symbol Integer Value
Two 2 2
Three 3 3
Four 4 4
Five 5 5
Six 6 6
Seven 7 7
Eight 8 8
Nine 9 9
Ten T 10
Jack J 11
Queen Q 12
King K 13
Ace A 14

Tab.2: Card suits and ranks.

The goal of this task is to calculate the number of times that player 1 wins. The winner is determined based on the strength of their hands.

Hand Rank Hand Name Definition / Example Consecutive Same Suit
9 Royal Flush Cards of Ten, Jack, Queen, King, Ace, in the same suit yes yes
8 Straight Flush Cards of the same suit and consecutive ranks: Jβ™ , Tβ™ , 9β™ , 8β™ , 7β™  yes yes
7 Four of a Kind Four cards of the same rank: 9β™ , 9♦, 9β™₯, 9β™  no no
6 Full House Three cards of the same rank, two of another: Kβ™ , Kβ™₯, K♣, 9β™₯, 9♣ no no
5 Flush Cards of the same suit but not consecutive: Aβ™₯, Kβ™₯, Qβ™₯, Tβ™₯, 2β™₯ no yes
4 Straight Cards of consecutive ranks but in the same suit: 7β™ , 6♦, 5♦, 4β™₯, 3♣ yes no
3 Three of a Kind Three cards of the same rank, others distinct: Aβ™ , A♦, A♣, T♦, 4♦ no no
2 Two Pair Two cards of the same rank and two of another: Aβ™ , A♣, Q♣, Q♦, 4♣ no no
1 One Pair Two cards of the same rank, others distinct: Jβ™₯, Jβ™ , T♦, 4β™ , 2β™₯ no no
0 High Card Any kind of hand not mentioned above: highest value card: Aβ™₯, Kβ™₯, Q♦, Jβ™ , 9β™₯ => A in this case - -

Tab.3: Poker hand ranks.

How the solution works

The solution employs anΒ object-oriented programmingΒ approach to simulate a poker game andΒ determine the winner of each hand. At the core of my design are two main classes: theΒ CardΒ class and theΒ HandΒ class. The Card class encapsulates the properties of individual cards, such as suit and number, providing a blueprint for creating and managing card objects. On the other hand, the Hand class represents a list of cards, offering functionalities to add or remove cards, evaluate hand ranks, and compare hands to determine winners. At a high level, the program parses input hands represented as strings, evaluates the rank of each hand, and determines the winner based on the ranks and card values. It employs various methods to analyze hands, such as checking for pairs, flushes, straights, and high cards. Moreover, this solution takes into account scenarios where ties occur (hands of equal strength). TheΒ getHandRankΒ method assesses the rank of each hand, allowing for comparisons to determine the winner.

Strengths and Weaknesses

One aspect I appreciate about my solution is its clarity and organization. By dividing functionality into separate classes (such as Card and Hand), the solution becomes more modular and easier to understand. Here are the key aspects I focused on:

  • Modular Design: I structured the solution into separate utility methods for parsing, analyzing, and comparing hands. This modular approach makes the solution easier to maintain and understand.
  • Use of Streams: While I had only a theoretical understanding of Java Streams before, I found them incredibly valuable in certain methods for their ability to enhance readability and conciseness. They were extremely useful, especially for tasks like counting occurrences and getting the highest card value, etc improving code clarity.
  • Error Handling: I tried to implement error handling to ensure robustness by validating input and invalid scenarios.

However, one potential drawback of the solution is itsΒ complexity in Hand Ranking. While I acknowledge the current complexity, I believe it could be further reduced by breaking down the logic into smaller, more focused methods. One alternative approach I considered, but didn't have time to implement, was assigning a numerical value to each hand type with additional values assigned based on factors such as the highest card in the hand. This would allow for direct comparison based on assigned values, potentially simplifying the comparison process.

New technologies or approaches

Initially, I struggled with understanding the game of poker as I wasn't familiar with card games. However, I found the learning process to be enjoyable and interesting! I was fascinated by the logic involved in evaluating poker hands. The problem itself wasn't too difficult, but what mattered the most was how I structured the code to ensure simplicity, and readability, and reduce complexity. Here are the things I employed:

  • Java Streams: This helped me with tasks like mapping, filtering, and collecting data and introduced a more functional programming approach to certain parts of the solution.
  • Unit Testing: This helped to validate the functionality of various methods, ensuring that changes to the codebase maintain expected behavior.
  • Code Analysis Tool: I used it to detect and rectify coding issues, code smells, and potential vulnerabilities in my code. So, I utilized these tools to calculate the complexity of the code and address any areas of concern. Although I had intended to work with SonarLint for code analysis, I resorted to the Codalyze tool due to limitations in installing extensions. Also, I used Visual Studio Code as my primary IDE.

(back to top)

Project Implementation

Project Structure

poker-hands-54_euler/
β”œβ”€β”€ src/                                                 # Source code directory
|   β”œβ”€β”€ main/
|   |   └── java/
|   |       └── com/
|   |           β”œβ”€β”€ poker/
|   |           |   β”œβ”€β”€ application/                      # Package for utility classes  
|   |           |   |   └── LaunchGame.java               # Main class for running the application
|   |           |   | 
|   |           |   β”œβ”€β”€ common/                           # Package for common classes
|   |           |   |   └── FileResource.java             # Class for file operation
|   |           |   |  
|   |           |   └── domains/
|   |           |       β”œβ”€β”€ Card.java                     # Class representing a playing card
|   |           |       β”œβ”€β”€ Hand.java                     # Class representing a hand of cards
|   |           |       └── HandRankEvaluator.java        # Class for hand comparison and evaluation  
|   |           |   
|   |           └── resources/
|   |                  └── 0054_poker.txt                 # File that contains one thousand random hands dealt to two players
|   | 
|   |   
|   └── test                                              # Test source code containing automated tests (JUnit5)
|       └── java/
|           └── com/
|                └── poker/
|                   β”œβ”€β”€ application/                      
|                   |   └── LaunchGameTest.java                   
|                   | 
|                   β”œβ”€β”€ common/                           
|                   |   └── FileResourceTest.java            
|                   |  
|                   └── domains/
|                       β”œβ”€β”€ CardTest.java                     
|                       β”œβ”€β”€ HandTest.java                     
|                       └── HandRankEvaluatorTest.java       
└── pom.xml
└── README.md

Project Roadmap

  • Phase 1: Understanding the Poker Hands Problem

    • Problem Definition: Defined the problem of Euler #54 of the poker hands and its goals.
    • Poker Rules Review: I needed to understand the rules of poker, especially the ranking and comparison of poker hands.
    • Algorithm Design: I developed an algorithm for evaluating poker hands (just handwriting of a high level).
  • Phase 2: Building Basic Classes

    • Card and Hand Classes: Implemented the Card and Hand classes.
  • Phase 3: Parsing Poker Hands from Text File

    • File Reading: Implemented logic to read the poker hands text file.
    • Hand Parsing: Developed a method to parse each line into two sets of poker hands, one for each player.
    • Round Representation: Created a method to represent a round of poker using the Card and Hand objects (using parseCard and parseHand).
  • Phase 4: Evaluating Poker Hands

    • Utility functions: Created functions like isSameSuit, isConsecutive, getHandHighCard, etc.
    • Hand Evaluation: Wrote down the logic to evaluate and rank poker hands according to the rules of poker.
  • Phase 5: Testing and Optimization

    • Solution Testing: Continuously tested all the elements thoroughly to ensure they worked as expected using JUnit5 tests.
    • Optimization: Continuously refined and optimized the code for efficiency and readability.

Representing Poker Hands in Java

The Card Class

Here’s the description for theΒ CardΒ class:

Method Description
Card(int rank, int suit) Constructor; creates a Card object from a string (e.g., β€œ9S” or β€œAH”).
int getNumber() Returns the rank of the card as an integer (e.g., 9 for Nine, 14 for Ace).
int getSuit() Returns the suit of the card (e.g., β€˜S’ for Spades, β€˜H’ for Hearts).
String toString() Returns a String representation of the card.

Tab.4: Card class description

The Hand Class

This class represents a collection of cards in a poker game.

Method Description
Hand(List<Card> hand) Constructor that takes a List object as input and initializes the internal card list.
getCards() Returns List object containing the cards in the hand.
toString() Returns a String representation of the hand.

Tab.5: Hand class description

The CardUtils Class

This class provides utility methods for parsing and interpreting card representations in a poker game.

Method Name Method Description
mapSuitToNumber(char symbol) Maps a suit symbol (H, C, S, D) to a corresponding integer value (0-3). Throws IllegalArgumentException for invalid symbols.
mapNumberToValue(char number) Maps a number symbol (2-9, T, J, Q, K, A) to its corresponding integer value (2-14). Throws IllegalArgumentException for invalid symbols.
parseCard(String cardString) Parses a card string representation (e.g., "3S") into a Card object. Validates the string format (length must be 2) and throws IllegalArgumentException for invalid strings.

Tab.6: CardUtils class description

The FileUtil Class

This class offers a method to obtain a BufferedReader object for reading resource files. The resources folder is located within the src/main/resources directory of your project structure. Files placed in this folder are accessible via this method:

Method Name Method Description
getFileResource(String filePath) Retrieves a BufferedReader object for reading a file located in the resources folder. The filePath argument specifies the path to the file relative to the resources directory. Throws IllegalArgumentException if the file is not found.

Tab.7: FileUtil class description

The HandUtil Class

This class provides utility methods for parsing a hand string representation into a Hand object, evaluating hand properties like a same suit, consecutive cards, highest card value, etc, and determining the poker hand rank (Straight Flush, Four of a Kind, etc). and Identifying specific card combinations like triplets and pairs.

Method Name Method Description
parseHand(String handString) Parses a hand string representation (e.g., "3S 3D 3H 9C 10H") into a Hand object. Handles invalid input and returns null if parsing fails.
isSameSuit(Hand hand) Checks if all cards in the hand have the same suit (e.g., all Spades).
isConsecutive(Hand hand) Checks if the cards in the hand have consecutive ranks (e.g., 3, 4, 5, 6, 7). Wraps around for high cards (e.g., T, J, Q, K, A).
getHighCard(Hand hand) Returns the value (rank) of the highest card in the hand.
countValuesOccurrences(Hand hand) Calculates the frequency of each card rank in the hand. The resulting Map associates each card rank (key) with the number of times it appears in the hand (value).
getNumberOfPairs(Map<Integer, Integer> occurrenceValues) Counts the number of pairs (two cards with the same rank) in the hand based on the provided occurrenceValues Map.
getHandRank(Hand hand) Analyzes the hand composition and determines the poker hand rank value (0-8) using a series of checks for different hand types (Straight Flush, Four of a Kind, etc.).
getThreeOfAKindValue(Hand hand) Returns the value (rank) of the three cards of the same kind if a Three of a Kind exists in the hand, otherwise returns -1.
getPairValue(Hand hand) Gets the value (rank) of the pair in the hand, if present. Otherwise, returns -1.
getHighestNonPairCard(Hand hand) Determines the highest non-pair card value in a hand.

Tab.8: HandUtil class description

The HandService Class

This class provides functionalities for evaluating and comparing poker hands offering a way to determine the winner between two poker hands.

Method Name Method Description
compareHands(Hand hand1, Hand hand2, Comparator<Hand> comparison) Determines the winner based on a provided Comparator function. This allows for flexible comparison logic based on different hand properties.
compareCardValues(Hand hand1, Hand hand2, Function<Hand, Integer> cardValueFunction) Compares the hands based on a specified card value function. This function should extract the relevant card value for comparison (e.g., highest card, three of a kind value).
compareFullHouse(Hand hand1, Hand hand2) Compares two Full House hands specifically, considering both the triplet and pair values within each hand.
evaluateHands(Hand hand1, Hand hand2) The main entry point for hand evaluation. Analyzes hand ranks and uses appropriate comparison methods based on the rank (e.g., comparing pairs, full houses, or highest non-pair cards).

Tab.9: HandService class description

About

πŸ‚‘ Solving Project Euler Problem #54, which involves analyzing and comparing poker hands to determine the number of hands Player 1 wins.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages