Package edu.villanova.studs.poker.calcengine

Source Code of edu.villanova.studs.poker.calcengine.CalcEngineImplProph

package edu.villanova.studs.poker.calcengine;

import com.javaflair.pokerprophesier.api.adapter.PokerProphesierAdapter;
import com.javaflair.pokerprophesier.api.card.Card;
import com.javaflair.pokerprophesier.api.card.CommunityCards;
import com.javaflair.pokerprophesier.api.card.Hand;
import com.javaflair.pokerprophesier.api.card.HoleCards;
import com.javaflair.pokerprophesier.api.exception.SimulatorException;
import com.javaflair.pokerprophesier.api.helper.MyGameStatsHelper;
import com.javaflair.pokerprophesier.api.helper.MyHandHelper;
import com.javaflair.pokerprophesier.api.helper.MyHandStatsHelper;
import com.javaflair.pokerprophesier.api.helper.MyOutsHelper;
import com.javaflair.pokerprophesier.api.helper.OppHandStatsHelper;
import com.javaflair.pokerprophesier.api.helper.PlayerGameStatsHelper;

import edu.villanova.studs.poker.exceptions.CalcEngineException;
import edu.villanova.studs.poker.exceptions.PokerMessages;
import edu.villanova.studs.poker.transport.PlayerResults;
import edu.villanova.studs.poker.transport.TableDataIntf;
import edu.villanova.studs.poker.utils.TransportUtils;

import java.util.Hashtable;

/**
* This is the abstract parent class for the family of classes that implement
* the calc engine interface using the Poker Prophesier hand calculating API.
* The API is specific to Texas Hold'em poker games, meaning implementations
* should use TableDataImpl class extensions that support community cards.
* It implements the public method to get calculation results required by the
* interface definition, but leaves the setup methods abstract.  This allows
* concrete extensions to only have to implement logic to setup the game data
* while leaving the result generation logic unchanged.
* @unit CalcEngineImplProph.java
* @package edu.villanova.studs.poker.calcengine
* @author Christopher Salembier
* @date 22 Oct 2008
*/
public abstract class CalcEngineImplProph  implements CalcEngineIntf {
   
    /*------------------------------------------------------------------------*/
    /*---------------------------PRIVATE CLASS MEMBERS------------------------*/
    /*------------------------------------------------------------------------*/
   
    //Private members used by the calculation engine to generate results
    //These do not need to be visible to child classes as their implementations
    //only deal with setup of hand data, not processing...
   
    //Multiplier for storing percent values: use one to store as a decimal 0 - 1
    private final static float PCT_MULT = 1;
    
    //This is the API to the Poker Prophesier calculation engine
    private PokerProphesierAdapter adapter;  
    //Helper classes for generating probabilties and statistics.  These are
    //never directly instantiated, but rather references are set via the adapter
    private MyHandHelper myHandHelper;
    private MyOutsHelper myOutsHelper;
    private MyHandStatsHelper myHandStatsHelper;
    private OppHandStatsHelper oppHandStatsHelper;
    private MyGameStatsHelper myGameStatsHelper;
    private PlayerGameStatsHelper playerGameStatsHelper;
    //Members used to return hand results to clients...
    //Holds the results for one player as they are being calculated
    private PlayerResults currResult;
    //An array of all player results to be returned to the client
    private PlayerResults[] playerResults;
   
    //The protected members are populated by concrete implementations.  They
    //represent the cards players, hand & table information needed by the calc
    //engine to generate results...
   
    //Array of hole cards each player is holding (each Hole Cards object
    //contains 2 cards and a folded flag).  Positions in this array can be null.
    protected HoleCards[] playerCards;
    //List of community cards on the table - can contains 0, 3, 4 or 5 cards
    protected CommunityCards communityCards;
    //Hash of other options that can be set through the UI and processed using
    //the setOtherOptions() method implementation
    protected Hashtable<String, Object> options;
    //Stores the enumerated street representation (not the # of actual cards)
    protected int cardsDealt;
    //# of active players in the hand (not folded and have hole cards)
    protected int activePlayers;
    //Total # of players at the table, dont have to have cards or be in the hand
    protected int totalPlayers;
    //# of times the API runs its internal simulations to get the hand results
    protected int simulations;
   
    /*------------------------------------------------------------------------*/
    /*----------------------------METHOD CONSTRUCTORS-------------------------*/
    /*------------------------------------------------------------------------*/   
   
    /**
     * General CalcEngineImplProph constructor.  It sets default values for
     * numeric fields, but does not instantiate the dynamic members.  This
     * action is left to setGameData() & setOtherOptions() implementations
     * because the setup method may vary.  Child classes may override as needed.
     */
    public CalcEngineImplProph() {
        cardsDealt = 0;
        activePlayers = 0;
        totalPlayers = 0;
        simulations = 0;
    }
   
    /*------------------------------------------------------------------------*/
    /*----------------------------PUBLIC CLASS METHODS------------------------*/
    /*------------------------------------------------------------------------*/
   
    /**
     * Declare this method abstract, leaving logical decisions on how to
     * populate Hole & Community information to implementing classes.
     */
    public abstract void setGameData( TableDataIntf td ) throws CalcEngineException;


    /**
     * Declare this method abstract, leaving logical decisions on how to
     * set & process other API options to implementing classes.
     */
    public abstract void setOtherOptions( Hashtable<String, Object> opts );

    /**
     * This implementation of the getCalculationResults() method will be the
     * same for all child classes.  It uses the protected members, populated
     * using implementations of the abstract set methods, to generate hand
     * calculations and return the results using a standard PlayerResults
     * array.  It drives the result generation using a series of helper methods.
     * @return an array of expected results & stats for each player in the hand
     * @throws CalcEngineException if there is any error thrown by the calc
     *         engine.  The exception handling block builds an informative
     *         error message with a linked cause and re-throws the error for
     *         the calling method to handle.
     */
    public PlayerResults[] getCalculationResults( ) throws CalcEngineException {
        //Stores a message of the current action for informative error reporting
        String errMsg = "";
       
        //Catch any processing errors here and re-throw as CalcEngineExceptions
        try {
            //Start by setting up the Prophisier adapter to run the simulations
            errMsg = PokerMessages.CALC_ADAPTER;
            setupProphesierAdapter();
           
            //If there is more than one player then run the multi-player
            //simulation to generate perfect hand result data.
            errMsg = PokerMessages.CALC_MULTI;
            if ( activePlayers > 1 )
                runMultiPlayerSimulation();  
           
            //Run simulations for each individual player to generate imperfect
            //hand results
            errMsg = PokerMessages.CALC_INDIV;
            runSinglePlayerSimulations();
        } catch ( SimulatorException e )  {
          e.printStackTrace(System.out);
            //There was a processing error somewhere.  Build a new exception
            //with a message indicating where the failure occurred and the root
            //cause.  Re-throw the error for the calling method to handle
            throw new CalcEngineException( PokerMessages.CALC_FAILED,
                                           new Object[]
                                            { PokerMessages.TRANSLATE_ARG,
                                              errMsg}, e );
   
        }
       
        //No errors, return the player results array
        return playerResults;
    }
   
    /*------------------------------------------------------------------------*/
    /*-------------------------PROTECTED CLASS METHODS------------------------*/
    /*------------------------------------------------------------------------*/
   
    /**
     * This method is used to generate a new PlayerResults instance to hold
     * calculation results for each player in the hand.  It is used by the
     * private method runSinglePlayerSimulations(), but is declared abstract
     * so individual implementations can configure the results object.  This is
     * because even though PlayerResults is concrete it does contain a method
     * for dynamically setting other data that may not pertain directly to
     * hand calculations.  This is useful in testing or random hand generation.
     * @param playerID is a Unique number to assign the new player results
     * @return a new PlayerResults instance with its other values hash table
     *         populated according to the needs of the implementation.
     */
    protected abstract PlayerResults getNewPlayerResult( int playerID );
   
    /*------------------------------------------------------------------------*/
    /*---------------------------PRIVATE CLASS METHODS------------------------*/
    /*------------------------------------------------------------------------*/

    /**
     * This method is used by the public getCalculationResults() method to setup
     * the calculation engine.  It creates an adapter to the simulator engine
     * and sets the configurable options.  The concrete implementations will
     * determine how these options are set.
     */
    private void setupProphesierAdapter()  {
        //Create an adapter to communicate with the hand simulator engine
        adapter  = new PokerProphesierAdapter();
       
        //Set the # of simulations to run when generating hand results.  How
        //this value gets set depends on the concrete implementation
        adapter.setNumSimulations( simulations );
       
        //Set other options used by the adapter
        //TODO: Pass these in through the options hash
        adapter.setOppHoleCardsRealistic( true );
        adapter.setOppProbMyHandSensitive( true );
        adapter.setMyOutsHoleCardSensitive( true );
    }
   
    /**
     * This method is called to generate the perfect hand result data if there
     * is more than 1 active player in the hand.  After running the player
     * simulations is gets a playerGameStatsHelper reference to read the results
     * @throws SimulatorException if there is an error thrown by the adapter.
     *         Most likely because of invalid player or community configurations
     */
    private void runMultiPlayerSimulation() throws SimulatorException {
        // Run the simulator
        adapter.runPlayerSimulations( playerCards, communityCards, cardsDealt );  

        // Get the PlayerGameStatsHelper
        playerGameStatsHelper = adapter.getPlayerGameStatsHelper();
    }
   
    /**
     * This is the driving method for generating imperfect calculations for
     * each player in the hand.  It is responsible for generating a Player
     * Results object for each player in the hand and building the array to
     * return from the getCalculationResults() method.
     * @throws SimulatorException if there is an error thrown by the adapter.
     *         Most likely because of invalid player or community configurations
     */
    private void runSinglePlayerSimulations() throws SimulatorException {
       
        HoleCards cards;
       
        //Create the results array.  All players have result records created
        //even if they are inactive or folded, so use total, not active Players
        playerResults = new PlayerResults[ totalPlayers ];
       
        //Process each player in a loop.  Create a results object to store the
        //current results, check if the player is inactive or folded, then if
        //they are active run the single player simulation using just their
        //HoleCards.  Finally, add the current player result to the results
        //array, regardless of the player state.
        for ( int ii = 0; ii < totalPlayers; ii++ ) {
            //Create a new Player Results object to store info for the current player
            currResult = getNewPlayerResult( ii+1 );
           
            //Get the next set of hole cards in the player array
            cards = playerCards[ii];
           
            //If there were multiple active players in the hand set the perfect
            //win percent using the player game stats helper
            if ( activePlayers > 1 )
                currResult.setPerfectWinPct( playerGameStatsHelper.getPlayerProb(ii) * PCT_MULT );
   
            //Make sure the player is active before calculating their results
            if ( cards == null )
                //The player was never assigned cards, they are just a place
                //holder for calculating player vs player results
                currResult.setPlayerState( TransportUtils.PLAYER_EMPTY );
            else if ( cards.isFolded() )
                //The player was assigned known cards, but folded.  This
                //changes the perfect win % calculations, but it treated
                //the same as an empty player for imperfect calculations
                currResult.setPlayerState( TransportUtils.PLAYER_FOLDED );
            else
                //Player is active with cards, calculate imperfect data
                runCurrentPlayerSimulation( cards );
           
            //Add the current player info to the reuslts array
            playerResults[ii] = currResult;
        }
    }
   
    /**
     * This is the driving method for generating the imperfect hand calculations
     * for a single player.  It uses the adapter to run the player simulations
     * then uses a series of helper methods to populate the current PlayerResults
     * @param cards are the current players hole cards - they cannot be empty
     * @throws SimulatorException if there is an error thrown by the adapter.
     *         Most likely because of invalid card or community configurations
     */
    private void runCurrentPlayerSimulation( HoleCards cards ) throws SimulatorException {
        //Run the simulation for the current player using imperfect info
        adapter.runMySimulations( cards, communityCards, totalPlayers, cardsDealt );
       
        //Set all the data for imperfect calculations...
        //Set the know info about the cards & hand
        setMyHandData();               
        //Set data about outs and potential opponent hands
        setAllHandData();
        //Set the imperfect win/tie/loss percents
        setImperfectStats();
    }
       
    /**
     * This method is used to set the information that is known about the
     * current players hand. That is the cards it contains and its strength.
     */
    private void setMyHandData() {
        //Get a hand helper reference
        myHandHelper = adapter.getMyHandHelper();
       
        //Set the player's poker hand (pair, flush, full house, etc)
        currResult.setCurrentHand( myHandHelper.getHand().getHandRank() );
       
        //Get the array of cards that make up the players hand
        Card[] cards = myHandHelper.getHand().getCards();
       
        //Since the getHand() method does not have a toString() implementatin
        //loop through the card array and build a single comma delimited string
        String strCards = "" + cards[0];
        for ( int jj = 1; jj < cards.length; jj++ )
            strCards += ", " + cards[jj];
       
        //Set the player's card string
        currResult.setCurrentCards( strCards );
    }
   
    /**
     * This method is used to set information about the chances of the player
     * making particular poker hands and that an opponent may be holding a
     * better hand already.  Since all this info can be set for each potential
     * poker hand (High Card through Strait Flush) it is all set in one loop.*
     */
    private void setAllHandData() {
        //Local processing variables to store results from helper methods
        Card[] cards;
        float myPct;
        float oppPct;
       
        //Get the adapter helpers needed to get outs and player stats
        myOutsHelper = adapter.getMyOutsHelper();
        myHandStatsHelper = adapter.getMyHandStatsHelper();
        oppHandStatsHelper = adapter.getOppHandStatsHelper();
       
        //Process each potential poker hand by looping through the enumerated
        //values that represent each hand/
        for ( int ii = Hand.HIGH_CARD; ii <= Hand.STRAIGHT_FLUSH; ii++ ) {
            //Get an array of cards that can make the current hand
            cards = getOutsArray( ii );
            //Get the % chance that the current hand will be made by the river
            myPct = getMyHandStats( ii ) * PCT_MULT;
            //Get the % chance that an opponent is holding the current hand
            oppPct = getOppHandStats( ii ) * PCT_MULT;
           
            //Check for outs to make the current hand
            if ( cards.length > 0 )
                //There are outs, add each card individually
                for ( int jj = 0; jj < cards.length; jj++ )
                    currResult.setOutCard( ii, cards[jj].toString() );
           
            //Check if there is any chance of making the current hand
            if ( myPct > 0 )
                //Set the % to make the hand
                currResult.setOutPct( ii, myPct );
           
            //Check if there is any chance an opponent already has the hand
            if ( oppPct > 0 )
                //Set the % that an opponent is holding the current hand
                currResult.setOppBeatPct( ii, oppPct );
        }
       
        //Null outs helper means pre-flop or river
        if ( myOutsHelper != null )
            //Set the total # of calculated out cards
            currResult.setImperfectOuts( myOutsHelper.getTotalNumOuts() );
       
        //Null hand stats helper means the river has been dealt (no chance to improve)
        if ( myHandStatsHelper != null )
            //Set the total % chance the hand will improve by the river
            currResult.setImperfectImprovePct( myHandStatsHelper.getTotalProb() * PCT_MULT );
       
        //The opponent hand help is always created, regardless of street, but
        //may be null if there are no opponents
        if ( oppHandStatsHelper != null )
            //Set the total % chance that an opponent will hold a better hand
            currResult.setOppBestHandPct( oppHandStatsHelper.getTotalProb() * PCT_MULT );
    }
   
    /**
     * This method is used by setAllHandData() to get the cards that can make
     * given poker hand for the current player.
     * @param outType an enumerated representation of a poker hand
     * @return an array of cards that can hit to make the specified hand
     */
    private Card[] getOutsArray( int outType ) {
        //The outs helper will be null pre-flop and on the river
        if ( myOutsHelper == null )
            //Out cards cannot be calculated, just return an empty array
            return new Card[0];

        //On the Flop or Turn, get the out card array for the specified hand type
        switch ( outType ) {
            case Hand.HIGH_CARD: return myOutsHelper.getHighCardOuts();
            case Hand.PAIR: return myOutsHelper.getPairOuts();
            case Hand.TWO_PAIRS: return myOutsHelper.getTwoPairsOuts();
            case Hand.THREE_OF_A_KIND: return myOutsHelper.getThreeOfAKindOuts();
            case Hand.STRAIGHT: return myOutsHelper.getStraightOuts();
            case Hand.FLUSH: return myOutsHelper.getFlushOuts();
            case Hand.FULL_HOUSE: return myOutsHelper.getFullHouseOuts();
            case Hand.FOUR_OF_A_KIND: return myOutsHelper.getFourOfAKindOuts();
            case Hand.STRAIGHT_FLUSH: return myOutsHelper.getStraightFlushOuts();
            default: return new Card[0];
        }
    }
   
    /**
     * This method is used by setAllHandData() to get the % chances that any
     * given poker hand will be made by the for the current player.
     * @param outType an enumerated representation of a poker hand
     * @return the % chance that the specified hand will be made by the river
     */
    private float getMyHandStats( int outType ) {
        //The outs helper will be null and on the river
        if ( myHandStatsHelper == null )
            //Alaways a 0% chance to improve on the river
            return 0;
       
        //Get the percent chance to make the specified hand type
        switch ( outType ) {
            case Hand.HIGH_CARD: return myHandStatsHelper.getHighCardProb();
            case Hand.PAIR: return myHandStatsHelper.getPairProb();
            case Hand.TWO_PAIRS: return myHandStatsHelper.getTwoPairsProb();
            case Hand.THREE_OF_A_KIND: return myHandStatsHelper.getThreeOfAKindProb();
            case Hand.STRAIGHT: return myHandStatsHelper.getStraightProb();
            case Hand.FLUSH: return myHandStatsHelper.getFlushProb();
            case Hand.FULL_HOUSE: return myHandStatsHelper.getFullHouseProb();
            case Hand.FOUR_OF_A_KIND: return myHandStatsHelper.getFourOfAKindProb();
            case Hand.STRAIGHT_FLUSH: return myHandStatsHelper.getStraightFlushProb();
            default: return 0;
        }
    }
   
    /**
     * This method is used by setAllHandData() to get the % chances that an
     * opponent may already be holding any given poker hand.
     * @param outType an enumerated representation of a poker hand
     * @return the % chance that an opponent is already holding the specified hand
     */
    private float getOppHandStats( int outType ) {
        //If the opponent outs helper is null just return 0% for the hand
        if ( oppHandStatsHelper == null )
            return 0;
       
        //Get the percent chance an opponent is already holding the specified hand
        switch ( outType ) {
            case Hand.HIGH_CARD: return oppHandStatsHelper.getHighCardProb();
            case Hand.PAIR: return oppHandStatsHelper.getPairProb();
            case Hand.TWO_PAIRS: return oppHandStatsHelper.getTwoPairsProb();
            case Hand.THREE_OF_A_KIND: return oppHandStatsHelper.getThreeOfAKindProb();
            case Hand.STRAIGHT: return oppHandStatsHelper.getStraightProb();
            case Hand.FLUSH: return oppHandStatsHelper.getFlushProb();
            case Hand.FULL_HOUSE: return oppHandStatsHelper.getFullHouseProb();
            case Hand.FOUR_OF_A_KIND: return oppHandStatsHelper.getFourOfAKindProb();
            case Hand.STRAIGHT_FLUSH: return oppHandStatsHelper.getStraightFlushProb();
            default: return 0;
        }
    }
   
    /**
     * This method is used to set imperfect win/loss/tie percents.  Because all
     * the data is based on imperfect information, these #s may not be in line
     * with the improve percents or opponent best hand chances.
     */
    private void setImperfectStats() {
        //Get a stats helper reference to get the imperfect probabilities
        myGameStatsHelper = adapter.getMyGameStatsHelper();               
       
        //Get the imperfect win probability
        currResult.setImperfectWinPct( myGameStatsHelper.getWinProb() * PCT_MULT );
        //Get the imperfect tie probability
        currResult.setImperfectTiePct( myGameStatsHelper.getTieProb() * PCT_MULT );
        //Get the imperfect loss probability
        currResult.setImperfectLosePct( myGameStatsHelper.getLoseProb() * PCT_MULT );
    }
}
TOP

Related Classes of edu.villanova.studs.poker.calcengine.CalcEngineImplProph

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.