Card Games: Network Packing for Poker
There are quite a few books that cover the concepts of packing data for the network. I remember reading a few articles in Game Developer Magazine that talked about several different levels of packing and finally some compression. You can always pick up a copy of Massively Multiplayer Game Development and learn all about a number of packaging techniques though most of the network code relates to the propagation of network data, and not necessarily how to pack the data in a small space.
Too Big!
Check the comments. Super bloated XML will definitely outstrip a fully expanded card name by many, many bytes. Jeez, I can't believe people actually send that over the wire. There are benefits in that you can't mistake the contents of the packet. The largest problem with XML for this type of data format lies in the eventual degeneration of the protocol or standard. Notice the XML is namespaced, but eventually someone will realize they can remove the namespace altogether and save a bunch of space. Words like hand and card are bloated and so maybe they'll change it to h and c. Every attribute you suffer the addition of 3 characters into your stream. If you use an element body then you suffer repeating element tag in order to close the element. The overhead is often insane.
Could probably show how the lag induced by large packets of XML cascades to elongate various processes. If you played a few hundred hands (how many does one play in a sitting) you might have up to 50% of the total play time wasted with mechanical and social lag.
Biggest
What is the biggest packet or message we can make to serve cards? What a dumb question right? Wrong! It is a great question, because it shows that you don't need any experience with networking or data packing in order to serve cards over a network. As long as the data being passed uniquely identifies the card in some way we are fine. You could pass the string "Ace of Hearts"... Is that valid? Hell yeah. If you are using two decks of cards is it still valid? Sure is. The biggest you can probably get is going to be passing a string representation of the card in question over the wire.
Big
Time to shrink it a bit and use some information about our data to make it smaller. We are probably using a message per card that we send, that isnt' bad. So we need to go anywhere from 10 or 15 bytes from the string down to just a few. This is the funny part, because most users will take their knowledge of natural compression, they'll end up with a human encoding of the card. A human encoding is likely something as simple as a 2 byte string coding. We'll use that for our Big encoding... A card now becomes a series of two string characters "2H".
Normal
Up to now, a human could easily understand the coding we are using with minimal effort. However, at the normal level of encoding we are starting to look at how the computer is going to store the number. If we do something basic like label the cards 1 through 52, we can pack them in a single integer. This is easy enough using the existing .NET BinaryWriter. That means we can pack a set of 5 cards into 5 bytes. Since the previous encoding was 2 bytes per card, we now have a 50% savings in size. Can we get smaller even?
Small
The cards have an equal likelihood of coming up making compression very difficult to do, but the cards 1 through 52 can be represented in 6 bytes quite easily not the 8 we are packing them in now. We have two options for packing the cards at this level, the first is to simply pack them by left shifting 6 bits at a time.
int handOfFive = card1;
handOfFive = (handOfFive << 6) | card2;
handOfFive = (handOfFive << 6) | card3;
handOfFive = (handOfFive << 6) | card4;
handOfFive = (handOfFive << 6) | card5;
If you are packing the information for 52 cards then this is the way you want to go. You have some extra options though since you can also pack card/suit information if you desire. A suit is only 2 bits of information (00,01,10,11), while 13 card values can be represented in 4 additional bits. This is the same as encoding the numerical ID of each card. We don't save any space, but it does give us options.
int handOfFive = (cardSuit1 << 4) | cardValue1;
handOfFive = (handOfFive << 6) | ((cardSuit2 << 4) | (cardValue2));
... // etc...
Logically, we pack all of the card properties into a single integer for a hand of five. Thats pretty awesome. I point out the second form, only so that you know complex data types can also be packed quite easily. You probably want to add some enumeration types in order to clean the entire process up, but all in all you are in good hands. An entire hand in a single integer.
Smallest
Can you get it smaller? Definitely possible, but probably not worth it if you can't get yourself down to the next byte level. We'd have to shave off 6 bits at this point to get from 30 bits down to 24 bits. Remember that you can always use those 2 slack bits for something else, primarily a command identifier maybe? You could pass up to 4 commands using the remaining 2 bits, which might in your scenario stand for dealt, discard, view, or maybe the command identifies the player the card is going to to implement view logic. Players 1 through 4. With that in mind, since we have a use for these 2 bits already maybe shaving off another 1 or 2 bits might be something we should look at?
If you think you have an algorithm in mind for packing 5 cards in less than 30 bits go ahead and post it for all to share.