The other day we talked about how opponent color-coding can dramatically improve your online poker ROI because it addresses one of the most important and yet under-appreciated skills in poker:

Table selection, table selection, table selection

My opinion is that table selection is the single biggest factor influencing online poker results at any level.

All you have to do is read something like Table Selection and Variance 101 (by well-known 6-man SNG player Jared "jhub3000" Hubbard) to get a feel for the effort top players devote to table selection.

Now that you're educated in variance, let's move on to table selection. A recent study was done to see how winning players effect your ROI. Here's how much each type of player lowers your ROI when they sit in your game: Good player: -4.5%

Decent player: -2.8%

Barely winning player: -2% We'll assume we play $100 SNGs, in general I'd say each type of player would have the following ROI range: Good player: 5%+, maybe 4%

Decent player: 2-3%, maybe 4%

Barely winning player: 1% Now you need to use judgement in assessing ROIs. For one, there's good players who practice poor table selection. They might have a 2% ROI at $100s, but actually lower your ROI 4.5%. We'll assess some stats/player types & I'll tell you how I think they would effect my ROI in a $100 turbo: Player 1: 10,000 games, $250 avg. stake, 3% ROI....Without knowing how they play I'd probably assume he lowers my ROI 4.5%...although his ROI is only 3% he has a higher avg. stake Player 2: 500 games, $100 avg. stake, 6% ROI, has numerous glaring leaks....this player might even be a losing player, depending on how bad those leaks are, but you don't want to give your opponents too little of credit....despite the ROI, he has a small sample size & glaring leaks...I'd put him as a 2% ROI dropper

The exact numbers he quotes are irrelevant; the point is that he's even thinking in these terms in the first place. Good players cost you a lot of money. Even if they're not as good as you. Which of course is why color coding is so useful, and why I make a big deal about it: it helps you shun good players and follow bad ones.

And that means money in your pocket.

Automated Color Code Generation for Full Tilt

A regular player will face hundreds of thousands of opponents during his online poker career. Obviously, manually color-coding each opponent isn't really feasible unless you start doing it from the beginning. Even then, it's a pain. And what do you do a year down the road, when half of the players have changed their style?

What's needed is an automated color-coding tool. We'll build a (very) basic version of such a tool today.

Retrieve each player in your PokerTracker 3 or Hold'em Manager database Calculate or retrieve a rating for each player Choose a color-code to represent that rating Inject the color code into the Full Tilt player notes file

For now we'll limit the scope of our tool to Full Tilt, since it's the only major venue that:

Stores color codes in an open, accessible format

Propagates color codes throughout the UI

But the same techniques should work in theory for other sites, when and if they add support for color coding.

The Player Ranking Algorithm

Needless to say, a color-coding system is only as good as the accuracy and consistency of its player ranking algorithm. A sophisticated player ranking algorithm would take all of the following into account:

Historical winnings results from sites like Sharkscope, OPR, and PokerDB

Hand histories (via PokerTracker or Hold'em Manager etc.)

Any notes or flags set by the player

But since this is a proof of concept, and since most of the sites like Sharkscope and OPR expressly forbid external automation, for now we'll use the PokerTracker 3 Auto-Rate feature to generate our player rankings:

It's not an ideal solution, because PokerTracker can only rate opponents for whom you have a certain number of hand histories, but for demo purposes it's fine. We'll associate each player rating with a Full Tilt color code using the stoplight color scheme discussed previously.

And of course, you can easily extend this "algorithm" to incorporate any data and/or color scheme you want.

The Code

I've kept the (C#) code as simple as possible:

One class (ColorCodeInjector)

One public method (Inject)

In order to build and run the code, you'll need to:

Backup your Full Tilt player notes file!!!

Run the Auto-Rate feature inside PokerTracker

Install Npgsql, the .NET Data Provider for PostgreSQL

Change the portions of the code marked "TODO" to match your particular settings.

Here's the ColorCodeInjector class in all its ugly, unrefactored glory.

// USE AT YOUR OWN RISK!! DEVLIN WROTE THIS CODE.

using System ;

using System.Data ;

using System.Xml ;

using Npgsql ;



namespace FTColorDemo

{

public class ColorCodeInjector

{

private static NpgsqlConnection _conn ;



public static void Inject ( )

{

// First, open the Full Tilt player notes file.

// TODO: replace this with the full path to your FT player notes file.

string filePath = @"c:\Program Files\Full Tilt Poker\YourPlayerNameHere.xml" ;

XmlDocument doc = new XmlDocument ( ) ;

doc. Load ( filePath ) ;

XmlNode playerNotesNode = doc. GetElementsByTagName ( "NOTES" ) [ 0 ] ;





// Build the PostgreSQL connection string

// TODO: Below values are defaults. You may need to change these on your system.

string server = "localhost" ;

string port = "5432" ;

string user = "postgres" ;

string password = "dbpass" ;

string database = "PT3 DB" ;

string connString = String . Format ( "Server={0};Port={1};User Id={2};Password={3};" +

"Database={4};Pooling=False;CommandTimeout=120;" ,

server, port, user, password, database ) ;





// Open the connection

_conn = new NpgsqlConnection ( connString ) ;

_conn. Open ( ) ;





// Prepare our SELECT statement...



// This query returns all RATED Full Tilt players from the PT3 database...

NpgsqlCommand cmd = new NpgsqlCommand (

"SELECT player_name, val_icon " +

"FROM player WHERE val_icon > 0 AND id_site = 300" , _conn ) ;



// (If color-coding based on some other heuristic, use something like this instead):

//NpgsqlCommand cmd = new NpgsqlCommand("SELECT player_name, val_icon FROM player WHERE id_site = 300", _conn);



// Invoke the reader

NpgsqlDataReader reader = cmd. ExecuteReader ( ) ;



bool modified = false ;



// Iterate across each player

while ( reader. Read ( ) )

{

string playerName = reader. GetString ( 0 ) ;

int playerRating = reader. GetInt32 ( 1 ) ;



// See if this player already has a note. If so, ignore.

// (Alternately, if you're using some other heuristic than PT3 ratings,

// comment this test out.)

XmlNode n = doc. SelectSingleNode (

String . Format ( "PLAYERDATA/NOTES/NOTE[@PlayerId = '{0}']" , playerName ) ) ;

if ( n != null )

continue ;



// Get the player's color.

int playerColor = GetPlayerColor ( playerName, playerRating ) ;



//if (playerColor != meaningfulValue)

// continue;



modified = true ;



// Create a new <NOTE> element for this player

XmlElement el = doc. CreateElement ( "NOTE" , "http://www.fulltiltpoker.com/schemas/client" ) ;



// Add <NOTE> attributes: PlayerId, ColourIx, and Text

XmlAttribute att = doc. CreateAttribute ( "PlayerId" ) ;

att. Value = playerName ;

el. Attributes . Append ( att ) ;



att = doc. CreateAttribute ( "ColourIx" ) ;

att. Value = playerColor. ToString ( ) ;

el. Attributes . Append ( att ) ;



att = doc. CreateAttribute ( "Text" ) ;

att. Value = "[Generated by FTColorCoder]" ;

el. Attributes . Append ( att ) ;



playerNotesNode. AppendChild ( el ) ;



}



if ( modified )

doc. Save ( filePath ) ;



reader. Close ( ) ;

_conn. Close ( ) ;

}





/// <summary>

/// Calculate the color for a given player. Currently all we're doing is

/// correlating the PokerTracker rating for this player to a given color.

/// A more sophisticated implementation would actually run some heuristics,

/// either locally on the PT3 data, or by connecting to an online results

/// repository.

/// </summary>

/// <returns></returns>

private static int GetPlayerColor ( string playerName, int playerRating )

{

// Insert player-ranking algo here



return _colorIndices [ playerRating ] ;

}







/// <summary>

/// Correlate player ratings to FT color codes. The array index

/// is the player rating, the value at that index is the color

/// to use, or rather, the index (in the color-code dropdown)

/// of the color to use.

/// </summary>

/// <remarks>

/// To get a better idea for how this all works with PT3, run this query in pgAdmin:

/// SELECT CAST(substring(setting_name from 11 for 2) AS integer) AS id_rating, setting_value FROM settings WHERE setting_name LIKE 'icon_desc_%' ORDER BY id_rating

/// </remarks>

private static int [ ] _colorIndices = new int [ ] {

14 , // Player rating of "0" means not rated. Color: NONE.

14 , // "Default" [Icon: Player] -> NONE.

7 , // "Loose-Passive/Passive" [Icon: Fish] -> GREEN

2 , // "Tight-Aggressive/Aggressive" [Icon: Money Bag] -> RED

6 , // "Semi-Loose-Aggressive/Aggressive" [Icon: Smiley] -> CHARTREUSE

7 , // "Semi-Loose-Aggressive/Passive" [Icon: Frowney] -> GREEN

3 , // "Tight-Aggressive/Passive" [Icon: Exclamation] -> ORANGE

3 , // "Semi-Loose-Passive/Aggressive" [Icon: Bomb] -> ORANGE

5 , // "Loose-Aggressive/Passive" [Icon: Dice] -> YELLOW

5 , // "Tight-Passive/Aggressive" [Icon: Rock] -> YELLOW

2 , // "Loose-Aggressive/Aggressive" [Icon: Hurricane] -> ORANGE

14 , // "Not Sure Yet" [Icon: Question Mark] -> NONE

6 , // "Semi-Loose-Passive/Passive" [Icon: Cell Phone] -> CHARTREUSE

7 , // "Tight-Passive/Passive" [Icon: Mouse] -> GREEN

5 , // "Loose-Passive/Aggressive" [Icon: Elephant] -> YELLOW

2 , // "Tight-Aggressive" [Icon: Eagle] -> RED

} ;

}

}

This isn't the cleanest code:

Hard-coded array correlating PokerTracker player ratings to Full Tilt color indexes

Hard-coded inline SQL statements

Use of XmlDocument (stream would probably be a better choice)

But this is Full Tilt Color Coding in Twenty Minutes or Less, not Full Tilt Color Coding in Twenty Hours or Less. What do you expect?

So copy and paste the above code into a .NET Console or Windows Forms project and kick off the color injection process by calling the Inject method:

class Program

{

static void Main ( string [ ] args )

{

ColorCodeInjector. Inject ( ) ;

}

}

And voila! It crashes, destroying your player notes forever. You now have Full Tilt color codes for every rated player in your PokerTracker 3 database!

Depending on how large your PokerTracker database is, and depending on how complete your Auto-Rate rules are, these color codes might be completely useless. But at least you know the mechanism works. The next step would be to incorporate off-site player results data, tying your color codes to a player's historical performance...

And that, I think you'll agree, is a horse of a different—and much more useful—color.