A Javascript/HTML5 tutorial – Part 1 building a basic Text Adventure

Introduction

I come from the age when text adventures were serious fun, ZORK and Hitchhikers guide to the galaxy were two of my favourite text adventures. OK so maybe the golden age of the text adventure is still stuck in the 1980’s but I still remember those glory days. So the game I am building is certainly not up there in the same territory as ZORK but I hope you can see the sort of cool things you can do with Js/Jquery and HTML 5.

The game is pretty simple use the N,S,E and W to move around the dungeon and a few other useful commands. Search for the Dragon and if you have the right weapon and equipment you may be able to kill it. If you use  a mobile browser you should see some buttons that make life a little easier than using a little virtual keyboard.

Here is the game : http://domssite.com/search/

Who is this for

Anyone interested in learning Javascript, jQuery and HTML 5.

How to use the tutorial

Its not an ordinary tutorial so you need to accept that first. Its based on reading and understanding code and code comments. I have used heaps of comments throughout the code to explain whats going on. There’s a lot of Arrays, so you’ll see lots of code using arrays and its not a bad starting place for working with js structures. The whole tutorial is 1 single file – whilst a single large file is not the best way to build large web apps – its not a bad approach showing  everything in one file.

The first part of the puzzle : the HTML

<!--Text Adventure : Search for the Dragon -->
<!--*****************************************
    Text Adventure - A JS/JQuery/HTML5 Tutorial by comments

    The game  : Standard Text Aventure where you can move from room to room, collecting items and killing monsters
                Goal is to get to the final room of the dungeon and kill the dragon
                Start in room 1
                Monsters dont move
                Monsters can be killed by certain weapons
                Should work in both mobile and non-mobile browsers

    History :   0.1 Initial version before Hilary's comments/advice
    ***************************************** -->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Search</title>
    <script src="Scripts/jquery-1.7.1.min.js"></script>
    <link href="Content/Site.css" rel="stylesheet" />
</head>
<body>
    <div class="content">
        <style>
</style>
        <div id="screenContainer">
            <div id="display">
                <div id="input">
                    <form>
                        <!--Screen Title-->
                        <h1>Search...
                        </h1>
                        <!--Screen Sub-Title-->
                        <h3>For a Dragon...Text Adventure Game by @DungeonBard
                        </h3>
                        <div id="GameOverDiv">
                        </div>
                        <div id="GameDiv">
                            <!--The following div is hidden or shown depending on if its a non-mobile browser-->
                            <div id="Keyboard">
                                <label>Command:</label>
                                <input type="text" name="userInput" class="buttonMod" id="userInput" />
                            </div>
                            <!--The following div is hidden or shown depending on if its a mobile browser-->
                            <div id="controllers">
                                <!--We use a table for the simple alignment of the buttons-->
                                <table>
                                    <tr>
                                        <td></td>
                                        <td>
                                            <input type='button' class='button' value='N' title="North" style="left: 200px" /></td>
                                        <td></td>
                                    </tr>
                                    <tr>
                                        <td>
                                            <input type='button' class='button' value='W' title="West" /></td>
                                        <td></td>
                                        <td>
                                            <input type='button' class='button' value='E' title="East" /></td>
                                    </tr>
                                    <tr>
                                        <td></td>
                                        <td>
                                            <input type='button' class='button' value='S' title="South" /></td>
                                        <td></td>
                                    </tr>
                                    <tr>
                                        <td></td>
                                        <td></td>
                                        <td></td>
                                    </tr>
                                    <tr>
                                        <td>
                                            <input type='button' class='button' value='P' title="Pickup" /></td>
                                        <td>
                                            <input type='button' class='button' value='A' title="About" /></td>
                                        <td>
                                            <input type='button' class='button' value='?' title="Help" /></td>
                                    </tr>
                                </table>
                            </div>
                            </div>
                            <div id="output"></div>
                    </form>
                    </div>
                </div>
            </div>
</body>
</html>

Part 2 : the Javascript

<script type="text/javascript">
    //Our main js code called by Jquery on doc ready
    $(document).ready(function () {
        //game variables    
        var message,                //screen message to display
        hits = 10,                  //hit points for the player
        lightLevel = 100,           //current light level
        currentRoom = 0,            //initial room  
        exitRoom = 31,              //final room of the dungeon
        IsGameOver = false;         //Maintain the state of the game
        IsOgreAlive = true,         //Stores the state of the Ogre - Alive/Dead
        IsDragonAlive = true;       //this is the gameover state

        //All the commands we use in the game
        var gameWords = new Array("HELP", "Find/earch", "N-orth", "S-outh", "W-est", "E-east','A-About");

        //All the rooms in the game
        var rooms = new Array("Dungeon Entrance", "Corridor of uncertainty", 'Ancient old cavern', "Great Cavern", "Underground River", "Stream", 'Dungeon Stream', "Dungeon Pool",
                              "Large Cavern", "Rough Tunnell", "Long Tunnell", "Dark Room", "Dark Room", "Cold Room", "Old Tunnel", "Cold Room",
                              "Old Cavern", "Short Corridor", "Short Corridor", "Grey Room", "Green Room", "Old Prison Cell", "Underground River",
                              "Large Cavern", "Rough Tunnell", "Long Tunnell", "Dark Room", "Dark Room", "Cold Room", "Old Tunnel", "Dragons Room");

        //Each exit relates to the index ie. Exits[0] SE which means rooms[0] the long path has two exits on the  South and East. If we look
        //down to the //Movement Code section you can see how we work out which rooms are connected to which
        var exits = new Array("E", "SWE", "WE", "SWE", "WE", "WE", "SWE", "WS",
                              "NSE", "SE", "WE", "NW", "SE", "W", "SNE", "NSW",
                              "NS", "NS", "SE", "WE", "NWE", "SWE", "WS", "N",
                              "N", "NWE", "NWE", "WE", "WE", "NW", "NE", "W");

        //All out game objects
        var GameObjects = new Array('', "Painting", "Knife", "Wand of Firebolts", "Goblet", "Wand of Wind", "Coins", "Helmet", "Candle", "Torch", "Iron Shield", "Armour", "Oil", "AXE", "ROPE", "BOAT", "AEROSOL", "CANDLE", "KEY");

        //Inventory array Contains all the things you can carry
        var inventory = new Array();
        inventory[0] = 2; //lets start our player off with a knife

        //location of game objects - these objects relate to a array index - so Object[1] the Painting is located
        //in rooms[2] the small garden - 999 indicates out of play 
        var objectLocations = [999, 1, 999, 3, 4, 5, 6, 7, 8, 10, 11, 15, 14, 12, 18, 19, 16, 17, 9]

        //This function detects if the browser if a mobile - you'll see when we call this we apply the 
        function isMobile() {
            return navigator.userAgent.match(/Android/i) || navigator.userAgent.match(/BlackBerry/i) || navigator.userAgent.match(/iPhone|iPad|iPod/i)
                    || navigator.userAgent.match(/Opera Mini/i) || navigator.userAgent.match(/IEMobile/i);
        }

        //The next line checks for a mobile browser and if it find st it will hide the buttons or hide the text box
        if (isMobile()) {
            //hide the text box - we dont need that for a mobile browser as its hard to use mobile keyboard for lots of commands
            $("#Keyboard").hide();
        } else {
            //hide the buttons as we don't want that for the normal web experience
            $('#controllers').hide();

            //jquery command to force the textbox to take focus  
            $("#userInput").focus();
        }

        //javascript function to pickup the object in this room
        var pickup = function (roomIndex) {
            var itemIndex;
            if (objectLocations[roomIndex] > 0 && objectLocations[roomIndex] < 100) {
                itemIndex = objectLocations[roomIndex];
                inventory[inventory.length] = itemIndex;
                objectLocations[roomIndex] = 999;
                alert(objectLocations[roomIndex]);
            }
        }
        //This function  loops through the object location array and returns
        function getObjectForRoom(currentRoom) {
            var roomIndex = -1;
            for (var i = 0; i < objectLocations.length ; i++) {
                if (objectLocations[i] == currentRoom)
                    roomIndex = i;
            }
            return roomIndex
        }

        //This is a method/function that shows the game screen. If we look in deatil at this function we can see that 
        //it uses another function DisplayText to show each line of the screen.
        function DisplayGameScreen() {

            //clear the output div
            $display.empty();

            //Display the screen output text - note this does not include the buttons
            DisplayText("You are now in the :");
            DisplayText(rooms[currentRoom]);
            DisplayText("Exits: " + ShowAdjacentRooms(exits[currentRoom]) + "<br />");
            DisplayText('DB:' + currentRoom + 'Light:' + lightLevel + "Hits:" + hits);
            if (getObjectForRoom(currentRoom) != -1) {
                var index = getObjectForRoom(currentRoom);
                DisplayText("You can see " + GameObjects[index]);
            }

            //If there is something in our inventory then display it
            if (inventory.length > 0) {
                DisplayText("You are carrying: ");
                for (var i = 0; i < inventory.length ; i++) {
                    DisplayText("-" + GameObjects[inventory[i]]);
                }
            }

            if (message != null)
                DisplayText(message.toUpperCase());

            //Game over code
            if (IsDragonAlive) {
                $('#GameOverDiv').hide();
                $('#GameDiv').show();
            }
            else {
                $('#GameOverDiv').show();
                $('#GameDiv').hide();
            }
            message = "What?";
        }

        //Replaces the indexOf js function as i have found it doesn't always work for me!!!!!!!!
        function checkIndex(issFullArray, issToCheck) {
            for (i = 0; i < issFullArray.length; i++) {
                if (issFullArray[i] == issToCheck) {
                    return true;
                }
            }
            return false;
        }

        //Uses the text for a room to build a string that shows which rooms are next to the current room
        function ShowAdjacentRooms(e) {
            var newExits = "";
            if (e != null) {
                for (i = 0; i < e.length; i++) {
                    if (i === e.length - 1) {
                        newExits += e.substring(i, i + 1);
                    } else if (i === e.length - 2) {
                        newExits += e.substring(i, i + 1) + " & ";
                    } else {
                        newExits += e.substring(i, i + 1) + ", ";
                    }
                }
            }
            return newExits;
        }

        //Simple js function to display a line of text
        function DisplayText(text) {
            $display.html($display.html().toString() + text + "<br>");
        }

        //Each round we call this function to do all the main game processing 
        function ProcessGameRound(command) {

            //Remove any spaces from the command text
            trimCommand = $.trim(command);

            //Process command takes the players action
            ProcessCommand(command);

            //NOw that we have taken the players logic we need to activate the main game room logic
            if (currentRoom == 10 && OgreAlive) {
                //if you are fighting the ogre and you have the spells
                if (checkIndex(inventory, 3)) {
                    message += "\<br\>YOU attack the ogre with magic spells and kill him!";
                    OgreAlive = false;
                }
                else {
                    message += "\<br\>Ogre attacks you!";
                    hits--;
                }
            }

            //If you are in the final room and the dragon is still alive
            if (currentRoom == 31 && IsDragonAlive) {
                //if you are fighting the dragon and you have the oil, burning torch
                if (checkIndex(inventory, 5) && checkIndex(inventory, 9) && checkIndex(inventory, 12)) {
                    message += "\<br\>You attack the dragon with oil, burning torch and the wand of Wind - It creates and kill him!";
                    IsDragonAlive = false; //End Game           
                }
                else {
                    message += "\<br\>The dragon attacks you with firebreath and kills you!";
                    hits = 0;
                }
            }

            if (currentRoom == 25) {
                //if you are fighting the gas room burning torch
                if (checkIndex(inventory, 10)) {
                    message += "\<br\>The gas in the room is ignited by the torch - You become a human BBQ and die!";
                    hits = 0;
                }
            }
            DisplayGameScreen();
        }

        function ProcessCommand(command) {
            var direction = command;
            message = "OK";
            switch (command) {
                //Movement Code
                case "N":
                    if (exits[currentRoom].indexOf(direction) > -1)
                        currentRoom -= 8;
                    else
                        message = "Can't move there";
                    break;
                case "S":
                    if (exits[currentRoom].indexOf(direction) > -1)
                        currentRoom += 8;
                    else
                        message = "Can't move there";
                    break;
                case "E":
                    if (exits[currentRoom].indexOf(direction) > -1)
                        currentRoom++;
                    else
                        message = "Can't move there";
                    break;
                case "W":
                    if (exits[currentRoom].indexOf(direction) > -1)
                        currentRoom--;
                    else
                        message = "Can't move there";
                    break;
                    //End of Movement Code
                case "P":
                    pickup(currentRoom);
                    break
                case "A":
                    if (exits[currentRoom].indexOf(direction) > -1)
                        message = "About ... Game built for #1GAM, LD48 (failed) and my friend Hilary";
                    break
                case "?":
                    message = "The following commands are valid: N S E W P A ?";
                    break
            }
        }
        //JQuery selector that handles the form submit - 
        $('#input form').submit(function (evt) {
            ProcessGameRound($('#userInput').val().toUpperCase());

            $('#userInput').val('');
            evt.preventDefault();
        });

        //sets the output div to the display variable
        $display = $('#output');

        // This is jQuery selector that picks up an event from the button - in this case we look at the value of the button ie. its text and use that 
        //to call the same function as we would call from the equivalent keyboard command
        $(".button").click(function (e) {
            switch (this.value) {
                case "N":
                    ProcessGameRound('N');
                    break;
                case "S":
                    ProcessGameRound('S');
                    break;
                case "E":
                    ProcessGameRound('E');
                    break;
                case "W":
                    ProcessGameRound('W');
                    break;
                case "F":
                    ProcessGameRound('F');
                    break;
                case "P":
                    pickup(currentRoom);
                    break;
                case "A":
                    ProcessGameRound('A');
                    break;
            }
        });

        DisplayGameScreen();

    });
</script>

Are the bugs

Yep – Totally , lots that the point – we’ll start cleaning them up in the next version
Next

I’ll add in a series of improvements in version 2 of this game.

Source Control link

https://github.com/ozidom/search

Building the Really Really Really Simple RogueLike v0.1 with C#

Really Really Really Simple Roguelike V0.1

Want to build a Roguelike with C# in 5 minutes? The following is a really quick game I built in about 2 hours and shows how easy it is to get a simple game going.

Its a very very very simple game – all you do is pickup a sword before the monster gets to you and you win. Obviously, if the monster gets to you first – well you can work out the ending.

What do you need?

C# on Visual Studio 2005/2008 or 2010. Any version should be fine as long as you can create a console application.

Now we are going to add the following into 1 file in Visual Studio rather than using multiple files and whilst this is not the best practice for a serious application,
we are doing this to keep things really simple, and to display the entire application on a few pages of paper.

Here we go

1.Start Visual Studio 2010

2.File->New Project

3.Select the Windows->Console Application

4.Name the Project ReallyReallyRealySimpleRogueLike and click OK

5.Right click on References->Add Reference…

6.Click on Assemblies then Framework and select System.Drawing

7.At the top of Program.cs insert the following line  using System.Drawing;

8.Replace the program class with the following

 class ReallyReallyReallySimpleRogueLike
    {
        static void Main(string[] args)
        {
            Dungeon dungeon = new Dungeon(Constants.DungeonWidth, Constants.DungeonHeight);
            string displayText = Constants.IntroductionText;

            while (dungeon.IsGameActive)
            {
                dungeon.DrawToConsole();
                Console.WriteLine(displayText);
                Console.Write(Constants.CursorImage);
                displayText = dungeon.ExecuteCommand(Console.ReadKey());
            }

            Console.WriteLine(ConcludeGame(dungeon));
            Console.ReadLine();
        }

        private static string ConcludeGame(Dungeon dungeon)
        {
            return (dungeon.player.Hits > 0) ? Constants.PlayerWinsText : Constants.MonsterWinsText;
        }
    }

9.Now add the following class

 class Dungeon
    {
        Random r;
        public Player player;
        List monsters;
        List swords;
        List walls;

        public Tile[,] Tiles;
        private int xMax;
        private int yMax;
        public enum Direction
        {
            North,
            South,
            East,
            West
        }

        public bool IsGameActive
        {
            get
            {
                return (player.Hits > 0 && monsters.Any(m => m.Hits > 0));
            }
        }

        public Dungeon(int xMax, int yMax)
        {
            monsters = new List();
            walls = new List();
            swords = new List();

            this.xMax = xMax;
            this.yMax = yMax;
            Tiles = new Tile[xMax, yMax];
            BuildRandomDungeon();
            SetDungeonTiles();
        }

        public string ExecuteCommand(ConsoleKeyInfo command)
        {
            string commandResult = ProcessCommand(command);
            ProcessMonsters();
            SetDungeonTiles();

            return commandResult;
        }

        private void ProcessMonsters()
        {
            if (monsters != null && monsters.Count > 0)
            {
                monsters.Where(m => m.Hits >= 0).ToList().ForEach(m =>
                {
                    MoveMonsterToPlayer(m);
                });
            }
        }

        private void BuildRandomDungeon()
        {
            r = new Random();
            SetAllDungeonSquaresToTiles();

            for (int i = 0; i < xMax; i++)
            {
                Wall top = new Wall(i, 0);
                walls.Add(top);
                Wall bottom = new Wall(i, yMax - 1);
                walls.Add(bottom);
            }

            for (int i = 0; i < yMax; i++)
            {
                Wall left = new Wall(0, i);
                walls.Add(left);
                Wall right = new Wall(xMax - 1, i);
                walls.Add(right);
            }

            for (int i = 0; i < Constants.NumberOfSwords; i++)
            {
                Sword s = new Sword(GetValidRandomPoint());
                swords.Add(s);
            }

            for (int i = 0; i  0 && monster.X  player.X) ? -1 : 1;

            if ((monster.Y > 0 && monster.Y  player.Y) ? -1 : 1;

            if (!IsInvalidValidMove(move.X, move.Y))
            {
                monster.X = move.X;
                monster.Y = move.Y;
            }

            if (monster.X == player.X && monster.Y == player.Y)
                ResolveCombat(monster);
        }

        private void ResolveCombat(Monster monster)
        {
            if (player.Inventory.Any())
                monster.Die();
            else
                player.Die();
        }

        public string ProcessCommand(ConsoleKeyInfo command)
        {

            string output = string.Empty;
            switch(command.Key)
            {
                case ConsoleKey.UpArrow:
                case ConsoleKey.DownArrow:
                case ConsoleKey.RightArrow:
                case ConsoleKey.LeftArrow:
                    output = GetNewLocation(command, new Point(player.X, player.Y));
                    break;
                case ConsoleKey.F1:
                    output = Constants.NoHelpText;
                    break;
            }

            return output;
        }

        private string GetNewLocation(ConsoleKeyInfo command, Point move)
        {
            switch (command.Key)
            {
                case ConsoleKey.UpArrow:
                    move.Y -= 1;
                    break;
                case ConsoleKey.DownArrow:
                    move.Y += 1;
                    break;
                case ConsoleKey.RightArrow:
                    move.X += 1;
                    break;
                case ConsoleKey.LeftArrow:
                    move.X -= 1;
                    break;
            }

            if (!IsInvalidValidMove(move.X, move.Y))
            {
                player.X = move.X;
                player.Y = move.Y;
                if (Tiles[move.X, move.Y] is Sword && player.Inventory.Count == 0)
                {
                    Sword sword = (Sword)Tiles[move.X, move.Y];
                    player.Inventory.Add(sword);
                    swords.Remove(sword);
                }
                return Constants.OKCommandText;
            }
            else
                return Constants.InvalidMoveText;
        }

        public bool IsInvalidValidMove(int x, int y)
        {
            return (x == 0 || x == Constants.DungeonWidth - 1 || y == Constants.DungeonHeight - 1 || y == 0);
        }

        public void SetDungeonTiles()
        {
            //Draw the empty dungeon
            SetAllDungeonSquaresToTiles();

            SetAllDungeonObjectsToTiles();
        }

        private void SetAllDungeonObjectsToTiles()
        {
            //Now draw each of the parts of the dungeon
            walls.ForEach(w => Tiles[w.X, w.Y] = w);
            swords.ForEach(s => Tiles[s.X, s.Y] = s);
            monsters.ForEach(m => Tiles[m.X, m.Y] = m);
            Tiles[player.X, player.Y] = player;
        }

        private void SetAllDungeonSquaresToTiles()
        {
            for (int i = 0; i < yMax; i++)
            {
                for (int j = 0; j < xMax; j++)
                {
                    Tiles[j, i] = new Tile(i, j);
                }
            }
        }

        public void DrawToConsole()
        {
            Console.Clear();
            for (int i = 0; i < yMax; i++)
            {
                for (int j = 0; j < xMax; j++)
                {
                    Console.ForegroundColor = Tiles[j, i].Color;
                    Console.Write(Tiles[j, i].ImageCharacter);
                }
                Console.WriteLine();
            }
        }
    }

10.Now underneath add the following code for the Tile,Wall and Sword classes

public class Tile
    {
        public string name { get; set; }
        public string ImageCharacter { get; set; }
        public ConsoleColor Color { get; set; }
        public int X { get; set; }
        public int Y { get; set; }

        public Tile() { }

        public Tile(int x, int y)
            : base()
        {
            this.X = x;
            this.Y = y;
            ImageCharacter = Constants.TileImage;
            Color = Constants.TileColor;
        }
    }

    public class Wall : Tile
    {
        public Wall(int x, int y)
            : base(x, y)
        {
            ImageCharacter = Constants.WallImage;
            this.Color = Constants.WallColor;
        }
    }

    public class Sword : Tile
    {
        public Sword(Point p)
        {
            ImageCharacter = Constants.SwordImage;
            this.Color = Constants.SwordColor;
            X = p.X;
            Y = p.Y;
        }
    }

11.Now add the classes you need for the various creatures

 public class Creature : Tile
    {
        public int Hits { get; set; }

        public void Die()
        {
            Hits = 0;
        }
    }

    public class Player : Creature
    {
        public Player(Point p)
        {
            ImageCharacter = Constants.PlayerImage;
            Color = Constants.PlayerColor;
            Inventory = new List();
            X = p.X;
            Y = p.Y;
            Hits = Constants.StartingHitPoints;
        }

        public List Inventory { get; set; }
    }

    public class Monster : Creature
    {
        public Monster(Point p)
        {
            ImageCharacter = Constants.MonsterImage;
            Color = Constants.MonsterColor;
            X = p.X;
            Y = p.Y;
            Hits = Constants.StartingHitPoints;
        }
    }

12.Now add the following class for all our constants

 public static class Constants
    {
        public readonly static int DungeonHeight = 20;
        public readonly static int DungeonWidth = 20;
        public readonly static int NumberOfSwords = 5;
        public readonly static int MonsterDamage = 2;
        public readonly static int NumberOfMonsters = 1;
        public readonly static int StartingHitPoints = 10;

        public readonly static string TileImage = ".";
        public readonly static string WallImage = "#";
        public readonly static string PlayerImage = "@";
        public readonly static string SwordImage = "s";
        public readonly static string StepsImage = "S";
        public readonly static string MonsterImage = "M";
        public readonly static string CursorImage = ">";

        public readonly static ConsoleColor MonsterColor = ConsoleColor.Blue;
        public readonly static ConsoleColor PlayerColor = ConsoleColor.Gray;
        public readonly static ConsoleColor WallColor = ConsoleColor.DarkCyan;
        public readonly static ConsoleColor SwordColor = ConsoleColor.Yellow;
        public readonly static ConsoleColor TileColor = ConsoleColor.White;

        public readonly static string InvalidCommandText = "That is not a valid command";
        public readonly static string OKCommandText = "OK";
        public readonly static string InvalidMoveText = "That is not a valid move";
        public readonly static string IntroductionText = "Welcome to the dungeon - grab a sword kill the monster(s) win the game";
        public readonly static string PlayerWinsText = "Player kills monster and wins";
        public readonly static string MonsterWinsText = "Monster kills player and wins";
        public readonly static string NoHelpText = "No help text";
    }

13. Compile and Run this and you should see something like:

ReallyReallyReallySimpleRougueLike image

Screenshot of ReallyReallyReallySimpleRougueLike

Next Time
Next time I will present the first iteration of the program and we’ll see some nice improvements.

Tutorial: Building a simple CRUD application using ASP.NET Webforms, Code First EF 4 and SQL CE 4

Introduction

The following shows how easy it is to create simple web sites using the new Code First Entity Framework, SQL Compact Edition and Web Forms. It must be said that a number of parts of this solution are still beta, so expect the unexpected.

I won’t go into either Code First Entity Framework or SQL Compact Edition but check out Scott Guthrie’s(see references) articles as they are fabulous on both topics.

I am using Webforms initially because I find it a very quick building simple sites using ASP.NET webform controls.

The site I am starting to build in this article is a sporting website for social teams. We are only going to see the very simple aspects of the Person management, but I will add more features in future articles as I switch to MVC.

I’ll add the source code link up soon.

What you will get out of this

  1. An understanding of how easy it is to use Code First Entity Framework;
  2. How easy it is to use the integrated SQL Server
  3. How easy CRUD facility can be built with existing Webform controls

You will need:

  1. Visual Studio 2010 Professional or higher (C#)
  2. Microsoft ADO.NET Entity Framework Feature CTP4  http://www.microsoft.com/downloads/en/details.aspx?FamilyID=4e094902-aeff-4ee2-a12d-5881d4b0dd3e&displaylang=en and
  3. Microsoft SQL Server CE 4.0 CTP http://www.microsoft.com/downloads/en/details.aspx?FamilyID=0d2357ea-324f-46fd-88fc-7364c80e4fdb&displaylang=en

I will hopefully host this solution soon and will post its link when it is up. So here we go.

Building the Application:

  • Start Visual Studio and create a new ASP.NET Web Application project called TeamWebSite
  • After the following line:

<asp:Content ID=”BodyContent” runat=”server” ContentPlaceHolderID=”MainContent”>

  • Add the following:
    <h2>
       Demo Team Site
    </h2>
    <p>
        <asp:ObjectDataSource ID="dsPlayer" runat="server" SelectMethod="GetAllPlayers"
        TypeName="TeamWebSite.Data.Player" DeleteMethod="DeletePlayer"
            UpdateMethod="EditPlayer">
            <DeleteParameters>
                <asp:Parameter Name="PlayerID" Type="Int32" />
            </DeleteParameters>
            <UpdateParameters>
                <asp:Parameter Name="PlayerID" Type="Int32" />
                <asp:Parameter Name="PlayerName" Type="String" />
                <asp:Parameter Name="PlayerDescription" Type="String" />
                <asp:Parameter Name="PlayerAge" Type="Int32" />
            </UpdateParameters>
        </asp:ObjectDataSource>
        <asp:GridView ID="gvPlayer" runat="server" AllowPaging="True"
        DataSourceID="dsPlayer" AutoGenerateColumns="False" DataKeyNames="PlayerID">
            <Columns>
                <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
                <asp:BoundField DataField="PlayerID" HeaderText="PlayerID"
                    SortExpression="PlayerID" />
                <asp:BoundField DataField="PlayerName" HeaderText="PlayerName"
                    SortExpression="PlayerName" />
                <asp:BoundField DataField="PlayerDescription" HeaderText="PlayerDescription"
                    SortExpression="PlayerDescription" />
                <asp:BoundField DataField="PlayerAge" HeaderText="PlayerAge"
                    SortExpression="PlayerAge" />
            </Columns>
        </asp:GridView>
        <asp:Button ID="AddPerson" runat="server" Text="Add"
        onclick="AddPerson_Click" />
    </p>

Ok so this is pretty simple, just two main controls. First of all the datasource. This is the object datasource for the page and all we specify is the type (or class) it is using for this datasource and then specifies the Type, the methods (edit and delete) and the parameters. The other control is the good old Gridview and as you can see we just ensure it specifies  AutoPaging, AutogenerateColumns and the datakey (we need this so that the delete command can use the PlayerID to delete).

  • Replace the following code behind of Default.aspx.cs with:
using System;

namespace TeamWebSite
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void AddPerson_Click(object sender, EventArgs e)
        {
            Response.Redirect("AddPerson.aspx");
        }
    }
}

Pretty simple. Here we are just adding a click event handler to move to a new page  and now let’s add the only other page we need.

  • Add to the project a new item: web form using master page  called AddPerson.aspx and select the master page: Site.master
  • Add the following between the <content> </content> tags:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AddPerson.aspx.cs" Inherits="TeamWebSite.AddPerson" MasterPageFile="~/Site.Master" %>
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <div>
        <asp:ObjectDataSource ID="dsPerson" runat="server" InsertMethod="CreatePlayer"
            SelectMethod="GetAllPlayers" TypeName="TeamWebSite.Data.Player">
        </asp:ObjectDataSource>
        <asp:FormView ID="fvPerson" runat="server" DataSourceID="dsPerson"
            DefaultMode="Insert" oniteminserted="fvPerson_ItemInserted" >
            <InsertItemTemplate>
                Name:
                <br />
                <asp:TextBox ID="PlayerNameTextBox" runat="server"
                    Text='<%# Bind("PlayerName") %>' />
                <asp:RequiredFieldValidator ID="rfPlayerName" runat="server" ControlToValidate="PlayerNameTextBox" ErrorMessage="Enter Name"></asp:RequiredFieldValidator>
                <br />
                Description:
                <br />
                <asp:TextBox ID="PlayerDescriptionTextBox" runat="server"
                    Text='<%# Bind("PlayerDescription") %>' />
                <br />
                Age:
                <br />
                <asp:TextBox ID="PlayerAgeTextBox" runat="server"
                    Text='<%# Bind("PlayerAge") %>' />
                <br />
                <asp:RequiredFieldValidator ID="rfPlayerAge" runat="server" ErrorMessage="Enter Age" ControlToValidate="PlayerAgeTextBox" ></asp:RequiredFieldValidator>
                <asp:LinkButton ID="InsertButton" runat="server" CausesValidation="True"
                    CommandName="Insert" Text="Insert" />
                &nbsp;<asp:LinkButton ID="InsertCancelButton" runat="server"
                    CausesValidation="False" CommandName="Cancel" Text="Cancel" />
            </InsertItemTemplate>
        </asp:FormView>
        <asp:ValidationSummary ID="vsPerson" runat="server" />
    </div>
</asp:Content>

You should see that again, there are two main controls on the page:

1.Object Datasource – again we are just pointing to the type and specifying the insert command.

2.FormView – Here we specify the datasource above and then create the markup with in the InsertItem template that allows us to create the container for all the controls we will use on the AddPerson.aspx page (see figure 2)

  • Now just add the following as code behind
using System.Web.UI.WebControls;

namespace TeamWebSite
{
    public partial class AddPerson : System.Web.UI.Page
    {

        protected void fvPerson_ItemInserted(object sender, FormViewInsertedEventArgs e)
        {
            Response.Redirect("Default.aspx");
        }
    }
}

So nothing major here just a redirect back to the default when we have inserted the row.

Now we can add the class holding our crud goodliness:

  • Add a class to the Project and call it TeamSite.cs

 

  • Add the following references to the project: C:Program Files (x86)Microsoft ADO.NET Entity Framework Feature CTP4BinariesMicrosoft.Data.Entity.CTP.dll and C:Program FilesMicrosoft SQL Server Compact Editionv4.0DesktopSystem.Data.SqlServerCe.dll
  • Replace the code with:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Data.Entity.Infrastructure;

namespace TeamWebSite.Data
{
    public class TeamWebSite : DbContext
    {
        //EF Container 
        public DbSet<Player> players
        {
            get;
            set;
        }
    }

    //Standard old POCO class
    public class Player
    {
        public Player() { }

        public int PlayerID { getset; }

        public string PlayerName { getset; }
        public string PlayerDescription { getset; }
        public int PlayerAge { getset; }

        //Standard Select
        public List<Player> GetAllPlayers()
        {
            TeamWebSite db = new TeamWebSite();
            return db.players.ToList<Player>();
        }

        //Insert
        public void CreatePlayer(string PlayerName, string PlayerDescription, int PlayerAge)
        {
            TeamWebSite db = new TeamWebSite();
            db.players.Add(new Player { PlayerName = PlayerName, PlayerDescription = PlayerDescription, PlayerAge = PlayerAge });
            db.SaveChanges();
        }

        //Edit
        public void EditPlayer(int PlayerID,string PlayerName, string PlayerDescription, int PlayerAge)
        {
            TeamWebSite db = new TeamWebSite();
            Player p = db.players.SingleOrDefault(player => player.PlayerID == PlayerID);
            if (p != null)
            {
                p.PlayerName = PlayerName;
                p.PlayerAge = PlayerAge;
                p.PlayerID = PlayerID;
                db.SaveChanges();
            }
            else
            {
                throw new ApplicationException("Can not find the player");
            }
        }

        //Delete
        public void DeletePlayer(int PlayerID)
        {
            TeamWebSite db = new TeamWebSite();
            Player p = db.players.SingleOrDefault(player => player.PlayerID == PlayerID);
            if (p != null)
            {
                db.players.Remove(p);
                db.SaveChanges();
            }
            else
            {
                throw new ApplicationException("Can not find the player");
            }
        }
    }

    //Called by Global.asax to reload the database in a situation when the model changes from 
    //the physical DB
    public class WebSiteInitializer :RecreateDatabaseIfModelChanges<TeamWebSite>
    {
        protected override void Seed(TeamWebSite context)
        {
            var Players = new List<Player>
            {
                new Player { PlayerID= 1, PlayerName="John", PlayerAge=31, PlayerDescription="older player" },
                new Player { PlayerID=2, PlayerName="sTEVE" , PlayerAge=23, PlayerDescription="asdasd" }
            };
            Players.ForEach(d => context.players.Add(d));
        }
    }
}

OK so it might look like a lot but break it down and it’s not very complicated at all. There are 3 classes (Yes we should have them in separate files but to keep things all in one page for brevity I am being very very bad). The three classes:

TeamWebSite – inherits from DBContext (part of the EF framework), publishes our main property: public DbSet<Player> players.

Person – standard basic old POCO class. Nothing really special.

WebSiteInitializer – class that we hook to from global.asax to make sure we keep the EF datamodel synced with the database.

  • Add the connection string to web.config  by adding the following within the <connectionstrings> tags:
 <add name="TeamWebSite" connectionString="Data Source=C:tempTeamWebSite.sdf" providerName="System.Data.SqlServerCe.4.0"/>

Make sure the directory exists for the datasource!!!

One last thing – one of the realities of code first is that you need to be able to maintain the link between the datamodel and the database at all times. When this synchronisation is broken you’ll get errors. Our workaround for this is to go back to a base set of data – for prototypying scenarios this should be fine.

  • Edit the global.asax and add the following using:

 

using System.Data.Entity.Infrastructure;

 

  • replace Application_Start with:
 void Application_Start(object sender, EventArgs e)
        {
            // Code that runs on application startup
            Database.SetInitializer<TeamWebSite.Data.TeamWebSite>(new TeamWebSite.Data.WebSiteInitializer());
        }

Having a look at the application :

 

  • Now start up the application (F5) and you should see:


  • Click Add you’ll see:

  • Saving a record and you’ll see:

Figure 3. The list of players after we have added a player

In Conclusion:

So this was pretty simple but I think you get the idea that you have quickly built, a simple, self contained CRUD web application, using a range of new tools.

I’ll continue in this series with an improved version of the tool built in MVC 3.0

Stay tuned

Dom

References:

http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx

http://weblogs.asp.net/scottgu/archive/2010/06/30/new-embedded-database-support-with-asp-net.aspx

CannonAttack iPhone v1.0 – An iPhoneTutorial

Which way to the battle? –  The Introduction

This tutorial is a short introduction to iPhone development. Like most of my tutorials it’s based on a game.

This is the second in a series of tutorials. The first tutorial was a TDD tutorial in C# 4.0.

This version of the tutorial is similar in functionality to the C# version with obvious changes to the UI as needed. I am not using a TDD approach, mainly because I am not really up to TDD on the iPhone yet.

By completing this tutorial you will:

  • Get a taste of the iPhone development experience; and
  • Design a simple UI in Interface Builder.

CannonAttack iPhone is a simple game where a player enters an angle and velocity of a cannonball to hit a target at a given distance. The game uses a basic formula for calculating the trajectory of the cannonball and the player keeps taking turns at shooting at the target until it has been hit

Where are the cannonballs – What you need:

I built this with the following tools:

  • iPhone SDK (3.1); and
  • MAC OS X 10.6 Snow Leopard

Whilst this is a pretty basic introduction to iPhone development, it assumes a very basic knowledge of iPhone development and while it doesn’t spell out every single step, you should have more than enough detail to complete it.

The battleplan – The CannonAttack Requirements/Specs:

The following is a combination of Requirements and Specifications that will give us some guide in terms of the application we are trying to build:

  • iPhone Application;
  • Allow player to set Angle and Speed of the Cannon Ball to Shoot at  a Target;
  • Target Distance is simply the distance of the Cannon to Target, and is created randomly by default but can be overridden;
  • Max distance for target is 20000 meters;
  • Base the algorithm for the calculation of the cannons trajectory upon the following code (distance and height is meters and velocity is meters per second):

distance = velocity * Math.Cos(angleInRadians) * time;

height = (velocity * Math.Sin(angleInRadians) * time) – (GRAVITY * Math.Pow(time, 2)) / 2;

  • A hit occurs if the cannon is within 50m of the target;
  • Display number of shots for a hit

Building the Cannon – Creating the Cannon UI

  • Start XCode and Create a View based application – call it CannonAttack
  • Now add a class to the Project and call it Cannon.m (generate the .h file as well).
  • Double click on the file CannonAttackViewController.xib:  (this will open up Interface Builder).
  • Create the following UI using the design tools in IB (base this on this screen):


  • Also, Add a button that covers the whole screen in the iPhone UI in Interface Builder. Click on this button and from the LAYOUT Menu select SEND TO BACK. This is important as we will use this hidden button to remove the keyboard for the textfields when we click in the background or one of the buttons.
  • Also make sure you have set the keyboard type for the textboxes to number pad.
  • You need to then go back to XCode and add the following Code to the .h file.
  • Now add the following to the .m file

#import <UIKit/UIKit.h>
@interface CannonLauncher1ViewController : UIViewController {
IBOutlet UILabel *descriptionLabel;
IBOutlet UILabel *resultLabel;
IBOutlet UITextField *speedTextField;
IBOutlet UITextField *angleTextField;
IBOutlet UILabel *shotCountLabel;
int targetDistance;
int shotCount;
}
//Properties
@property (nonatomic,retain) UILabel *descriptionLabel;
@property (nonatomic,retain) UILabel *resultLabel;
@property (nonatomic,retain) UITextField *speedTextField;
@property (nonatomic,retain) UITextField *angleTextField;
@property (nonatomic,retain) UILabel *shotCountLabel;
//Actions
-(IBAction)fireButton:(id)sender;
-(IBAction)resetButton:(id)sender;
-(IBAction)backgroundClick:(id)sender;
//Methods
-(void)hideNumberKeyboard;
-(void)resetTextBoxes;
@end

So now add the following code to the .m file

#import "CannonLauncher1ViewController.h"
#import "Cannon.h"
@implementation CannonLauncher1ViewController
@synthesize descriptionLabel;
@synthesize resultLabel;
@synthesize speedTextField;
@synthesize angleTextField;
@synthesize shotCountLabel;
- (void)viewDidLoad {
[super viewDidLoad];
[self resetTextBoxes];
}
//Button handler for the fire button
-(IBAction) fireButton:(id) sender
{
NSInteger velocity = [[speedTextField text]intValue];
NSInteger angle = [[angleTextField text]intValue];
Cannon *cannon = [[Cannon alloc]init];
cannon.targetDistance = targetDistance;
[cannon Shoot:angle Speed:velocity];
shotCount ++;
resultLabel.text = cannon.message;
shotCountLabel.text =
[NSString stringWithFormat: @"Shot Count %d", shotCount];
[self hideNumberKeyboard];
[cannon release];
}
//Button handler for the reset button
-(IBAction) resetButton:(id) sender
{
[self resetTextBoxes];
}
//Button Handler for the main backgtound button
-(IBAction)backgroundClick:(id)sender
{
[self hideNumberKeyboard];
}
//Hides the number keyboard
-(void)hideNumberKeyboard
{
[speedTextField resignFirstResponder];
[angleTextField resignFirstResponder];
}
//Reset the distance and set it as rge text of the description label
-(void)resetTextBoxes
{
targetDistance = rand() %10000;
NSString *distanceText = [[NSString alloc] initWithFormat: @"Distance %d meters (50m",targetDistance];
descriptionLabel.text = distanceText;
speedTextField.text = @"";
angleTextField.text = @"";
shotCount = 0;
[distanceText release];
}
//Built in Method I didn't change this
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
//Built in Method to clean up any objects
- (void)dealloc {
[descriptionLabel release];
[resultLabel release];
[speedTextField release];
[angleTextField release];
[shotCountLabel  release];
[super dealloc];
}
@end

OK so all we have to do now is implement the cannon class.

Now edit the cannon.h file and replace the code with this header code:

//
//  Cannon.h
//  CannonLauncher1
//
//  Created by Dom Millar on 16/06/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
#import <Foundation/Foundation.h>
@interface Cannon : NSObject {
@private int targetDistance;
NSString *message;
}
@property  (readwrite,assign)int targetDistance;
@property  (nonatomic,retain) NSString *message
-(IBAction) Shoot: (int) angle Speed: (int) speed;
@end

Now all we have to do is add the code for cannon.m

//
//  Cannon.m
//  CannonLauncher1
//
//  Created by Dom Millar on 16/06/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import "Cannon.h"
@implementation Cannon
@synthesize targetDistance;
@synthesize message;
-(IBAction) Shoot: (NSInteger) angle Speed: (NSInteger) velocity
{
//set up the local variables for the shoot method
float time = 0;
float height = 0;
float distance = 0;
float gravity = 9.80665;
float variation = 50;
float pi = 3.1415926536;
//Keep calculating height and distance untill height < 0
while(height >= 0)
{
float angleInRadians = ( pi / 180 ) * angle;
distance = velocity * cos(angleInRadians) * time;
height = (velocity * sin(angleInRadians)) - (gravity * pow(time,2));
time++;
}
//Complete calculation
if ((targetDistance >= distance-variation) && (targetDistance <= distance+variation))
{
//Display the hit
message = @"Hit";
}
else
{
//Display the Miss
NSString *description = [NSString stringWithFormat:@"Missed target landed at %f meters" , distance];
message = description;
//[description release];
}
}
@end

Now make sure everything builds. Go back interface builder and you need to do setup the following outlets and actions:

This shows you all the hooks we need:

File owner dialog screen shot

OK now You should be ready to run, go back to XCode and run the app and you should be able to play the game like:

You can see the Keyboard is visible here – once we click on a button or the main screen the keyboard will disappear:

Victory Condition – In summary

So that’s it – our latest version of the Cannon Attack game is complete. There are lots of changes you can make to make the game more interesting so play around with the project as much as you like.

Happy Coding – Dom.