Welcome Guest, you are in: Login

Assaultware Wiki



Search the wiki


Hand grabbers

NoteCaddy creates data by reading the hands in your database. There are two methods with which this can be done:

Turbo hand grabber creates a cursor (more information) to your database which gives it the ability to quickly cycle through hands. This mode puts significant strain on weak computers. It is turned off by default

Standard hand grabber repeatedly executes queries grabbing 1000 hands at a time. This is much slower than the turbo hand grabber. However, it works well on weak computers.

You configure which hand grabber to use by going to file->settings->database settings


Creating notes

In each hand there are 2-10 players. NoteCaddy will look at each player unless a white list or black list is defined in file->filters. For each player, NoteCaddy will see if any active note definition is valid. If it is, NoteCaddy will create a note for that player. All of the notes are stored in memory temporarily. You can see the progress of this on the creating notes panel in NoteCaddy


This screen lists how many hands are in your database and how many of them NoteCaddy has processed. As you can see in the screenshot, the total hands is listed as an estimate. For large databases, it can take several minutes to count the exact amount of hands. This is time that is wasted since that number is of no practical consequence. The estimate that is used takes only a fraction of a second to obtain. This number will be less accurate if you have purged hands from your database or have encountered a large amount of import errors.

If you are using the HM-App version of NoteCaddy (the one embedded in HM2) then this process will start automatically. Please see Getting Started with NoteCaddy for recommended settings. You can configure NoteCaddy to not automatically create notes by going to file->settings->database settings and unchecking "automatically start taking notes when HM2 opens"


NoteCaddy will create notes only for the database you have selected in HM2. There is no way to configure a certain database to be excluded. When you play new hands, notes are automatically created for them assuming two things are true:
  • You did not configure NoteCaddy to not automatically take notes (see the screenshot above)
  • Your entire database has been processed already

Saving notes to the database

The data cannot remain in memory as that would be lost when you close HM2. Therefore it is saved to the database periodically. When this happens, you will see a message that says "Saving notes to the database" on the creating notes panel. By default this is every 2000 hands. You can configure NoteCaddy to wait longer by going to file->settings->database settings and entering a larger number


In the above image you can see that it is set to 5000. This can lead to a performance increase. The reason is that there is less time spent writing to the database. However, if HM2 closes in the middle of those 5000 hands, then no data will be saved and the next time you open HM2 they will need to be processed again. Therefore you should avoid making this value too large

Vacuuming your database

The database that HM2 uses is PostGres. This database temporarily occupies excessive disk space when data is being created and updated. You can immediately reclaim this space by performing the vacuum function. You can perform this directly from NoteCaddy by clicking tools->Vacuum Database. Furthermore, a Pending Task will be created for you if you process more than 100,000 hands. This is as a reminder that you may want to perform a database vacuum.

Data persistence format

NoteCaddy stores all player data in the database. One row is created for each player and the full data is stored as a single piece of text. This is done to avoid issues with database schema changes. The name of the table is notecaddy_data. If you are a developer you can easily read the data from there.

An example query would be:

select * from notecaddy_dat where player_id = 7


select ncd.* from notecaddy_data ncd inner join players p on p.player_id = ncd.player_id where p.playername = 'SretiCentV'}}

Below is a source code sample of this data is parsed

{{ String rawNote = dt.Rows[0]["data"].ToString();

if (rawNote.Trim() == "") return null;

String[] noteTokens = rawNote.Split('\n');

Int32 index = 0;

foreach (String noteToken in noteTokens) { NoteDetails nd = new NoteDetails();

nd.Player = player;

String[] fieldTokens = noteToken.Split('\t');

Int16 dbID = Int16.Parse(fieldTokens[0]);

if (!GlobalController.DbNoteDefinitions.ContainsKey(dbID)) { ReadDescription definition = GlobalController.NoteDefinitions.FirstOrDefault( d => d.DatabaseID == dbID);

if (definition == null) continue;

try { GlobalController.DbNoteDefinitions.TryAdd(dbID, definition); } catch { }

} nd.Definition = GlobalController.DbNoteDefinitions[dbID]; nd.DatabaseIndex = dbID;

//this prevents the note from getting deleted using the task //if (!nd.Definition.IsActive) // continue;


if (nd.Definition == null) nd.Definition = new ReadDescription();


nd.ShowPercentage = true; nd.ShowPrefix = true;

nd.ReadID = nd.Definition.ID.ToString(); nd.Demographics = Int16.Parse(fieldTokens[7]);

nd.Prefix = nd.Definition.Category;

//List instanceHits = new List();

#region instances

String rawGuilty = fieldTokens[1];

if (!String.IsNullOrEmpty(rawGuilty)) { String[] guiltyTokens = rawGuilty.Split(',');

foreach (String guiltyToken in guiltyTokens) instances.Add(Int32.Parse(guiltyToken));

//Int32 skip = 0;

if (!nd.Definition.IsSaveHistory) { nd.Instances = instances[0]; nd.Opportunities = instances[1]; } else {

//if (useLast != 0 && useLast < instances.Count) // skip = instances.Count - useLast;

foreach (Int32 i in instances)//.Skip(skip)) { nd.GuiltyHands.Add(i);

nd.Opportunities++; nd.InstanceIndex++;

if (i > 0) { nd.Instances++; //instanceHits.Add(i); } } } }


#region range variable

String rawRangeVar = fieldTokens[5]; if (!String.IsNullOrEmpty(rawRangeVar)) { Int32[] rangeVarResults = rawRangeVar.ToInt32List();

foreach (Int32 i in rangeVarResults)//.Skip(skip)) { //if (i == 0) // continue;

nd.RangeVariableResults.Add(i); }

nd.RangeVarIndex = rangeVarResults.Length; }


#region spark

Int32[] sparkPoints = null; String rawSpark = fieldTokens[2];

if (!String.IsNullOrEmpty(rawSpark)) { sparkPoints = rawSpark.ToInt32List(); //Int32 skip = 0; //if (useLast > 0) // skip = sparkPoints.Length - nd.Instances; //if (skip < 0) // skip = 0;

//Int32 index = -1; foreach (Int32 i in sparkPoints)//.Skip(skip)) { nd.SparkSequence.Add((Int16)i); //index++;

//if (i == 0) // continue; //if (!nd.SparkRange.ContainsKey(i)) // nd.SparkRange.Add(i, 0); //nd.SparkRange[i]++; }

nd.SparkIndex = sparkPoints.Length; }


#region scatter

String rawScatter = fieldTokens[3]; if (!String.IsNullOrEmpty(rawScatter)) { Int32[] betSizes = rawScatter.ToInt32List(); //Int32 skip = 0; //if (useLast > 0) // skip = betSizes.Length - nd.Instances; //if (skip < 0) // skip = 0;

//Int32 index = -1; foreach (Int32 i in betSizes)//.Skip(skip)) { nd.ScatterSequence.Add((Int16)i); //index++; if (i <= 0) continue;

//ScatterPoint sp = new ScatterPoint();

//if (nd.Definition.IsSaveHistory) // sp.HandID = instances[index];

//if ((sp.HandID > 0 || !nd.Definition.IsSaveHistory) && // nd.SparkSequence.Count > index) //{ // sp.PotRatio = (Decimal)i / 100; // sp.Strength = nd.SparkSequence[index]; // nd.ScatterPoints.Add(sp); //}


nd.ScatterIndex = betSizes.Length; }


#region timing points

String rawTiming = fieldTokens[4];

if (!String.IsNullOrEmpty(rawTiming)) { Int32[] timings = rawTiming.ToInt32List(); //Int32 skip = 0; //if (useLast > 0) // skip = timings.Length - nd.Instances; //if (skip < 0) // skip = 0;

//Int32 index = -1; foreach (Int32 i in timings)//.Skip(skip)) { nd.TimingSequence.Add((Int16)i); //index++; //if (i <= 0) // continue;

//TimingPoint tp = new TimingPoint(); //tp.Action = TableActions.Bet;

//if (nd.Definition.IsSaveHistory && instances.Count > index) // tp.HandID = instances[index]; //tp.TimingDelay = (Int16)i;

//if (nd.SparkSequence.Count > index) // tp.Strength = nd.SparkSequence[index];

//tp.Street = nd.Definition.AutoDetectStreet();

//if (tp.TimingDelay > 0) // nd.TimingPoints.Add(tp); }

nd.TimingIndex = timings.Length; }


#region other variables

String rawOther = fieldTokens[6];

if (!String.IsNullOrEmpty(rawOther)) { Int32[] othervarresults = rawOther.ToInt32List();

foreach (Int32 i in othervarresults) { nd.VariableResults.Add(i); } }


#region player partition

String rawPlayer = fieldTokens[8]; if (!String.IsNullOrEmpty(rawPlayer)) { Int16 id = Int16.Parse(rawPlayer); nd.PlayerPartitionID = id;

PartitionRange guiltyPartition = nd.Definition.PlayerPartitionRanges .FirstOrDefault(p => p.ID == id);

if (guiltyPartition == null) guiltyPartition = GlobalController.GlobalPartitions .FirstOrDefault(p => p.ID == id);

if (guiltyPartition != null) { if (guiltyPartition.DisplayMinimum > 0) nd.MinimumPlayerRange = guiltyPartition.DisplayMinimum; else nd.MinimumPlayerRange = guiltyPartition.Minimum;

if (guiltyPartition.DisplayMaximum > 0) nd.MaximumPlayerRange = guiltyPartition.DisplayMaximum; else nd.MaximumPlayerRange = guiltyPartition.Maximum; } else { switch (nd.GameSize) { case GameSizes.HeadsUp: nd.MinimumPlayerRange = 2; nd.MaximumPlayerRange = 2; break; case GameSizes.Medium: nd.MinimumPlayerRange = 3; nd.MaximumPlayerRange = 6; break; case GameSizes.Full: nd.MinimumPlayerRange = 7; nd.MaximumPlayerRange = 10; break; } } } else { switch (nd.GameSize) { case GameSizes.HeadsUp: nd.MinimumPlayerRange = 2; nd.MaximumPlayerRange = 2; break; case GameSizes.Medium: nd.MinimumPlayerRange = 3; nd.MaximumPlayerRange = 6; break; case GameSizes.Full: nd.MinimumPlayerRange = 7; nd.MaximumPlayerRange = 10; break; } }


#region stack partition

String rawStack = fieldTokens[9];

if (!String.IsNullOrEmpty(rawStack)) { Int16 id = Int16.Parse(rawStack); nd.StackSizePartitionID = id;

PartitionRange guiltyPartition = nd.Definition.AverageStackPartitionRanges .FirstOrDefault(p => p.ID == id); if (guiltyPartition != null) { nd.MinimumAverageStackRange = guiltyPartition.Minimum; nd.MaximumAverageStackRange = guiltyPartition.Maximum; } }


nd.NetBigBlinds = Decimal.Parse(fieldTokens[10]);

For additional performance tips, please see NoteCaddy speed improvement

ScrewTurn Wiki version Some of the icons created by FamFamFam.