Ben's Video Poker

An attempt to clone the video poker machines found in casinos. Try it out here.

This is a little video poker project I created in my spare time. The goal was to make a server-run python virtual deck of cards which clients could talk to. I decided to have the server return a JSON object with the current hand, the client could then query the server with their discard and retrieve the draw. It would also be the server's responsibility to determine whether the hand won.

Later on, I plan to have the server handle bets and winnings.

I've also written an example client for it in HTML/Javascript/JQuery/Ajax. You can try it out here or make a direct call to the back end here.

Server

Poker.py - Server side logic for creating/shuffling a deck of cards, and determining whether the hand is a win.

Poker.py [download]
#!/usr/bin/python

import random 
import json
import cgi

print "Content-type: text/html"
print

form = cgi.FieldStorage()

suits = ['H', 'D', 'S', 'C']
deck = [] 
hand = []
draw = False
seed = 0 

# create the deck
for suit in suits:
	for cval in range(2,15):
		deck.append([suit, cval])

# get or generate the seed
if "s" in form:
	seed = int(form.getvalue("s"))
else: 
	seed = random.randint(0,99999999999)

# seed the generator
random.seed(seed)

# shuffle the deck 
random.shuffle(deck)

# deal the hand
for i in range(5):
	hand.append(deck.pop())

skeep = ""
keep = []
if "d" in form:
	draw = True 
	skeep = form.getvalue("d")

	# deal draw 
	i = 0
	for ccard in hand:
		if skeep[i] == "O":
			hand[i] = deck.pop()
		i += 1

class Result:
        aPair = False
        firstPair = False
        secondPair = False
        threeKind = False
        fourKind = False
        fullHouse = False
        jacksOrHigher = False
        straight = False
        flush = False
        wintext = ""


nums = []
suits = []
result = Result()


for card in hand:
        nums.append(card[1])
        suits.append(card[0])

for s in ["H","D","S","C"]:
        if suits.count(s) == 5:
                result.flush = True


list.sort(nums)
for i in range(2,15): 
        if nums.count(i) > 1: 
                result.aPair = True
        if nums.count(i) == 4:
                result.fourKind = True
        if nums.count(i) == 3:
                result.threeKind = True
                if result.firstPair:
                        result.fullHouse = True
        if nums.count(i) == 2:
                if result.threeKind:
                        result.fullHouse = True
                if result.firstPair:
                        result.secondPair = True
                result.firstPair = True
                if i > 10: 
                        result.jacksOrHigher = True

if not result.aPair:
        if max(nums) - min(nums) == 4:  
                result.straight = True

result.wintext = "No Win"

if result.jacksOrHigher: 
        result.wintext = "Jacks or Better"

if result.secondPair:
        result.wintext = "Two Pair"

if result.threeKind:
        result.wintext = "Three of a Kind"

if result.straight:
        result.wintext = "Straight"

if result.flush:
        result.wintext = "Flush"

if result.fullHouse:
        result.wintext = "Full House"

if result.fourKind:
        result.wintext = "Four of a Kind"

if result.straight and result.flush:
        result.wintext = "Straight Flush"

# output json
#    hand, draw, result
print json.dumps([hand, draw, result.wintext, seed])

Client

Javascript for dealing cards.

Deal.js [download]
var time = 300; 
var keep = [true, true, true, true, true];
var seed;
var manseed = false; 

function hideHelds() { 
	$(".heldcard").each(function(i, item)  { 
		$(item).css("visibility","hidden");	
	});
}

function dealCard(i, card) { 
	console.log("dealing card... " ) ; 
	console.log(card); 	
	$("#card" + i).removeClass("back").html(getCard(card[0], card[1])); 
	try {
		document.getElementById("audio").currentTime = 0;
		document.getElementById("audio").play();
	} catch(err) { console.log("something went wrong with the audio."); }
}

function toggleCard(v) { 
	var index = v.currentTarget.id.replace("card", "")-1;
	keep[index] = !keep[index];
	$("#heldcard"+(index+1)).css("visibility",(keep[index]?"hidden":"visible"));
}

function draw() { 
	var skeep = "";
	for (i=0;i<5;i++) { 
		skeep += keep[i]?"O":"X";
	}
	deal(skeep);
}

function deal(skeep) { 
	var url = "/cgi-bin/poker.py";
	if(skeep==null) { 
		keep = [true, true, true, true, true];
		toggleLinks(false);
		hideHelds(); 
		if(manseed) { 
			url += "?s=" + $("#manseed").val(); 
			manseed = false; 
		}
	} else { 
		toggleLinks(true);
		console.log("draw. holding: " + skeep);
		url += "?s="+seed+"&d="+skeep;
	}
	console.log("retrieving hand: " + url);
	$.getJSON(url, function(data) {
		var hand = data[0];
		var result = data[2];
		var draw = data[1];
		var wait = 1; 
		seed = data[3]; 
		$("#seed").html(seed); 
		console.log(hand);
		$.each(hand, function(i, card) {
			if(keep[i] || (!draw)) { 
				setTimeout("dealCard("+(i+1)+", [\"" + card[0] + "\", " + card[1] + "])", wait * time);
				$("#card"+(i+1)).empty().addClass("back");
				wait++; 
			}
		});
		console.log("Result: " + result);
		if((!draw)&&result=="No Win") { 
			$("#wintext").html(""); 
		} else { 
			setTimeout("$(\"#wintext\").html(\""+result+"\")", 1300);
		}
	});
}

function toggleLinks(dealVis) { 
	$("#drawLink").toggle(!dealVis);
	$("#dealLink").toggle(dealVis);
}

Very basic HTML layout for the page.

index.html [download]
<html>
<head>
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript"></script>
	<script>
		if (typeof console == "undefined" || typeof console.log == "undefined") var console = { log: function() {} }; 
	</script>
	<link rel="stylesheet" type="text/css" href="css/card.css" />
	<script type="text/javascript" src="js/card.js"></script>
	<script type="text/javascript" src="js/deal.js"></script>
	<style>
		body { 
			background-color: #0009a0; 
		}
	</style>
	<script language="javascript">
		$(document).ready(function() { 
			$(".card").bind("click", toggleCard);
			$("body").keypress(key); 
			deal(); 
		});     

		function key(e) { 
			console.log(e); 
		}
	</script>
</head>
<body>
	<div style="padding: 10px; background-color: #FFF; border: solid 1px #000;">
		Developed by Ben Friedland. View a write-up on the code for this <a href="/samples/poker.php">here</a>.
	</div>
	<div id="heldcard1" class="heldcard">HELD</div>
	<div id="heldcard2" class="heldcard">HELD</div>
	<div id="heldcard3" class="heldcard">HELD</div>
	<div id="heldcard4" class="heldcard">HELD</div>
	<div id="heldcard5" class="heldcard">HELD</div>
	<div class="clear"></div>
	<div id="card1" class="card back"></div>
	<div id="card2" class="card back"></div>
	<div id="card3" class="card back"></div>
	<div id="card4" class="card back"></div>
	<div id="card5" class="card back"></div>
	<input type="button" id="dealLink" onclick="deal()" value="deal">
	<input type="button" id="drawLink" onclick="draw()" value="draw">
	<div class="clear"></div>
	<div id="wintext" class="wintext"></div>
	<audio src="audio/card-flip.mp3" id="audio" preload="auto"></audio>
	<script language="javascript">
		function replay() { 
			$("#manseed").val($("#seed").html()); 
			manseed = true; 
			deal(); 
		}
	</script>
	<div id="debug" style="border: solid 3px black; background-color: #FFC; padding: 20px;">
		<b>Debug Area:</b><br />
		Seed used to generate this hand: <span id="seed" style="font-weight: bold;"></span> 
		[<a href="#" onclick="replay()">replay this hand</a>] <br />
		Play a new hand based on the seed: <input type="text" id="manseed">
		<input type="button" onclick="manseed=true; deal();" value="play"> <br />
	</div>
</body>
</html>

Javascript and CSS for drawing out a playing card.

Card.js [download]
function getCard(suit, val) { 
	var red = (suit=="H" || suit=="D"); 
	var cval = val;
	var csuit; 
	var royal = false; 
	switch (suit) { 
		case "H":
			csuit = "&hearts;" 
			break;
		case "D":
			csuit = "&diams;"
			break;
		case "S":
			csuit = "&spades;" 
			break;
		case "C":
			csuit = "&clubs;" 
			break;
	}
 	switch (val) { 
		case 11: 
			cval = "J"; 
			royal = true; 
			break; 
		case 12:
			cval = "Q"; 
			royal = true; 
			break; 
		case 13: 
			cval = "K"; 
			royal = true; 
			break; 
		case 14: 
			cval = "A"; 
			royal = true; 
			break; 
	}

 	var ret = "<table border=\"0\" width=\"100%\" class=\"" + (red?"red":"") + "\">" +  
	        "	<tr>" + 
	        "		<td class=\"card-corner\" align=\"center\">" + 
		"			<div class=\"corner-val\">" + cval + "</div>" + 
                "                       <div class=\"corner-suit\">" + csuit + "</div>" + 
                "                </td>" + 
                "                <td align=\"right\">" + 
                "                        " + (royal?"<img src=\"images/royal/" + cval + ".png\">":"") + 
                "                </td>" + 
                "        </tr>" + 
                "        <tr>" + 
                "                <td colspan=\"2\" align=\"center\" class=\"big-suit\">" + 
                "                       " + csuit +  
                "                </td>" + 
                "        </tr>" + 
                "</table>"; 

	return ret; 
}
Card.css [download]
.heldcard { 
	width: 107px; 
	font-size: 22px; 
	text-align: center; 
	font-family: sans-serif; 
	font-weight: bold; 
	color: FFF; 
	margin-right: 10px; 
	float: left; 
	padding: 10px;
	visibility: hidden; 
}
.wintext { 
	width: 107px; 
	font-size: 22px; 
	text-align: center; 
	font-family: sans-serif; 
	font-weight: bold; 
	color: FFF; 
	padding: 10px;
}
.card {
	-moz-border-radius: 5px;
	border-radius: 5px;
	-moz-box-shadow: 3px 3px 0px #000;
	-webkit-box-shadow: 3px 3px 0px #000;
	box-shadow: 3px 3px 0px #000;
	width: 107px;
	height: 150px;
	background-color: #FFF;
	padding: 10px;
	font-size: 40px;
	float: left;
	margin-right: 10px;
	cursor: hand; 
}
.card table {
	font-size: 30px;
	font-weight: bold;
}
.card-corner {
	font-size: 30px;
	line-height: 20px;
	width: 20px; 
}
.corner-suit {
	padding-left: 0px;
	padding-top: 5px;
	font-size: 25px; 
}
.big-suit {
	font-size: 90px;
	font-weight: 900;
}
.back {
	background: url('../images/cardback.gif');
}
.red { color: red; } 
.clear { clear: both; }