// Path: src/shaft/poker/game/table/IPlayerData.java
// public interface IPlayerData {
//     
//     public int amountToCall();
//     public int betsToCall();
//     public double potOdds(int potSize);
//     public int moneyInPotForRound();
//     public int totalMoneyInPot();
//     public int stack();
//     public int position();
//     public String id();
// }
// 
// Path: src/shaft/poker/agent/handranges/weightedrange/IFrequencyTable.java
// public interface IFrequencyTable {
//     
//     public double estimatedStrength(ActionType a, int numBets, Round bettingRound);
//     
// }
// 
// Path: src/shaft/poker/game/table/IPlayerActionListener.java
// public interface IPlayerActionListener {
// 
//     public void gameAction(ITable table, IPlayerData plData, ActionType type, int amount);
//     
//     public void leave(ITable table, IPlayerData plData);
// }
// 
// Path: src/shaft/poker/game/ITable.java
// public interface ITable {
//    
//     enum ActionType {
//         FOLD,
//         CALL,
//         BET
//     }
//     
//     enum Round {
//         PREFLOP,
//         FLOP,
//         TURN,
//         RIVER
//     }
//     
//     public void runGame(int hands, int stackSize, int sBlind, int bBlind);
//     
//     public int numberBets();
//     public int maxBets();
//     public int numberCallers();
//     public Round round();
//     public int potSize();
//     
//     public int numberPlayers();
//     public int numberActivePlayers();
//     public int numberPlayersToAct();
//     
//     public int smallBlind();
//     public int bigBlind();
//     
//     public List<Card> board();
//     
//     public String playerSmallBlind();
//     public String playerBigBlind();
//     public String playerDealer();
//     
//     public void registerListenerForPlayer(String id, IPlayerActionListener listener);
//     public void registerPriorityListenerForPlayer(String id, IPlayerActionListener listener);
//     public void registerListenAllPlayerEvents(IPlayerActionListener listener);
//     public void registerEventListener(IGameEventListener listener);
// 
// }
// 
// Path: src/shaft/poker/game/ITable.java
// public interface ITable {
//    
//     enum ActionType {
//         FOLD,
//         CALL,
//         BET
//     }
//     
//     enum Round {
//         PREFLOP,
//         FLOP,
//         TURN,
//         RIVER
//     }
//     
//     public void runGame(int hands, int stackSize, int sBlind, int bBlind);
//     
//     public int numberBets();
//     public int maxBets();
//     public int numberCallers();
//     public Round round();
//     public int potSize();
//     
//     public int numberPlayers();
//     public int numberActivePlayers();
//     public int numberPlayersToAct();
//     
//     public int smallBlind();
//     public int bigBlind();
//     
//     public List<Card> board();
//     
//     public String playerSmallBlind();
//     public String playerBigBlind();
//     public String playerDealer();
//     
//     public void registerListenerForPlayer(String id, IPlayerActionListener listener);
//     public void registerPriorityListenerForPlayer(String id, IPlayerActionListener listener);
//     public void registerListenAllPlayerEvents(IPlayerActionListener listener);
//     public void registerEventListener(IGameEventListener listener);
// 
// }


import java.util.List;
import shaft.poker.game.table.IPlayerData;
import shaft.poker.agent.handranges.weightedrange.IFrequencyTable;
import shaft.poker.game.table.IPlayerActionListener;
import shaft.poker.game.ITable;
import shaft.poker.game.ITable.*;
import shaft.poker.game.table.*;
        
        if (total > 0) {
            actualFreq = (double) count / (double) total;
        }
        
        // Linearly interpolate with generic default frequency table if not enough data has been collected
        if (total < MINIMUM_DATA) {
            double defFreq = _defaultFreqs.estimatedStrength(a, numBets, bettingRound);
            actualFreq = (actualFreq * (total / ((double) MINIMUM_DATA)))
                    + (defFreq * ((MINIMUM_DATA - total) / ((double) MINIMUM_DATA)));
        }
        
        return actualFreq;
    }
    
    private void doIncrease(int numBets, Round r, ActionType a) {
        if (numBets > 2) {
            numBets = 2;
        }
        
        int lastIdx = ActionType.values().length;
        if (_lastAction != null) {
            lastIdx = _lastAction.ordinal();
        }
        
        _freqs[r.ordinal()][lastIdx][numBets][a.ordinal()]++;
        _lastAction = a;
    }

    @Override
// Path: src/shaft/poker/game/table/IPlayerData.java
// public interface IPlayerData {
//     
//     public int amountToCall();
//     public int betsToCall();
//     public double potOdds(int potSize);
//     public int moneyInPotForRound();
//     public int totalMoneyInPot();
//     public int stack();
//     public int position();
//     public String id();
// }
// 
// Path: src/shaft/poker/agent/handranges/weightedrange/IFrequencyTable.java
// public interface IFrequencyTable {
//     
//     public double estimatedStrength(ActionType a, int numBets, Round bettingRound);
//     
// }
// 
// Path: src/shaft/poker/game/table/IPlayerActionListener.java
// public interface IPlayerActionListener {
// 
//     public void gameAction(ITable table, IPlayerData plData, ActionType type, int amount);
//     
//     public void leave(ITable table, IPlayerData plData);
// }
// 
// Path: src/shaft/poker/game/ITable.java
// public interface ITable {
//    
//     enum ActionType {
//         FOLD,
//         CALL,
//         BET
//     }
//     
//     enum Round {
//         PREFLOP,
//         FLOP,
//         TURN,
//         RIVER
//     }
//     
//     public void runGame(int hands, int stackSize, int sBlind, int bBlind);
//     
//     public int numberBets();
//     public int maxBets();
//     public int numberCallers();
//     public Round round();
//     public int potSize();
//     
//     public int numberPlayers();
//     public int numberActivePlayers();
//     public int numberPlayersToAct();
//     
//     public int smallBlind();
//     public int bigBlind();
//     
//     public List<Card> board();
//     
//     public String playerSmallBlind();
//     public String playerBigBlind();
//     public String playerDealer();
//     
//     public void registerListenerForPlayer(String id, IPlayerActionListener listener);
//     public void registerPriorityListenerForPlayer(String id, IPlayerActionListener listener);
//     public void registerListenAllPlayerEvents(IPlayerActionListener listener);
//     public void registerEventListener(IGameEventListener listener);
// 
// }
// 
// Path: src/shaft/poker/game/ITable.java
// public interface ITable {
//    
//     enum ActionType {
//         FOLD,
//         CALL,
//         BET
//     }
//     
//     enum Round {
//         PREFLOP,
//         FLOP,
//         TURN,
//         RIVER
//     }
//     
//     public void runGame(int hands, int stackSize, int sBlind, int bBlind);
//     
//     public int numberBets();
//     public int maxBets();
//     public int numberCallers();
//     public Round round();
//     public int potSize();
//     
//     public int numberPlayers();
//     public int numberActivePlayers();
//     public int numberPlayersToAct();
//     
//     public int smallBlind();
//     public int bigBlind();
//     
//     public List<Card> board();
//     
//     public String playerSmallBlind();
//     public String playerBigBlind();
//     public String playerDealer();
//     
//     public void registerListenerForPlayer(String id, IPlayerActionListener listener);
//     public void registerPriorityListenerForPlayer(String id, IPlayerActionListener listener);
//     public void registerListenAllPlayerEvents(IPlayerActionListener listener);
//     public void registerEventListener(IGameEventListener listener);
// 
// }


// Path: src/shaft/poker/agent/handranges/weightedrange/frequencytable/PlayerSpecificFrequencyTable.java
import java.util.List;
import shaft.poker.game.table.IPlayerData;
import shaft.poker.agent.handranges.weightedrange.IFrequencyTable;
import shaft.poker.game.table.IPlayerActionListener;
import shaft.poker.game.ITable;
import shaft.poker.game.ITable.*;
import shaft.poker.game.table.*;

        
        if (total > 0) {
            actualFreq = (double) count / (double) total;
        }
        
        // Linearly interpolate with generic default frequency table if not enough data has been collected
        if (total < MINIMUM_DATA) {
            double defFreq = _defaultFreqs.estimatedStrength(a, numBets, bettingRound);
            actualFreq = (actualFreq * (total / ((double) MINIMUM_DATA)))
                    + (defFreq * ((MINIMUM_DATA - total) / ((double) MINIMUM_DATA)));
        }
        
        return actualFreq;
    }
    
    private void doIncrease(int numBets, Round r, ActionType a) {
        if (numBets > 2) {
            numBets = 2;
        }
        
        int lastIdx = ActionType.values().length;
        if (_lastAction != null) {
            lastIdx = _lastAction.ordinal();
        }
        
        _freqs[r.ordinal()][lastIdx][numBets][a.ordinal()]++;
        _lastAction = a;
    }

    @Override
    public void gameAction(ITable table, IPlayerData plData, ActionType type, int amount) {