Enhanced Prwin
From Maxin Wiki
[edit] Overview
OH prwin calculation can be switched from its normal table-level weighting calculation to one which considers hands likely to be held by individual opponent chairs. This can only be activated at dll level using the prw1326 structure whose address is passed to the dll at load time. The mechanism OH uses to select opponent cards for each pass of its iterator is, for each active opponent, to choose a random number between zero and .limit and use this as an index into the chair [1326] arrays. .ranklo and .rankhi are checked to ensure that neither of the cards is already in use, if .weight is less than .level another random number is used so that the hand will only be selectable .weight/.level of the time, and if the hand is available after these tests it is assigned for the opponent for that pass of the iterator.
This approach allows the dll coder to implement something very like the current OH weighted prwin, but at opponent chair level; to trim the probabilities of certain opponent hands as the hand develops; or to switch to an approach where only limited handlists are considered for an opponent (possibly without any relative weighting). Please bear in mind that with Enhanced Prwin OH is completely dependent on you to set up the prw1326 structure, and any errors you make will affect the reliability of the result; or cause OH to hang or crash. It is not a feature for the novice coder to experiment with.
[edit] How to use it from DLL
You need to declare the structures and pick up the pointer to them as described in prw1326. To switch OH from its weighted prwin to calculation using data in the structure which you have generated you need to set
prw1326->useme = 1326;
If this is all you do OH will switch at flop and after to an enhanced calculation using the vanilla hand profiles it set up for each chair at initialisation. This will probably give slightly worse results than a well-configured weighted prwin, since the profiles are static!
Now assume that at the flop you become convinced that chair 2 has a pair of aces. No other ace is known, so he can have six possible hands. You want to set the structure so that OH only calculates prwin using those hands for that opponent. First we need to note how OH (or more specifically the PokerEval library) represents cards. Card rank 2 to Ace is represented by 0 to 12. You then add a number representing the suit. For hearts add 0, for diamonds add 13, for clubs add 26, and for spades add 39. Ac is thus 12+26=38. The six pairs he can have are AhAd,AhAc,AhAs,AdAc,AdAs,AcAs. You set the first six positions in the [1326] arrays
prw1326->chair[2].rankhi[0]=12; //Ah prw1326->chair[2].ranklo[0]=25; //Ad ... etc ... prw1326->chair[2].rankhi[5]=25; //Ad prw1326->chair[2].ranklo[5]=51; //As
Since you only want six card pairs to be considered you set prw1326->chair[2].limit=6; Finally you set the weighting for each hand. As each hand is considered equally probable:-
for(i=0;i<6;i++)prw1326->chair[2].weight[i]=prw1326->chair[2].level;
Once the hand was over you would not want to leave chair 2 as having a pair of aces. prw1326->chair[2]=prw1326->vanilla_chair; would reset it.
This was an example of using prw1326 to set an opponent on a handlist. We chose a very small list for ease of illustration, but it is worth noting that this can be dangerous. If another opponent had a handlist which included a pair of aces, and an ace appeared on common, then there would be a risk that OH could not find enough cards to satisfy an iteration and would hang. Since the iteration process is so cpu intensive we did not include code to catch this situation; this can easily be changed if the OH community require it.
Now let us suppose that chair 3 is a tight player. You think he will only place money on the upper half of the cards, and will only reliably pay for the top 25%. In this case you would want to take the vanilla profile and change the weightings on it.
for(i=0;i<332;i++) prw1326->chair[3].weight[i]=prw1326->chair[3].level; for(i=332;i<663;i++) prw1326->chair[3].weight[i]=prw1326->chair[3].level*(663-i)/332; prw1326->chair[3].limit=663;
Because we have set .limit for the chair we do not need to bother about the weightings for the bottom half of the handlist, because they will never be considered.
The procedure above would set a general profile for chair 3 based on pre-flop behaviour. You might have a situation in which his betting behaviour indicated that he might well have a jack in his hand. One response would be to traverse the handlist and reduce the weighting for any hand which did not contain a jack. A quicker technique is to increase the .level value (which automatically reduced the probability for all hands) and then increase the weighting only for hands containing a jack. It can be very useful to build up indexes in your dll which show the hands which contain aces, kings etc, and also for those which contain suited cards. This can speed up this sort of operation considerably.
prw1326->preflop
Even if you use prwin at all pre-flop, there are real advantages to using the normal OH weighted prwin before the flop. Enhanced prwin works by considering specific hand profiles for opponents, and in general pre-flop you are not sure who your opponents later on will be. It also does not use P factor to calculate against a number of opponents; it works strictly on active opponents. Using table tightness and position in an f$P formula to estimate likely number of opponents means that weighted prwin is more useful pre-flop. Nevertheless, if you set prw1326->preflop=1326; enhanced prwin will also be used pre-flop.
prw1326->bblimp
If this is set non-zero then enhanced prwin will not attempt any weighting for a chair who has not voluntarily put any money into the pot pre-flop (i.e. an unraised BB). Please note that this is not set by default, but is a flag which should be set for the prwin calculation to be wholly realistic
prw1326->chair[x].ignore
If this is set non-zero then enhanced prwin will not attempt any weighting for chair x. The iterator will make no assumptions about the chair handlist. The chair itself will still be included in the prwin calculation if it is still playing. It is just treated as if it was an unraised BB, so that it could have any available hand.
CALLBACK
There is a mechanism for synchronising your setting of the prw1326 structure with the prwin cycle. You can specify a callback function which will be invoked immediately before the prwin iteration loop is started. You can use this to copy a local structure image over prw1326. Any code here should be short, simple and clean. Avoid gws() calls, logging or complex external functions. If you think the callback function is a good place to make a couple of hundred PT queries then the OH developers reserve the right to be offensively unsympathetic to you. You have been warned!
If you have callback function in your code of type:
double mycallback(void)
{
//do something here
return 0;
}
then you can set
prw1326->prw_callback=mycallback; prw1326->usecallback=1326;
The return value is not used currently by OH.
[edit] Notes
The simple minded technique for getting a random number between 0 and X is rand()%X. Unfortunately the rand() function returns an integer between 0 and 32767, and if X is not a sub-multiple of 32768 then the result is biased (only slightly if X is not too large). You can code to eliminate this, but in an iterated piece of code this introduces an extra cpu load. Currently in prw1326 we do this elimination on the random numbers used to select the hand from the handlist, but not on the .weight / .level assessment since the default .level of 1024 does not introduce any bias. The effect of an unfortunate choice of .level (such as one > 16384) would be to cause hands with a low .weight to be over-represented in the calculations. If the OH community feels this can cause a problem it can very easily be corrected, at the cost of some cpu load.
If you want to set an opponent on a limited hand list there are two ways you can do it with prw1326; you can take a 1326 list and set the weight of the unwanted hands to zero, or you can create a sequence of just those hands you are interested in and set .limit so that only they are considered. Both will produce the same result, but the latter is more cpu-efficient, particularly for a short list, since the iterator does not have to waste time rejecting many hands with a zero .weight.
When you unload a dll from the OH menu, the .useme and .usecallback flags will be cleared but nothing else in the prw1326 structure will be changed. Another loaded dll will then find the structure as the previous one left it, so you cannot assume the initial OH initialisation unless you do careful housekeeping to restore it on dll exit.
--- work in progress - to be continued ---
