Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/pokersource/enum/BeliefVector.java


1   // $Id: BeliefVector.java,v 1.5 2002/06/13 03:04:56 mjmaurer Exp $
2   
3   package org.pokersource.enum;
4   import org.pokersource.game.Deck;
5   import java.util.HashSet;
6   import java.util.HashMap;
7   import java.util.Iterator;
8   
9   /** Represents subjective beliefs about the possible hands held by a
10      player.  Maintains a mapping from each hand to its probability of
11      occurrence.
12      @author Michael Maurer <mjmaurer@yahoo.com>
13  */
14  
15  public abstract class BeliefVector {
16    /** Our string representation, from the constructor. */
17    private String myspec;
18  
19    /** The universe of all possible hands.  Should be set in the subclass'
20        constructor.  Used by addRemaining() to know which hands haven't
21        yet been added. */
22    HandGroup universalGroup;
23  
24    /** The belief probability (unconditioned by dead cards) of each hand group.
25        Hash key is HoldemHandGroup, value is Double.  Positive value is the
26        relative probability compared to uniform Bayesian prior (so, a value of
27        +2 means hand from this group are twice as likely as would be expected
28        from a uniform distribution over all possible hands); a negative value
29        indicates an absolute probability (so -0.40 means that hands in this
30        group account for 40% of the total probability).  Values must be either
31        all positive or all negative. */
32    private HashMap groupProb;
33  
34    /** The bitmask of dead cards.  This conditions the probabilities returned
35        by getBeliefProb(). */
36    private long deadCards;
37  
38    /** The belief probability (unconditioned by dead cards) for each atomic
39        starting hand.  Updated as needed by computeUnconditionedHandProb().  */
40    private HashMap uncondHandProb;
41  
42    /** The belief probability (conditioned by dead cards) for each atomic
43        starting hand.  Updated as needed by computeConditionedHandProb().  */
44    private HashMap condHandProb;
45  
46    private boolean hasRelative = false;
47    private boolean hasAbsolute = false;
48  
49    /** Instantiate self from string respresentation.  Meant to be called from
50        subclass's constructor.  The subclass constructor should then parse the
51        string spec and populate groupProb and deadCards. */
52    public BeliefVector(String spec) {
53      myspec = spec;
54      groupProb = new HashMap();
55      deadCards = 0;
56      uncondHandProb = null;
57      condHandProb = null;
58    }
59  
60    /** Instantiate self from string respresentation.  This method must be
61        implemented by all subclasses.  Also, subclass constructors should call
62        super(spec) and then fromString(spec). */
63    public abstract void fromString(String spec);
64  
65    /** Generate string representation of self; the inverse of fromString(). */
66    public String toString() {
67      StringBuffer buf = new StringBuffer();
68      for (Iterator iter = groupProb.keySet().iterator(); iter.hasNext(); ) {
69        HandGroup group = (HandGroup) iter.next();
70        if (buf.length() > 0)
71          buf.append(" ");
72        buf.append(group.toString());
73        double prob = ((Double) groupProb.get(group)).doubleValue();
74        String delim = (prob < 0) ? "=" : ":";
75        int percent = (int) Math.round(100 * Math.abs(prob));
76        buf.append(delim + percent);
77      }
78      if (deadCards != 0)
79        buf.append(" / " + Deck.cardMaskString(deadCards));
80      return buf.toString();
81    }
82  
83    /** Generate a string representation of self that gives probability
84        details for all atomic hands, conditioned on the dead cards. */
85    public String toStringAtomic() {
86      StringBuffer buf = new StringBuffer();
87      for (Iterator iter = condHandProb.keySet().iterator(); iter.hasNext(); ) {
88        Long lhand = (Long) iter.next();
89        long hand = lhand.longValue();
90        double prob = ((Double) condHandProb.get(lhand)).doubleValue();
91        if (buf.length() > 1)
92          buf.append(" ");
93        buf.append(Deck.cardMaskString(hand, ""));
94        int percent = (int) Math.round(10000 * Math.abs(prob));
95        buf.append(":" + percent);
96      }
97      return buf.toString();
98    }
99  
100 
101   /** Return an array of bitmasks representing hands with nonzero probability
102       of occurring (conditioned on the dead cards). */
103   public long[] getHands() {
104     long[] hands = new long[condHandProb.size()];
105     int nhands = 0;
106     for (Iterator iter = condHandProb.keySet().iterator(); iter.hasNext(); )
107       hands[nhands++] = ((Long) iter.next()).longValue();
108     return hands;
109   }
110   
111   /** Return the absolute probability that hand will occur, conditioned on
112       the dead cards. */
113   public double getBeliefProb(long hand) {
114     Double prob = (Double) condHandProb.get(new Long(hand));
115     if (prob == null)
116       return 0;
117     else
118       return prob.doubleValue();
119   }
120 
121   private void computeConditionedHandProb() {
122     // adjust hand probabilities by eliminating hands that require a dead card
123     if (deadCards == 0) {
124       condHandProb = uncondHandProb;
125       return;
126     }
127     double deadProb = 0.0;  // total prob of hands containing dead cards
128     for (Iterator iter = uncondHandProb.keySet().iterator(); iter.hasNext(); ) {
129       Long key = (Long) iter.next();
130       long hand = key.longValue();
131       Double value = (Double) uncondHandProb.get(key);
132       double hprob = value.doubleValue();
133       if ((hand & deadCards) != 0) { // hand uses a dead card
134         /*System.out.println("  condition: dead " + Deck.cardMaskString(hand) +
135           " hprob=" + hprob);*/
136         deadProb += hprob;
137       }
138     }
139     condHandProb = new HashMap();
140     if (deadProb > 0.999999)
141       throw new IllegalArgumentException("dead cards exclude all hands");
142     double condscale = 1 / (1 - deadProb);
143     /*System.out.println("  condition: deadProb=" + deadProb + ", condscale="
144       + condscale);*/
145     for (Iterator iter = uncondHandProb.keySet().iterator(); iter.hasNext(); ) {
146       Long key = (Long) iter.next();
147       long hand = key.longValue();
148       if ((hand & deadCards) == 0) { // hand does not use dead card
149         Double value = (Double) uncondHandProb.get(key);
150         double hprob = value.doubleValue();
151         double condprob = hprob * condscale;
152         /*System.out.println("  condition: update " + Deck.cardMaskString(hand) +
153           " condprob=" + condprob);*/
154         condHandProb.put(key, new Double(condprob));
155       }
156     }
157   }
158 
159   private double totalRelativeProb() {
160     // sum over groups of each group's relative prob
161     // times the group's number of atomic hands
162     double totalRel = 0.0;
163     for (Iterator iter = groupProb.keySet().iterator(); iter.hasNext(); ) {
164       HandGroup group = (HandGroup) iter.next();
165       double gprob = ((Double) groupProb.get(group)).doubleValue();
166       int gsize = group.getHands().length;
167       if (gprob > 0)
168         totalRel += gprob * gsize;
169     }
170     return totalRel;
171   }
172 
173   private double totalAbsoluteProb() {
174     // sum over groups of each group's absolte prob
175     double totalAbs = 0.0;
176     for (Iterator iter = groupProb.keySet().iterator(); iter.hasNext(); ) {
177       HandGroup group = (HandGroup) iter.next();
178       double gprob = ((Double) groupProb.get(group)).doubleValue();
179       if (gprob < 0)
180         totalAbs += -gprob;
181     }
182     return totalAbs;
183   }
184 
185   private void computeUnconditionedHandProb() {
186     uncondHandProb = new HashMap();
187     double totalRel = totalRelativeProb();
188     /*System.out.println("recomputing: totalRel=" + totalRel);*/
189     for (Iterator iter = groupProb.keySet().iterator(); iter.hasNext(); ) {
190       HandGroup group = (HandGroup) iter.next();
191       long[] ghands = group.getHands();
192       double gprob = ((Double) groupProb.get(group)).doubleValue();
193       double hprob;   // absolute probability of each hand in this group
194       if (gprob < 0) {           // group has absolute probability
195         hprob = -gprob / ghands.length;
196       } else if (gprob > 0) {    // group has relative probability
197         hprob = gprob / totalRel;
198       } else {
199         hprob = 0;
200       }
201       /*System.out.println("           : group=" + group.toString() +
202                          ", gsize=" + ghands.length +
203                          ", gprob=" + gprob +
204                          ", hprob=" + hprob);*/
205       if (hprob > 0) {
206         for (int i=0; i<ghands.length; i++) {
207           Long key = new Long(ghands[i]);
208           Double value = new Double(hprob);
209           if (uncondHandProb.containsKey(key))
210             throw new IllegalArgumentException
211               ("duplicate hand: " + Deck.cardMaskString(ghands[i]));
212           uncondHandProb.put(key, value);
213         }
214       }
215     }
216   }
217 
218  /** Set the "dead cards", cards that are known not to be available.  This
219      sets the probability to zero of any hand including any of these cards
220      increases the probabilities of the other hands in proportion. */
221   public void setDeadCards(long cards) {
222     /*System.out.println("DEAD " + Deck.cardMaskString(cards));*/
223     deadCards = cards;
224     computeConditionedHandProb();
225   }
226 
227   private void addHandGroup(HandGroup group, double prob) {
228     /*System.out.println("ADD group=" + group.toString() + ", prob=" + prob);*/
229     groupProb.put(group, new Double(prob));
230     computeUnconditionedHandProb();
231     computeConditionedHandProb();
232   }
233 
234   /** During construction, add a new hand group with its probability of
235       occurrence relative to the Bayesian probability.  Meant to be called by
236       subclass's fromString() */
237   void addHandGroupRelative(HandGroup group, double relativeProb) {
238     if (hasAbsolute)
239       throw new IllegalArgumentException("cannot mix relative and absolute probs");
240     addHandGroup(group, relativeProb);
241     hasRelative = true;
242   }
243   
244   /** During construction, add a new hand group with its absolute probability
245       of occurrence.  The sum of absolute probabilities over groups should be
246       1.  Meant to be called by subclass's fromString(). */
247   void addHandGroupAbsolute(HandGroup group, double absoluteProb) {
248     if (hasRelative)
249       throw new IllegalArgumentException("cannot mix relative and absolute probs");
250     addHandGroup(group, -absoluteProb);
251     hasAbsolute = true;
252   }
253 
254   private void addRemaining(double prob) {
255     /*System.out.println("ADD all remaining hands, prob=" + prob);*/
256     // Form a special hand group whose set of hands is the difference between
257     // the universe of possible hands and hands present in groups we have
258     // already added.
259     HandGroup others = new HandGroup();
260     others.myspec = "<other>";
261     others.myhands = new HashSet();
262     others.myhands.addAll(universalGroup.myhands);
263     for (Iterator iter = groupProb.keySet().iterator(); iter.hasNext(); ) {
264       HandGroup group = (HandGroup) iter.next();
265       others.myhands.removeAll(group.myhands);
266     }
267     groupProb.put(others, new Double(prob));
268     computeUnconditionedHandProb();
269     computeConditionedHandProb();
270   }
271 
272   /** During construction, add each hand not yet added with its probability of
273       occurrence relative to its Bayesian probability.  Meant to be called by
274       subclass's fromString() */
275   void addRemainingRelative(double relativeProb) {
276     if (hasAbsolute)
277       throw new IllegalArgumentException("cannot mix relative and absolute probs");
278     addRemaining(relativeProb);
279     hasRelative = true;
280   }
281   
282   /** During construction, add each hand not yet added with the given absolute
283       probability of occurrence of the entire set of such hands.  The sum of
284       absolute probabilities over groups should be 1.  Meant to be called by
285       subclass's fromString(). */
286   void addRemainingAbsolute(double absoluteProb) {
287     if (hasRelative)
288       throw new IllegalArgumentException("cannot mix relative and absolute probs");
289     addRemaining(-absoluteProb);
290     hasAbsolute = true;
291   }
292 
293   /** After construction, subclasses should call this for a sanity check. */
294   void validate() {
295     if (hasAbsolute) {
296       if (totalAbsoluteProb() != 1.0)
297         throw new IllegalArgumentException("absolute probabilities must sum to 1");
298     }
299     if (hasRelative) {
300       if (totalRelativeProb() <= 0)
301         throw new IllegalArgumentException("relative probabilities must be positive");
302     }
303   }
304 }