JQuery AJAX and ASP.NET MVC Texas Hold’em.

by breeve 1. November 2009 12:13

I wrote this little app to show how easy it is to do Ajax calls with jQuery. It can be downloaded here. Below is the HTML that defines the tables the cards are a part of. There are three main div sections with id attributes of actions, dealer, and players. These divs define the main regions of the game above.

<div id="main">
     <input type="hidden" />
     <div id="actions">
        <table>
            <tr>
                <th colspan="4">Actions</th>
            </tr>
            <tr>
                <td><button>Deal</button></td>
                <td><button disabled="disabled">Flop</button></td>
                <td><button disabled="disabled">Turn</button></td>
                <td><button disabled="disabled">River</button></td>
            </tr>
        </table>
     </div>
     <div id="dealer">
        <table>
            <tr>
                <th colspan="3">Flop</th>
                <th>Turn</th>
                <th>River</th>
            </tr>
            <tr>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
            </tr>
        </table>
    </div>
    <div id="players">
        <table>
            <tr>
                <th colspan="2">Player 1</th>
            </tr>
            <tr>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
            </tr>
        </table>
        <table>
            <tr>
                <th colspan="2">Player 2</th>
            </tr>
            <tr>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
            </tr>
        </table>
        <table>
            <tr>
                <th colspan="2">Player 3</th>
            </tr>
            <tr>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
            </tr>
        </table>
    </div>
</div>

Server Side

My shuffling algorithm is very simple but let's pretend it is a highly secretive new shuffling technique that we want to keep a secret; so I do it server side. Listed below are the three services and a nested helper class called Game that keeps the state of the game. They are all members of the default HomeController ASP.NET MVC creates when you do a new MVC project.

The first service, the method called Deal, takes the number of players and returns the unique gameID—that the client will use to call the other two services—and the players initial dealt cards. The cards are integers from 1 to 52 that match the corresponding png card images. In the last line of all three services, I take advantage of the ability of ASP.NET MVC to take .NET objects and turn them into Json.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Deal(int playerCount)
{
    Game game = new Game();

    IDictionary<string, int[]> playerCards = game.Deal(playerCount);
    HttpContext.Cache[game.ID.ToString()] = game;

    return Json(new {gameID = game.ID, cards = playerCards});
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Flop(string gameID)
{
    Game game = HttpContext.Cache[gameID] as Game;
    if (game == null)
        throw new InvalidOperationException("can't find game");

    return Json(game.Flop());
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DrawCard(string gameID)
{
    Game game = HttpContext.Cache[gameID] as Game;
    if (game == null)
        throw new InvalidOperationException("can't find game");

    return Json(game.DrawCard());
}

private class Game
{
    private Guid _guid;
    private List<int> _cards;
    private Random _rand;

    public Game()
    {
        _rand = new Random((int)DateTime.Now.Ticks);
        _guid = Guid.NewGuid();
        _cards = new List<int>();
        for (int i = 1; i <= 52; i++)
            _cards.Add(i);
    }

    public Guid ID
    {
        get
        {
            return _guid;
        }
    }

    public int DrawCard()
    {
        int index = _rand.Next(0, _cards.Count - 1);
        int value = _cards[index];
        _cards.Remove(value);
        return value;
    }

    public IDictionary<string, int[]> Deal(int playerCount)
    {
        Dictionary<string, int[]> playerCards = new Dictionary<string, int[]>();
        for (int i = 0; i < playerCount; i++)
        {
            int[] cards = new int[] { DrawCard(), DrawCard() };
            playerCards.Add(i.ToString(), cards);
        }

        return playerCards;
    }

    public int[] Flop()
    {
        return new int[] { DrawCard(), DrawCard(), DrawCard() };
    }
}

Client Side

Listed below is the jQuery and JavaScript. The $().ready function is jQuery’s way of calling me to run code once the page is loaded. In that function I use jQuery's selectors to subscribe to the Deal, Flop, Turn, and River button click events.

The Deal button selector $("#actions button:eq(0)"), for example, says pick the first button under the element with an id=action. This will give me the button labeled “Deal”. I then hook up to the click event of the Deal button. Once the Deal button is clicked, I set all img elements in the page to the blank card to reset the game. Then I disable/enable other action buttons and set up the AJAX call. I set up the AJAX call by telling jQuery to do a HTTP post to the URL '/Home/Deal' and pass it the playerCount.

If there is no error on the server, jQuery will call the function set to the success property. In my case, if there is no error on the server, I set the gameID to a hidden element in the page so other service calls can use it and then go through the returned cards setting the images.

function SetPlayersCards(selector, cards){
    for (i = 0; i < cards.length; i++) {
        var imgPath = "/Images/" + cards[i] + ".png";
        selector.eq(i).attr("src", imgPath);
    }
}

$().ready(function(){

    //deal button
    $("#actions button:eq(0)").click(function(){
        $("img").attr("src", "/Images/b1fv.png");
        $("#actions button:gt(1)").attr("disabled", "disabled");
        $("#actions button:eq(1)").removeAttr("disabled");
        $.ajax({
            type: "Post",
            url: '/Home/Deal',
            data: {playerCount:3},
            dataType: "json",
            success: function(data) {
                $("#main :hidden").val(data.gameID);
                //player 1
                SetPlayersCards($("#players img:lt(2)"), data.cards[0]);
                //player 2
                SetPlayersCards($("#players img:gt(1):lt(2)"), data.cards[1]);
                //player 3
                SetPlayersCards($("#players img:gt(3)"), data.cards[2]);
            }
        });
    });
   
    //flop button
    $("#actions button:eq(1)").click(function(){
        $("#actions button:eq(1)").attr("disabled", "disabled");
        $("#actions button:eq(2)").removeAttr("disabled");
        var gameID = $("#main :hidden").val();
        $.ajax({
            type: "Post",
            url: '/Home/Flop',
            data: {gameID:gameID},
            dataType: "json",
            success: function(data) {
                var flopCards = $("#dealer img:lt(3)");
                for(i = 0; i < data.length; i++){
                     var imgPath = "/Images/" + data[i] + ".png";
                     flopCards.eq(i).attr("src", imgPath);
                }
            }
        });
    });
   
    //turn button
    $("#actions button:eq(2)").click(function(){
        $("#actions button:eq(2)").attr("disabled", "disabled");
        $("#actions button:eq(3)").removeAttr("disabled");
        var gameID = $("#main :hidden").val();
        $.ajax({
            type: "Post",
            url: '/Home/DrawCard',
            data: {gameID:gameID},
            dataType: "json",
            success: function(data) {
                var imgPath = "/Images/" + data + ".png";
                $("#dealer img:eq(3)").attr("src", imgPath);
            }
        });
    });
   
    //river button
    $("#actions button:eq(3)").click(function(){
        $("#actions button:eq(3)").attr("disabled", "disabled");
        var gameID = $("#main :hidden").val();
        $.ajax({
            type: "Post",
            url: '/Home/DrawCard',
            data: {gameID:gameID},
            dataType: "json",
            success: function(data) {
                var imgPath = "/Images/" + data + ".png";
                $("#dealer img:eq(4)").hide().attr("src", imgPath).fadeIn(1000);
            }
        });
    });

});

Using fiddler, I can see what is sent on the wire when the Deal button is pressed:

/****Request****/

POST /Home/Deal HTTP/1.1
Host: localhost:49220
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7 (.NET CLR 3.5.30729)
Accept: application/json, text/javascript, */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://localhost:49220/
Content-Length: 13
Cookie: filterTags=
Pragma: no-cache
Cache-Control: no-cache

playerCount=3

/****Response****/

HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Sun, 01 Nov 2009 16:58:39 GMT
X-AspNet-Version: 2.0.50727
X-AspNetMvc-Version: 1.0
Cache-Control: private
Content-Type: application/json; charset=utf-8
Content-Length: 94
Connection: Close

{"gameID":"2ce44c42-20b8-4819-b796-3f07c305fc9e","cards":{"0":[47,32],"1":[18,9],"2":[16,41]}}

By looking at the Json in the response, I can verify that the gameID is a GUID and the cards for all three players are there. The other two services send the data in different Json formats but the same pattern is there.

Tags:

Software

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen

About Me

I am a Principal Engineer with 13 years experience developing and releasing software products. I started developing in C/C++ then moved into .NET and C# and have tech lead multiple projects. I have developed products in Windows Forms, ASP.NET/MVC, Silverlight, and WPF. I currently reside in Austin, Texas.

Own Projects

Pickaxe - An easy to use web page scraper. If you know a little SQL and how basic CSS selectors work, no web scraping product will be easier to use.

Download Page

Source Code

Created ASP.NET MVC forum originally targeting home owner associations but now in use by an investor group.

http://vtss.brockreeve.com

A language for querying PGATour golf strokes.

http://pga.brockreeve.com/

Real time bidder for car ads demo

http://cargawk.com/

Simple front end tic tac toe

http://tictac.brockreeve.com/

Currently Reading