Last Good Quote: Son's are the seasoning on our lives. - Someone on Facebook

Showing posts with label code. Show all posts
Showing posts with label code. Show all posts

Saturday, April 26

T-SQL - What performance benefits can views give me?

I won't cover it all, but read this and stop wondering:

http://stackoverflow.com/questions/439056/is-a-view-faster-than-a-simple-query

(Remember you gotta use them correctly, and just cause you got a hammer don't make all problems into nails)

Some highlights:

After a unique clustered index is created on the view, the view's result set is materialized immediately and persisted in physical storage in the database, saving the overhead of performing this costly operation at execution time.

these indexed views can work even when they are not directly referenced by another query as the optimizer will use them in place of a table reference when appropriate

View itself would provide us with a performance advantage (3x) over the simple use of the index alone
Using indexes to improve query performance is not a new concept; however, indexed views provide additional performance benefits that cannot be achieved using standard indexes.
This is true for MS Sql Server 2005 and beyond.

LogiAnalytics - 5 Steps to Masking Your Connection String

Hi All,

I was recently asked if there was a way to mask your connection string in Logi Studio. I thought I'd jot down some notes.


My thinking was why not put the connection string in a DLL. That will make it a bit harder for folks to find. Not exactly 128 bit encryption but each step helps. So that means I need a plugin that populates the connection string element in the Settings.xml file BEFORE the Logi report is processed.

This should be pretty straight forward.

First: There is an excellent plugin example already available on Dev-Net. But it reads from a text file which is still not masked. (Take a look)

Step 1: Fire up Visual Studio and create a new Class Library project

Step 2: Add a reference to Logi's DLL.

  • Project Properties
  • References
  • Add
  • Browse
  • In your web root folder is a "bin" folder, in there is a file called rdPlugin.dll, add that
  • Click the just added reference and in the Properties of it, set Copy Local to false. (This makes sure we don't overwrite the existing dll file when we deploy, this is imporant, do not skip)
I then used this code, it's fairly straight forward, if that doesn't makes sense to you, you probably shouldn't be going this route.

BTW: Do both of us a favor and check that your connection string is correct by putting it in a normal connection element in the Settings file.
Imports System.Xml
Imports System.Web
Imports System.IO
Public Class LogiPlugin
    Public Sub setConnectionString(ByRef rdObjects As rdPlugin.rdServerObjects)
        Dim sConnID As String = rdObjects.PluginParameters("ConnID")
        Dim xmlSettings As New XmlDocument()
        xmlSettings.LoadXml(rdObjects.CurrentDefinition)
        'locate your connection element from it's ID (this is passed in from the variable)
        Dim eleConn As XmlElement = xmlSettings.SelectSingleNode("//*[@ID='" & sConnID & "']")
        Dim sConnection As String = "Server=OneWickedMachine;Database=NorthWind;User Id=sa;Password=ucannothave;"
        eleConn.SetAttribute("ConnectionString", sConnection)
        rdObjects.CurrentDefinition = xmlSettings.OuterXml
    End Sub

End Class

Step 3: Deploy to the file

There are multiple ways to do this, the most stright forward...but not so formal approach

  • Build your project.
  • Browse to the Debug/Bin folder of your VS project
  • Copy the SetConnection.dll to the wwwroot/YourSite/bin folder

Step 4: Call Plugin in Studio

Open up Logi Studio and alter the Settings file

Add a Connection Element, but place "NA" in all the required attributes.

Add a Plugin element

  • Assembly Name: ..\bin\SetConnection.dll (referencial path, so you can deploy on Dev, QA and Prod
  • Class Type Name: SetConnection.LogiPlugin.LogiPlugin
  • Event: Load Definition
  • Method: setConnectionString
Add a Plugin Parameter call and ConnID = NorthWind (or your database)

Step 5: Test it!

You should be good to go from this point.

Suggestion: If you have multiple environments (Dev/QA/Prod), I would add an additional parameter for which connection string to use. This can be done within the vb file and should be pretty straight forward.

Thursday, April 3

Autocomplete

I think I already wrote this post, but just in case. Here is how I implimented Autocomplete with jQuery:

You will need jquery, I had a copy in my templates, you can find a copy at jquery.com

You will also need autocomplete.js, which I got from here: https://github.com/devbridge/jQuery-Autocomplete/tree/master/scripts

I put the following with my css (in the head of the document)

.autocomplete-suggestions {
border: 1px solid #333;
font-size: 12px;
font-family: arial;
padding: 3px;
background:rgba(93,167,245,0.2);
}
.autocomplete-suggestion
{
padding:3px;
}

I then put the following where I wanted the text box to appear in the HTML code:

Stock: <input type="text" name="txtSymbol" id="txtSymbol">

I then put the following in my document (toward the bottom)
<script>

$(document).ready(function(){
    var options, a;
jQuery(function(){
//options = { serviceUrl:'qryStocks.php' };
var a = $('#txtSymbol').autocomplete({
serviceUrl:'qryStocks.php',
minChars:2,
maxHeight:400,
width:300,
zIndex: 9999,
deferRequestBy: 250, //miliseconds
noCache: false, //default is false, set to true to disable caching
onSelect: function(value, data){ alert('You selected: ' + value.value + ', ' + value.id); }
});

});

});

</script>

Now we also need a file that will return the list to display in our ajax:

<?php
$time_start = microtime(true); 

$conn=new mysqli("localhost","root","ididgood","stock_tracker");

$searchString = $_GET["query"];

$sql = "select distinct symbol, symbol from stock_history where symbol like '$searchString%' order by symbol limit 10";

$result = $conn->query($sql);

$out = "{\"query\":\"$searchString\",";
$suggestions = "";
$data = "";
$i=0;
while ($row = $result->fetch_object()) 
{
if($i> 0) $suggestions.= ",";
if($i> 0) $data.= ",";
$suggestions.= "\"$row->symbol\"";
$data.= "\"$row->symbol\"";
$i++;
}

$out.="\"suggestions\":[$suggestions],\"data\":[$data]}";

print $out;
?>

That's it.

Saturday, March 22

JS Starfield

The following will create a flowing field of stars on a black background. Used the HTML5 Canvas element to create this effect:

<script>

var canvas = "";
var context = "";
var stars = new Array();



function loadStarfield()
{
canvas = document.getElementById("canvas_starfield");
context = canvas.getContext("2d");

addNewStars(100);

//stars[stars.length-1].starSize = 50;
//stars[stars.length-1].drawColor = "#333";
//stars[stars.length-1].position.x = -10;
//stars[stars.length-1].position.y = 150;
//stars[stars.length-1].velocity.x = 1;


starfieldLoop();

}


function starfieldLoop()
{
context.fillStyle = "black";
context.fillRect(0, 0, canvas.width, canvas.height);

for(var i=0;i<stars.length;i++)
{
drawStar(stars[i]);
stars[i].animate();
}

window.requestAnimationFrame(starfieldLoop);

}

function drawStar(star)
{
  var position = star.position;
  context.fillStyle = star.drawColor;
  context.beginPath();
  context.arc(position.x, position.y, star.starSize, 0, Math.PI * 2);
  context.closePath();
  context.fill();
}




function addNewStars(numStars)
{
for (var i = 0; i < numStars; i++)
{
stars.push(new Star());
}
}

function Star() {
  this.position = new Vector(randBetween(0,canvas.width),randBetween(0,canvas.height)); // Vector
  this.velocity = new Vector(randBetween(1,5),0);
  var c = randBetween(0,9);
  this.drawColor = "#FFF"; //"#" + c + "" + c + "" + c; // So we can tell them apart from Fields later
  this.starSize = randBetween(1,1);
}

Star.prototype.animate = function()
{
this.position.add(this.velocity);

if(this.position.x > canvas.width)
{
this.position.x = 0;
}
}



//////////////////////////////////////////////////////////////////////////////////////////////
//
// Utils
//
//////////////////////////////////////////////////////////////////////////////////////////////
function randBetween(min, max)
{
return Math.floor(Math.random()*(max-min+1)+min);
}

function Vector(x, y) {
  this.x = x || 0;
  this.y = y || 0;
}

// Add a vector to another
Vector.prototype.add = function(vector) {
  this.x += vector.x;
  this.y += vector.y;
}

// Gets the length of the vector
Vector.prototype.getMagnitude = function () {
  return Math.sqrt(this.x * this.x + this.y * this.y);
};

// Gets the angle accounting for the quadrant we're in
Vector.prototype.getAngle = function () {
  return Math.atan2(this.y,this.x);
};

// Allows us to get a new vector from angle and magnitude
Vector.fromAngle = function (angle, magnitude) {
  return new Vector(magnitude * Math.cos(angle), magnitude * Math.sin(angle));
};

</script>

<canvas id="canvas_starfield" width="600" height="300"
style="
border:5px solid black;">
</canvas>
<script>
loadStarfield();
</script>



Saturday, November 16

Particle Emitters - Part III

And here are the actual effects themselves. It generates the actual effect:


function WhirlEmitter(point,color)
{
    this.position = point; // Vector
    this.velocity = Vector.fromAngle(0,.5); // Vector
    this.spread = Math.PI/1; // possible angles = velocity +/- spread
    this.drawColor = "#999"; // So we can tell them apart from Fields later
    this.particleColor = color;
    this.maxParticles = 100;
    this.particleSize = 1;
    this.numParticles = 0;
    this.emissionRate = 1;
    this.tether = rand(60,100);
}

WhirlEmitter.prototype.emit = function()
{
    var angle = this.velocity.getAngle() + this.spread - (Math.random() * this.spread * 2);
    var magnitude = this.velocity.getMagnitude();

    var velocity = new Vector(0,0);//Vector.fromAngle(angle, magnitude);
    var position = new Vector(this.position.x + velocity.x * 25, this.position.y + velocity.y * 25);
    var particle = new Particle(position,velocity,new Vector(0,0),this.particleColor,2);
    particle.action = "whirlParticle";
    particle.particleColor = this.particleColor;//'rgb(' + rand(0,255) + ','+ rand(0,255) + ','+ rand(0,255) + ')'
    particle.tether = rand(1,360)/100;
    particle.origin = new Vector(this.position.x, this.position.y);

    //this.position.x++;
    return particle;
};


function whirlParticle(particle)
{
    //alert(particle.life + ":" + particle.tether);
    var tmpX = particle.origin.x + particle.radius * Math.cos(particle.tether);
    var tmpY = particle.origin.y + particle.radius * Math.sin(particle.tether);
    particle.position = new Vector(tmpX, tmpY);
    particle.tether+=.25;
    particle.particleSize-=.01;
   
    if(particle.tether > 7)
    {
        particle.tether = .36;
        particle.radius-=5;
    }
   
    if(particle.radius < 0 || particle.particleSize <= 0)
    {
        return "";
    }

    return particle;
}

Saturday, November 9

Particle Emitters - Part II

Check out the Particle Emitters Series to get the full picture.

There are some basic functions that are needed for to make this work, they are as follows. We won't be modify this much except for the plotParticles function, where we have to change the if statements for each of our effects. (I know there must be a clever way to do this, but I ran outta time)


function Particle(point, velocity, acceleration,color,size) {
  this.position = point || new Vector(0, 0);
  this.velocity = velocity || new Vector(0, 0);
  this.acceleration = acceleration || new Vector(0, 0);
  this.origin = new Vector(0,0);
  this.color = color
  this.particleSize = size;
  this.action = "";
  this.life = 0;
  this.tether = 0;
  this.radius = 25;
}

function plotParticles(boundsX, boundsY)
{
    // a new array to hold particles within our bounds
    var currentParticles = [];

    for (var i = 0; i < particles.length; i++)
    {
        var particle = particles[i];

       
        var pos = particle.position;

               
        if(particle.action == "warpParticle")
        {
            particle = warpParticle(particle);
            particle.move();
        }

        if(particle.action == "whirlParticle")
        {
            particle = whirlParticle(particle);
        }
       
        if(particle.action == "beamParticle")
        {
            particle = beamParticle(particle);
        }
       
       
        if(particle != "")
        {
            // If we're out of bounds, drop this particle and move on to the next
            if (pos.x < 0 || pos.x > boundsX || pos.y < 0 || pos.y > boundsY) continue;
            if (particle.particleSize <= 0) continue;

           
            // Add this particle to the list of current particles
            currentParticles.push(particle);
        }
    }

    // Update our global particles, clearing room for old particles to be collected
    particles = currentParticles;
}



function rand(from,to)
{
    return Math.floor(Math.random()*(to-from+1)+from);
}


function drawParticles()
{
    // For each particle
    for (var i = 0; i < particles.length; i++)
    {
        context.fillStyle = particles[i].color;
        var position = particles[i].position;
        //context.fillRect(position.x, position.y, particles[i].particleSize, particles[i].particleSize);

        context.fillStyle = particles[i].particleColor;
        context.beginPath();
        context.arc(position.x, position.y, particles[i].particleSize, 0, Math.PI * 2);
        context.closePath();
        context.fill();

    }
}


function addNewParticles()
{
    for (var i = 0; i < emitters.length; i++)
    {
        if(emitters[i].numParticles < emitters[i].maxParticles)
        {
            for (var j = 0; j < emitters[i].emissionRate; j++)
            {
                particles.push(emitters[i].emit());
                emitters[i].numParticles++;
            }
        }

    }
}



Particle.prototype.move = function () {
  // Add our current acceleration to our current velocity
  this.velocity.add(this.acceleration);

  // Add our current velocity to our position
  this.position.add(this.velocity);
};



function Vector(x, y) {
  this.x = x || 0;
  this.y = y || 0;
}

// Add a vector to another
Vector.prototype.add = function(vector) {
  this.x += vector.x;
  this.y += vector.y;
}

// Gets the length of the vector
Vector.prototype.getMagnitude = function () {
  return Math.sqrt(this.x * this.x + this.y * this.y);
};

// Gets the angle accounting for the quadrant we're in
Vector.prototype.getAngle = function () {
  return Math.atan2(this.y,this.x);
};

// Allows us to get a new vector from angle and magnitude
Vector.fromAngle = function (angle, magnitude) {
  return new Vector(magnitude * Math.cos(angle), magnitude * Math.sin(angle));
};

Saturday, November 2

Particle Emitters - Part I

I've been playing around with Particle Emitters. Those are the cool little animations like star bursts and flowing bubbles that you see in games.

I used the following as a blank canvas for me to work with:

(notice you will need particleFunctions,js and particleEffects.js which are coming in part II and III)

<html>
  <head>
    <script language=javascript src="particleFunctions.js"></script>
    <script language=javascript src="particleEffects.js"></script>
  </head>
  <body>
    <canvas    style="width:300px; height:300px;border:1px solid black;background:black;" width=300 height=300></canvas>
<script>
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
var particles = [];
emitters = new Array();
emitters.push(new WhirlEmitter(new Vector(canvas.width/2+5,canvas.height/2),'green'));
emitters.push(new BeamEmitter(new Vector(canvas.width/2,canvas.height/2),'green'));
    
function loop() {
    clear();
    update();
    draw();
    queue();
}

function clear() {
  context.clearRect(0, 0, canvas.width, canvas.height);
}

function queue() {
  window.requestAnimationFrame(loop);
}

function update() {
  addNewParticles();
  plotParticles(canvas.width, canvas.height);
}

function draw() {
    drawParticles();
}

loop();
</script>

    </body>
</html>

Friday, June 3

Free Code

Just a quick post..I'm slowly posting my playground work to my "lab" site.

www.mobeamer.com/lab

Consider it all open source, please provide a link back to this blog or mobeamer.com if you use in a production site.

Monday, May 17

5 min Intro to PHP

If your interested in learning or playing with PHP. Here is a short introduction to get you started. I expect that you have some basic programming skills already. (or the will power to stay and play for a while)

Download It

If your using a window OS you will want an "XAMP" installation. It installs Apache, PHP and MySQL on windows. Most of them are simple: download, unzip and click the "start" executable. I like to use Uniform Server. I won't go into debugging apache in this article, but I will suggest that you shut off any program that uses the web server ports. (Visual Studio, IIS, Skype).

Alternatively you can play on any website you have, most will have php enabled.

Basic Things to know

  • Save your page with a .php extension
  • Surround your code with <?php ...code.... ?>
  • You can use <? ...code.... ?> but that is non-standards compliant
  • All statements in php must end with ";"
  • Variables start with $. IE: $username, $today, $yesterday, $i
  • Variables "live" to the end of page, but do not carry over into functions
  • Single line comments use //
  • Multi line comments use /* .... */
  • To add strings use "." (IE: $firstName . " " . $lastName)
  • To add numbers use "+" (IE: $age + 10)
  • Use print to print (IE: print "Hello " . $username)
  • Use echo to print (IE: echo "Hello " . $username)
If statements

if( boolean )
{
//...some code
}
elseif( boolean )
{
//...some code
}
else
{
//...other code
}

Loops

for($i=0;$i<10;$i++)
{
//...code
}

while( boolean )
{
// ...code
}

Variables, Sessions and Forms

Normal Variable: $username;
Session Variable: $_SESSION["username"]
Form Post Variable: $_POST["username"]
Form Get Variable: $_GET["username"]

Kill a session variable: $_SESSION["username"] = "";


Database

PHP normally uses mySQL, but each of the functions below have an equivalent function for most database systems.

$db_host = "localhost";
$db_user = "user";
$database = "dbname";
$db_passwd = "pass";
mysql_connect($db_host, $db_user, $db_passwd);
$q = mysql_query("select * from user");

while($row = mysql_fetch_object($q))
{
print $row->username;
}


Alternativly you can use mysql_fetch_row($q)...this will get an array of the field values and you can access them by $row[0], $row[1]....etc.

As with most high level languages, php has a number of functions available to it. You can search the very friendly documentation at www.php.net


Objects and Classes
Class DateUtils
{

function DateUtils()
{
//...constructor
}


function getToday($format)
{
return date($format);
}
}

And here is how to use the class

$util = new DateUtils();
print $utils->getToday();



What else would you like to know?

Monday, April 26

CSS: Tabbed Menu

Free Code. This comes directly from this site http://www.webcredible.co.uk/user-friendly-resources/css/css-round-corners.shtml



Creates rounded menus.

You will need the following four images:
  • - Right Tab
  • Left Tab

  • Right Tab Hover

  • Left Tab Hover



  • Code

    <html>

    <style>
    #navigation a
    {
    color: #000;
    background: #ffa20c url(left-tab.gif) left top no-repeat;
    text-decoration: none;
    padding-left: 10px
    }

    #navigation a span
    {
    background: url(right-tab.gif) right top no-repeat;
    padding-right: 10px
    }

    #navigation a, #navigation a span
    {
    display: block;
    float: left
    }

    /* Hide from IE5-Mac \*/
    #navigation a, #navigation a span
    {
    float: none
    }
    /* End hide */

    #navigation a:hover
    {
    color: #fff;
    background: #781351 url(left-tab-hover.gif) left top no-repeat;
    padding-left: 10px
    }

    #navigation a:hover span
    {
    background: url(right-tab-hover.gif) right top no-repeat;
    padding-right: 10px
    }

    #navigation ul
    {
    list-style: none;
    padding: 0;
    margin: 0
    }

    #navigation li
    {
    list-style: none;
    float: left;
    margin: 0;
    }
    </style>



    <body>
    <h1>Tabbed Menu Test</h1>

    <ul id="navigation">
    <li><a href="#"><span>Home</span></a></li>
    <li><a href="#"><span>Services</span></a></li>
    <li><a href="#"><span>Take a tour</span></a></li>
    <li><a href="#"><span>About us</span></a></li>
    <li><a href="#"><span>Contact us</span></a></li>
    </ul>

    </body>
    </html>

    Friday, April 23

    Javascript - 2D Map Library

    This article is a wrap up of a series of articles in which I walk through building a simple 2d top down map library. Below are the articles that build this library:
    For those of you that want the code, scroll past the explanations, it's at the bottom.

    Style.css.
    While testing and playing I usually will include the style in one page, for the ease of editing, but it's always a good idea to separate the style sheet from the main body of code. This allows an artist to come along and play with the look and feel of the site without screwing with your brilliantly created code.

    Map.js.
    All the general re-usable functions for mapping. If you ever finish coding a function and think, man I'll never have to touch this again, its so perfect and I'll use it all the time! First, slap yourself, cause we all know it's going to have a bug in it at some time. Second realize that the function in question, is prime material to go into a library of sorts. The functions in map.js are generic and can be kept from game to game as you build different things. At least that is my hope.

    TileClicked()
    I left tile clicked in the main body of the page because the actions you might want to take when a tile is clicked will change from game to game. Thus it is not library material.

    DestReached()
    This is a new function that was not discussed in other articles. Once a unit is done moving to it's destination of location, this function is called. I figured game builders might want to change this function depending on the game.

    Play()
    This is the heart of your game, if you want enemies to run around randomly, here is where you put that stuff. This function will call itself every few milliseconds, the more code in this function the slower your game is going to run.

    ObjectsCollided()
    This function is called when ever two units combine. The unit that moved last and did the collision is passed along with the id of each unit that is involved in the collision. You'll want to customize this function.


    The Body Tag
    The body tag calls the buildMap and addUnit function. I'd expect that you will want to change that.

    Div Elements
    There are two divs in the html one to hold the map and another to hold the score. You can move those around, but they must be somewhere in the page for the library to work.

    Map.js Function List

    BuildMap(width, height)
    Builds a map of width and height. It will also add walls to all edges of the map, so units don't wonder off it. Comment out the "addBorders()" line if you don't want this.

    AddBorders()
    Ummm, it adds borders...nuff said

    addWall(x,y)
    Turns tile x,y into a wall. Sets tile's flag = -1, sets the background color

    addUnit(unitID, x, y)
    Adds a unit with that ID to the map at tile location x, y. Multiple units can be added. If unitID is 1000 it is given the css class "unit". Otherwise the class is "enemy"

    setWalkableFlag(x, y, flag)
    Sets tile x, y "flag" variable to the flag passed.

    jumpUnit(unitID, tileDestX, tileDestY)
    Moves a unit directly to that tile, no animation at all...unit just jumps to location.

    move(unitID, dir)
    Move a unit one tile in the direction specified. Valid directions are N, E, S, W. This is an animated move (one pixel at a time). This uses the moveUnit() function.

    moveUnit(unitID, tileDestX, tileDestY)
    Moves a unit to the tile passed in. If at anytime the unit hits a none walkable tile, the unit will move back to last position. (no route finding). Calls DestReached() when completed.

    isWalkable(x,y)
    Returns "Y" or "N" if pixel x, y is walkable

    isWalkableTile(x,y)
    Returns "Y" or "N" if tile x, y is walkable

    getTop(obj)
    Return the top pixel (y) of the object. Could use some optimization

    getLeft(obj)
    Return the left pixel (x) of the object. Could use some optimization

    translateTileX(x), translateTileY(y)
    return the tileX that pixel x is located within

    getTileX(unitID), getTileY(unitID)
    return the tile x and tile y that the unit is located on

    Here be code!

    Style Sheet (style.css)


    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .unit {
    background:green;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .enemy {
    background:red;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .food {
    background:black;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }



    Map.js (the library)



    var originX = 0;
    var originY = 0;
    var tileSize = 16;
    var mapHeight = 0;
    var mapWidth =0;
    var score=0;
    var playSpeed = 1000;
    var numUnits = 0;
    var maxNumUnits = 40;


    function buildMap(width, height)
    {
    mapHeight = height;
    mapWidth = width;

    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("flag","0");
    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")
    document.getElementById("divMap").appendChild(d);
    }

    addBorders();



    }

    function addBorders()
    {
    for(x=0;x<mapWidth;x++)
    {
    addWall(x, 0);
    addWall(x, mapHeight-1);
    }


    for(y=0;y<mapHeight;y++)
    {
    addWall(0, y);
    addWall(mapWidth-1,y);
    }
    }

    function addWall(x,y)
    {
    var tile = document.getElementById("tile_" + x + "_" + y)
    tile.setAttribute("flag","-1");
    tile.style.background = "#777"
    }


    function addUnit(unitID, x,y)
    {
    var d = document.createElement("DIV");
    var tile = document.getElementById("tile_" + x + "_" + y)
    if(unitID == 1000)
    {
    d.className="unit";
    }
    else
    {
    d.className="enemy";
    }
    d.setAttribute("xVel", 0);
    d.setAttribute("yVel", 0);
    d.setAttribute("ID","unit_" + unitID);
    d.style.top = tile.style.top;
    d.style.left = tile.style.left;
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("currTileX",x);
    d.setAttribute("currTileY",y);
    d.setAttribute("priorTileX",x);
    d.setAttribute("priorTileY",y);

    setWalkableFlag(x, y, unitID);

    document.getElementById("divMap").appendChild(d);
    }

    function setWalkableFlag(x, y, flag)
    {
    var tmpTile = document.getElementById("tile_" + x + "_" + y)
    tmpTile.setAttribute("flag", flag);

    }

    function jumpUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    unit.style.left = getLeft(tile);
    unit.style.top = getTop(tile);
    //
    //mark the current tile as UNwalkable
    //
    setWalkableFlag(unit.getAttribute("currTileX"), unit.getAttribute("currTileY"), "0");

    //
    //mark the old tile as WALKABLE
    //
    setWalkableFlag(unit.getAttribute("priorTileX"), unit.getAttribute("priorTileY"), "0");

    unit.setAttribute("currTileX",x);
    unit.setAttribute("currTileY",y);
    unit.setAttribute("priorTileX",x);
    unit.setAttribute("priorTileY",y);

    }



    function move(unitID, dir)
    {
    x = getTileX(unitID)
    y = getTileY(unitID)

    if(dir == "N")
    {
    y--;
    }

    if(dir == "E")
    {
    x++;
    }

    if(dir == "S")
    {
    y++;
    }

    if(dir == "W")
    {
    x--;
    }


    if(isWalkableTile(x, y) == "Y")
    {
    moveUnit(unitID, x, y);
    }


    }


    function moveUnit(unitID, tileDestX, tileDestY)
    {

    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    unit.setAttribute("priorTileX",getTileX(unitID));
    unit.setAttribute("priorTileY",getTileY(unitID));

    if(!tile)
    {
    alert("no tile found");
    return;
    }
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile);
    destY = getTop(tile);
    newX = x;
    newY = y;

    if(x > destX) newX--;
    if(x < destX) newX++;
    if(y > destY) newY--;
    if(y < destY) newY++;

    if(isWalkable(newX, newY) == "Y" && isWalkable(newX+tileSize-2, newY + tileSize-2) == "Y")
    {
    unit.style.left = newX;
    unit.style.top = newY;

    if(newX != destX || newY != destY)
    {
    setTimeout("moveUnit(" + unitID + "," + tileDestX +"," + tileDestY+")",5);
    }
    else
    {
    destReached(unitID, destX, destY);
    }
    }

    }

    function isWalkable(x,y)
    {
    var tile = document.getElementById("tile_" + translateTileX(x) + "_" + translateTileY(y))
    if(tile.getAttribute("flag") >= 0) return "Y";


    return "N";
    }


    function isWalkableTile(x,y)
    {
    var tile = document.getElementById("tile_" + x + "_" + y)
    if(tile.getAttribute("flag") >= 0) return "Y";


    return "N";
    }

    function getTop(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.top.replace("px",""));
    }

    return 0;
    }

    function getLeft(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.left.replace("px",""));
    }

    return 0;
    }


    function translateTileX(x)
    {
    return Math.floor(x/tileSize - originX);
    }

    function translateTileY(y)
    {
    return Math.floor(y/tileSize - originY);
    }


    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    return translateTileY(getTop(unit));
    }

    function getTileX(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    return translateTileY(getLeft(unit));
    }

    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    x = Math.floor(getLeft(unit)/tileSize - originX);
    y = Math.floor(getTop(unit)/tileSize - originY);

    return y;
    }




    Basic HTML page (index.html)


    <html>
    <link type="text/css" href="style.css"rel="stylesheet" />
    <script language=javascript src="map.js"></script>

    <script language=javascript>
    function tileClicked(x,y)
    {
    //called whenever a tile is clicked
    moveUnit(1000, x, y);
    }

    function destReached(unitID, x, y)
    {
    //called when ever the unit is done moving to a spot

    }


    function play()
    {
    //once called, this will loop forever, checking the game state and other things
    setTimeout("play()",playSpeed);
    }


    function objectsCollided(unit, unitID, enemyID)
    {
    //called whenever two objects collide

    }

    </script>

    <body onload="buildMap(10,10); addUnit(1000,4,4);">

    <div id="divMap"></div>

    <div id="score" style="font-family:verdana;size:14px; background:green;position:absolute;top:5px;left:350px; width:100px height:15px;border:5px solid green;">
    Score: 0
    </div>


    </body>
    </html>

    Monday, April 5

    Code: Simple 2D Javascript Map - Part 6

    This code will create a map grid, when you click the grid the green box will move to the tile you clicked. Red boxes will "fall" from the top down at random intervals. If the green box hits a red box, the red box disappears and starts to fall again. Think of it as a catching game.

    This was built to demonstrate one form of collision detection.In my mind there are two ways you can do collision detection. The first method is what most systems and algorithms perform:
    • Calculate the new position of the unit
    • Cycle through all other units to see if one of them occupies the new position
    • If no one is occupying then move the unit

    The second method is to make tiles as walkable as a unit steps on the tile. Then rather then cycling through all the units, you just check the tile the unit is moving to.

    • Calculate the new position of the unit
    • Check to see if that tile is occupied
    • If no one is occupying then move the unit

    The second method means that it "cost" more to move a unit. However it cost less than cycling through all units, every time there is any movement on the screen.

    How did I implement? First, I had to edit the buildMap() function so that each tile has a flag for who is standing on it. So I added the following line inside my buildMap() loops:

    d.setAttribute("flag","0");


    I then needed a function to set and unset this flag. If you un comment out a few lines, it will change the background of the tile, so that you can see which tiles get marked as unwalkable.

    function setWalkableFlag(x, y, flag)
    {
    var tmpTile = document.getElementById("tile_" + x + "_" + y)
    tmpTile.setAttribute("flag", flag);

    /*
    if(flag > 0)
    {
    tmpTile.style.background = "yellow";
    }
    else
    {
    tmpTile.style.background = "#BBB";
    }
    */
    }


    I also need the unit to "remember" what tile they are on (currTile) and what tile they were on (priorTile). To do this, I add the following code to the addUnit() function:


    d.setAttribute("currTileX",x);
    d.setAttribute("currTileY",y);
    d.setAttribute("priorTileX",x);
    d.setAttribute("priorTileY",y);
    setWalkableFlag(x, y, unitID);


    Again, I needed a few "wrapper" functions. One translates a pixel point X to a tile X. To keep things clean I changed the getTile function to use the new translate function. Again, there is plenty of room to optimize.


    function translateTileX(x)
    {
    return Math.floor(x/tileSize - originX);
    }

    function translateTileY(y)
    {
    return Math.floor(y/tileSize - originY);
    }


    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    return translateTileY(getTop(unit));
    }

    function getTileX(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    return translateTileY(getLeft(unit));
    }


    Now we get to the meat and potatoes of coding. The move unit function is where all the collision detection takes place. Reading through the logic of this function reads as follows:
    1. Grab the unit object and the destination tile object
    2. If the destination tile does not exist, stop
    3. Move 1 pixel closer to the destination
    4. Get the tile located at the center of the new position. (Center of the unit)
    5. If that tile is not flagged or if it is flagged as this unit
    6. Move the unit to the new location
    7. Set the priorTile and currentTile values
    8. Flag the new current tile as walkable
    9. Flag the old prior tile as unwalkable
    10. If we still have not found our destination, repeat in 5 milliseconds
    11. If there is a collision (back at #5), call the collide function



    function moveUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    unit.setAttribute("priorTileX",getTileX(unitID));
    unit.setAttribute("priorTileY",getTileY(unitID));

    if(!tile)
    {
    return;
    }
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile);
    destY = getTop(tile);
    newX = x;
    newY = y;

    if(x > destX) newX--;
    if(x < destX) newX++; if(y > destY) newY--;
    if(y < destY) newY++; checkX = newX + tileSize/2; checkY = newY + tileSize/2; var newTile = document.getElementById("tile_" + translateTileX(checkX) + "_" + translateTileY(checkY)) if(newTile.getAttribute("flag") == "0" || newTile.getAttribute("flag") == unitID) { unit.style.left = newX; unit.style.top = newY; unit.setAttribute("currTileX",getTileX(unitID)); unit.setAttribute("currTileY",getTileY(unitID)); if(unit.getAttribute("priorTileY") != unit.getAttribute("currTileY") || unit.getAttribute("priorTileX") != unit.getAttribute("currTileX") ) { // //mark the current tile as UNwalkable // setWalkableFlag(unit.getAttribute("currTileX"), unit.getAttribute("currTileY"), unitID); // //mark the old tile as WALKABLE // setWalkableFlag(unit.getAttribute("priorTileX"), unit.getAttribute("priorTileY"), "0"); } if(newX != destX || newY != destY) { setTimeout("moveUnit(" + unitID + "," + tileDestX +"," + tileDestY+")",5); } } else { // //we collided //snap back to last tile // objectsCollided(unit, unitID, newTile.getAttribute("flag")); } }


    In our game, if the user/player manages to collide with an enemy we will pop the enemy back up to the top for them to start over.



    function objectsCollided(unit, unitID, enemyID)
    {
    var tmpTile = document.getElementById("tile_" + unit.getAttribute("priorTileX") + "_" + unit.getAttribute("priorTileY"))
    unit.style.top = getTop(tmpTile);
    unit.style.left = getLeft(tmpTile);

    if(unitID == 1000)
    {
    x = Math.floor(Math.random()*9);
    y = 0;
    jumpUnit(enemyID, x, y);
    }

    if(enemyID == 1000)
    {
    x = Math.floor(Math.random()*9);
    y = 0;
    jumpUnit(unitID, x, y);
    }

    }



    One last change, I had to make was in the jumpUnit() function. I found that I needed to flag and unflag the tiles as the unit jumped.

    function jumpUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    unit.style.left = getLeft(tile);
    unit.style.top = getTop(tile);
    //
    //mark the current tile as UNwalkable
    //
    setWalkableFlag(unit.getAttribute("currTileX"), unit.getAttribute("currTileY"), "0");

    //
    //mark the old tile as WALKABLE
    //
    setWalkableFlag(unit.getAttribute("priorTileX"), unit.getAttribute("priorTileY"), "0");

    unit.setAttribute("currTileX",x);
    unit.setAttribute("currTileY",y);
    unit.setAttribute("priorTileX",x);
    unit.setAttribute("priorTileY",y);

    }


    And that's the entire change I made...the following is the entire code set:

    <html>
    <style>
    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .unit {
    background:green;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .enemy {
    background:red;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }
    </style>
    <script language=javascript>

    var originX = 0;
    var originY = 0;
    var tileSize = 32;
    var mapHeight = 0;
    var mapWidth =0;
    function buildMap(width, height)
    {
    mapHeight = height;
    mapWidth = width;

    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("flag","0");
    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")
    document.getElementById("divMap").appendChild(d);
    }
    }

    function tileClicked(x,y)
    {
    //var tile = document.getElementById("tile_" + x + "_" + y)
    //tile.style.background = "yellow"

    moveUnit(1000, x, y);
    }

    function addUnit(unitID, x,y)
    {
    var d = document.createElement("DIV");
    var tile = document.getElementById("tile_" + x + "_" + y)
    if(unitID == 1000)
    {
    d.className="unit";
    }
    else
    {
    d.className="enemy";
    }
    d.setAttribute("xVel", 0);
    d.setAttribute("yVel", 0);
    d.setAttribute("ID","unit_" + unitID);
    d.style.top = tile.style.top;
    d.style.left = tile.style.left;
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("currTileX",x);
    d.setAttribute("currTileY",y);
    d.setAttribute("priorTileX",x);
    d.setAttribute("priorTileY",y);

    setWalkableFlag(x, y, unitID);

    document.getElementById("divMap").appendChild(d);
    }

    function setWalkableFlag(x, y, flag)
    {
    var tmpTile = document.getElementById("tile_" + x + "_" + y)
    tmpTile.setAttribute("flag", flag);

    /*
    if(flag > 0)
    {
    tmpTile.style.background = "yellow";
    }
    else
    {
    tmpTile.style.background = "#BBB";
    }
    */
    }

    function jumpUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    unit.style.left = getLeft(tile);
    unit.style.top = getTop(tile);
    //
    //mark the current tile as UNwalkable
    //
    setWalkableFlag(unit.getAttribute("currTileX"), unit.getAttribute("currTileY"), "0");

    //
    //mark the old tile as WALKABLE
    //
    setWalkableFlag(unit.getAttribute("priorTileX"), unit.getAttribute("priorTileY"), "0");

    unit.setAttribute("currTileX",x);
    unit.setAttribute("currTileY",y);
    unit.setAttribute("priorTileX",x);
    unit.setAttribute("priorTileY",y);

    }



    function moveUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    unit.setAttribute("priorTileX",getTileX(unitID));
    unit.setAttribute("priorTileY",getTileY(unitID));

    if(!tile)
    {
    return;
    }
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile);
    destY = getTop(tile);
    newX = x;
    newY = y;

    if(x > destX) newX--;
    if(x < destX) newX++;
    if(y > destY) newY--;
    if(y < destY) newY++;

    //if(newX < 0) newX = 0;
    //if(newX > mapWidth-1) newX = mapWidth-1;
    //if(newY < 0) newY = 0;
    //if(newY > mapHeight-1) newY = mapHeight-1;

    checkX = newX + tileSize/2;
    checkY = newY + tileSize/2;

    var newTile = document.getElementById("tile_" + translateTileX(checkX) + "_" + translateTileY(checkY))
    if(newTile.getAttribute("flag") == "0" || newTile.getAttribute("flag") == unitID)
    {
    unit.style.left = newX;
    unit.style.top = newY;

    unit.setAttribute("currTileX",getTileX(unitID));
    unit.setAttribute("currTileY",getTileY(unitID));

    if(unit.getAttribute("priorTileY") != unit.getAttribute("currTileY") || unit.getAttribute("priorTileX") != unit.getAttribute("currTileX") )
    {
    //
    //mark the current tile as UNwalkable
    //
    setWalkableFlag(unit.getAttribute("currTileX"), unit.getAttribute("currTileY"), unitID);

    //
    //mark the old tile as WALKABLE
    //
    setWalkableFlag(unit.getAttribute("priorTileX"), unit.getAttribute("priorTileY"), "0");
    }

    if(newX != destX || newY != destY)
    {
    setTimeout("moveUnit(" + unitID + "," + tileDestX +"," + tileDestY+")",5);
    }
    }
    else
    {
    //
    //we collided
    //snap back to last tile
    //
    objectsCollided(unit, unitID, newTile.getAttribute("flag"));


    }



    }


    function objectsCollided(unit, unitID, enemyID)
    {
    var tmpTile = document.getElementById("tile_" + unit.getAttribute("priorTileX") + "_" + unit.getAttribute("priorTileY"))
    unit.style.top = getTop(tmpTile);
    unit.style.left = getLeft(tmpTile);

    if(unitID == 1000)
    {
    x = Math.floor(Math.random()*9);
    y = 0;
    jumpUnit(enemyID, x, y);
    }

    if(enemyID == 1000)
    {
    x = Math.floor(Math.random()*9);
    y = 0;
    jumpUnit(unitID, x, y);
    }

    }


    function getTop(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.top.replace("px",""));
    }

    return 0;
    }

    function getLeft(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.left.replace("px",""));
    }

    return 0;
    }


    function translateTileX(x)
    {
    return Math.floor(x/tileSize - originX);
    }

    function translateTileY(y)
    {
    return Math.floor(y/tileSize - originY);
    }


    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    return translateTileY(getTop(unit));
    }

    function getTileX(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    return translateTileY(getLeft(unit));
    }

    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    x = Math.floor(getLeft(unit)/tileSize - originX);
    y = Math.floor(getTop(unit)/tileSize - originY);

    return y;
    }


    var playSpeed = 1000;
    var numUnits = 0;
    var maxNumUnits = 5;
    function play()
    {
    if(numUnits < maxNumUnits)
    {
    unitID = 1001+numUnits
    x = Math.floor(Math.random()*9);
    speed = Math.floor(Math.random()*3);
    addUnit(unitID,x,0);
    var unit = document.getElementById("unit_" + unitID);
    unit.setAttribute("xVel", 0);
    unit.setAttribute("yVel", speed+1);
    numUnits++;
    }

    for(i=0;i<numUnits;i++)
    {
    unitID = 1001+i;
    var unit = document.getElementById("unit_" + unitID);
    x = getTileX(unitID) + parseInt(unit.getAttribute("xVel"));
    y = getTileY(unitID) + parseInt(unit.getAttribute("yVel"));

    if(y >= mapHeight)
    {
    x = Math.floor(Math.random()*9);
    y=0;
    jumpUnit(unitID, x, y);
    }
    moveUnit(1001+i, x,y);
    }

    setTimeout("play()",playSpeed);
    }


    </script>
    <body onload="buildMap(10,10); addUnit(1000,4,4);play();">

    <div id="divMap"></div>

    </body>
    </html>


    Question: How would you go about adding a score indicator?

    Code: Simple 2D Javascript Map - Part 5

    This code will show how to create enemies and move them around the screen. It's going to randomly create 5 red boxes which will "fall" from the top of the grid to the bottom. The user can click a tile and the green box will move to the tile clicked. There is no collision detection...yet. I'm keeping this as simple as possible so in this example, "enemies" will drop from the top of the screen. Your job is to dodge them.

    The play function will get called every one second (playSpeed). It will add a new enemy until it has added 5 enemies. It chooses a random location at the top of the map, places the unit there.

    It will then pick a random speed and set the y velocity (think downward speed) equal to this random speed value.

    The second piece has the magic. It cycles through all enemy units and sets their next location to 3 spaces below where the unit currently is. Notice that this where our new functions get called.

    var playSpeed = 1000;
    var numUnits = 0;
    var maxNumUnits = 5;
    function play()
    {
    if(numUnits < unitid =" 1001+numUnits" x =" Math.floor(Math.random()*9);" speed =" Math.floor(Math.random()*3);" unit =" document.getElementById(" i="0;i= mapHeight)
    {
    x = Math.floor(Math.random()*9);
    y=0;
    jumpUnit(unitID, x, y);
    }
    moveUnit(1001+i, x,y);
    }

    setTimeout("play()",playSpeed);
    }


    Other changes



    Again, we have some plain Jane functions to get the tile location (x and y) of where our units is located. Make a note to yourself, there is plenty of room to optimize this code. It's dog slow.


    function getTileX(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    x = getLeft(unit)/tileSize - originX;
    y = getTop(unit)/tileSize - originY;

    return x;
    }

    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    x = getLeft(unit)/tileSize - originX;
    y = getTop(unit)/tileSize - originY;

    return y;
    }


    We also need to create a style for our enemy units. So I added this to the style.

    .enemy {
    background:red;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }


    Since we are moving random objects around, we need to start tracking the map height and width. So I changed the build map function.

    var mapHeight = 0;
    var mapWidth =0;
    function buildMap(width, height)
    {
    mapHeight = height;
    mapWidth = width;

    for(x=0;x<width;x++) y=0;y<height;y++);x++)


    Again, because we are moving enemies around randomly, need a bit of error catching added to the move function. If we try to move to a tile that does not exist, just stop moving.

    function moveUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile)
    ......


    Finally, I needed a function which will take an existing unit and "jump" them to a specific place on the map. This means they don't "move" one pixel at a time, but instead are immediately placed at that location:

    function jumpUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    unit.style.left = getLeft(tile);
    unit.style.top = getTop(tile);
    }


    Last but not least, we have to call our new play function from within the body tag.

    <body onload="buildMap(10,10); addUnit(1000,4,4); play()">





    Finally, the entire page looks like this:


    <html>
    <style>
    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .unit {
    background:green;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .enemy {
    background:red;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }
    </style>
    <script language=javascript>

    var originX = 0;
    var originY = 0;
    var tileSize = 32;
    var mapHeight = 0;
    var mapWidth =0;
    function buildMap(width, height)
    {
    mapHeight = height;
    mapWidth = width;

    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")
    document.getElementById("divMap").appendChild(d);
    }
    }

    function tileClicked(x,y)
    {
    //var tile = document.getElementById("tile_" + x + "_" + y)
    //tile.style.background = "yellow"

    moveUnit(1000, x, y);
    }

    function addUnit(unitID, x,y)
    {
    var d = document.createElement("DIV");
    var tile = document.getElementById("tile_" + x + "_" + y)
    if(unitID == 1000)
    {
    d.className="unit";
    }
    else
    {
    d.className="enemy";
    }
    d.setAttribute("xVel", 0);
    d.setAttribute("yVel", 0);
    d.setAttribute("ID","unit_" + unitID);
    d.style.top = tile.style.top;
    d.style.left = tile.style.left;
    d.style.width = tileSize;
    d.style.height = tileSize;
    document.getElementById("divMap").appendChild(d);
    }


    function jumpUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    unit.style.left = getLeft(tile);
    unit.style.top = getTop(tile);
    }



    function moveUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    if(!tile)
    {
    return;
    }
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile);
    destY = getTop(tile);
    newX = x;
    newY = y;

    if(x > destX) newX--;
    if(x < destX) newX++;
    if(y > destY) newY--;
    if(y < destY) newY++;

    unit.style.left = newX;
    unit.style.top = newY;

    if(newX != destX || newY != destY)
    {
    setTimeout("moveUnit(" + unitID + "," + tileDestX +"," + tileDestY+")",5);
    }

    }


    function getTop(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.top.replace("px",""));
    }

    return 0;
    }

    function getLeft(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.left.replace("px",""));
    }

    return 0;
    }

    function getTileX(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    x = getLeft(unit)/tileSize - originX;
    y = getTop(unit)/tileSize - originY;

    return x;
    }

    function getTileY(unitID)
    {
    var unit = document.getElementById("unit_" + unitID);
    x = getLeft(unit)/tileSize - originX;
    y = getTop(unit)/tileSize - originY;

    return y;
    }


    var playSpeed = 1000;
    var numUnits = 0;
    var maxNumUnits = 5;
    function play()
    {
    if(numUnits < maxNumUnits)
    {
    unitID = 1001+numUnits
    x = Math.floor(Math.random()*9);
    speed = Math.floor(Math.random()*3);
    addUnit(unitID,x,0);
    var unit = document.getElementById("unit_" + unitID);
    unit.setAttribute("xVel", 0);
    unit.setAttribute("yVel", speed+1);
    numUnits++;
    }

    for(i=0;i<numUnits;i++)
    {
    unitID = 1001+i;
    var unit = document.getElementById("unit_" + unitID);
    x = getTileX(unitID) + parseInt(unit.getAttribute("xVel"));
    y = getTileY(unitID) + parseInt(unit.getAttribute("yVel"));

    if(y >= mapHeight)
    {
    x = Math.floor(Math.random()*9);
    y=0;
    jumpUnit(unitID, x, y);
    }
    moveUnit(1001+i, x,y);
    }

    setTimeout("play()",playSpeed);
    }


    </script>
    <body onload="buildMap(10,10); addUnit(1000,4,4); play()">

    <div id="divMap"></div>

    </body>
    </html>

    Friday, April 2

    Code: Simple 2D Javascript Map - Part 4

    What's that you say, you want to move the character around the screen.

    OK, ok, ok...here we go.

    Because we will need to get the exact position of the div objects, I use the following two functions (In all fairness there are better, faster, more accurate ways to get the position of a div tag, but this has worked for me and is simple enough for my simple mind to grasp.)

    You pass them an object and they give you back the position of the object.


    function getTop(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.top.replace("px",""));
    }

    return 0;
    }

    function getLeft(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.left.replace("px",""));
    }

    return 0;
    }



    A bit boring, nothing to fascinating in those functions. However here is where things get a bit more exciting. The following function will move any unit object to the desired Tile position.

    We pass in the unitID we want to move, and the TILE x and y we want to move to. The function will:
    • Get the unit's and the tile's position
    • Compare the two positions and move the unit closer to the tile by one pixel.
    • At the very end of the function, if we have not reached our destination, we call the same function in 5 milliseconds.



    function moveUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile);
    destY = getTop(tile);
    newX = x;
    newY = y;



    if(x > destX) newX--;
    if(x <> destY) newY--;
    if(y < left =" newX;" top =" newY;">

    Now that moves the unit around, but no one is actually calling this function so



    Full Code:

    <html>
    <style>
    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .unit {
    background:green;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }
    </style>
    <script language=javascript>

    var originX = 0;
    var originY = 0;
    var tileSize = 32;

    function buildMap(width, height)
    {
    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")
    document.getElementById("divMap").appendChild(d);
    }
    }

    function tileClicked(x,y)
    {
    //var tile = document.getElementById("tile_" + x + "_" + y)
    //tile.style.background = "yellow"

    moveUnit(1000, x, y);
    }

    function addUnit(unitID, x,y)
    {
    var d = document.createElement("DIV");
    var tile = document.getElementById("tile_" + x + "_" + y)
    d.className="unit";
    d.setAttribute("ID","unit_" + unitID);
    d.style.top = tile.style.top;
    d.style.left = tile.style.left;
    d.style.width = tileSize;
    d.style.height = tileSize;
    document.getElementById("divMap").appendChild(d);
    }

    function moveUnit(unitID, tileDestX, tileDestY)
    {
    var unit = document.getElementById("unit_" + unitID);
    var tile = document.getElementById("tile_" + tileDestX + "_" + tileDestY)
    x = getLeft(unit);
    y = getTop(unit);
    destX = getLeft(tile);
    destY = getTop(tile);
    newX = x;
    newY = y;



    if(x > destX) newX--;
    if(x < destX) newX++;
    if(y > destY) newY--;
    if(y < destY) newY++;

    unit.style.left = newX;
    unit.style.top = newY;

    if(newX != destX || newY != destY)
    {
    setTimeout("moveUnit(" + unitID + "," + tileDestX +"," + tileDestY+")",5);
    }

    }


    function getTop(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.top.replace("px",""));
    }

    return 0;
    }

    function getLeft(obj)
    {
    if(obj)
    {
    return parseInt(obj.style.left.replace("px",""));
    }

    return 0;
    }


    </script>
    <body onload="buildMap(10,10); addUnit(1000,4,4)">

    <div id="divMap"></div>

    </body>
    </html>



    For giggles, I used this quick function to test play it. The function adds a number of units and randomly moves them around the screen. My desktop (Intel 2 Duo 2.2GH with 2GB Ram) can handle about 20 units in firefox, before it gets a bit odd.


    var playSpeed = 3000;
    var numUnits = 0;
    var maxNumUnits = 5;
    function play()
    {
    while(numUnits < i="0;i

    Code: Simple 2D Javascript Map - Part 3

    All right, let's put a character on the board!

    It's not difficult, first we create a function that will add the character for us. So we know that we will need to know the following:
    • A distinct ID for the unit (unitID)
    • The x and y of where to place the unit.


    Things to note:
    • We have to get the tile at X, Y.
    • We set the new div's tile to the Top and Left of the tile
    • We give the div tag a distinct "ID"
    • We give the new div tag a class of "unit"



    function addUnit(unitID, x,y)
    {
    var d = document.createElement("DIV");
    var tile = document.getElementById("tile_" + x + "_" + y)
    d.className="unit";
    d.setAttribute("ID","unit_" + unitID);
    d.style.top = tile.style.top;
    d.style.left = tile.style.left;
    d.style.width = tileSize;
    d.style.height = tileSize;
    document.getElementById("divMap").appendChild(d);
    }


    We've created a new css class "unit" so we need to add the following to our styles:


    .unit {
    background:green;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }


    Finally, we actually add the unit to the map when the body is loading.

    </script>
    <body onload="buildMap(10,10); addUnit(1000,4,4)">


    So the full page, looks like this...


    <html>
    <style>
    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }

    .unit {
    background:green;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }
    </style>
    <script language=javascript>

    var originX = 0;
    var originY = 0;
    var tileSize = 32;

    function buildMap(width, height)
    {
    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")
    document.getElementById("divMap").appendChild(d);
    }
    }

    function tileClicked(x,y)
    {
    var tile = document.getElementById("tile_" + x + "_" + y)
    tile.style.background = "yellow"
    }

    function addUnit(unitID, x,y)
    {
    var d = document.createElement("DIV");
    var tile = document.getElementById("tile_" + x + "_" + y)
    d.className="unit";
    d.setAttribute("ID","unit_" + unitID);
    d.style.top = tile.style.top;
    d.style.left = tile.style.left;
    d.style.width = tileSize;
    d.style.height = tileSize;
    document.getElementById("divMap").appendChild(d);
    }

    </script>
    <body onload="buildMap(10,10); addUnit(1000,4,4)">

    <div id="divMap"></div>

    </body>
    </html>

    Code: Simple 2D Javascript Map - Part 2

    So, we've built a map in javascript and displayed it. Quite impressive...or maybe not.

    Now we want to make something happen when the user clicks on a tile. In this example, we will change the background. First we add a simple function which we will call whenever a tile is clicked.

    function tileClicked(x,y)
    {
    var tile = document.getElementById("tile_" + x + "_" + y)
    tile.style.background = "yellow"
    }


    Now, we will attach this function to all the tiles, so within our buildMap() function we add this line of code:

    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")


    That's it...now when you click a tile, it turns yellow. Why don't you try to change it so that the background has an image and the image changes as you click it?

    Full Code:


    <html>
    <style>
    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }
    </style>
    <script language=javascript>

    var originX = 0;
    var originY = 0;
    var tileSize = 32;

    function buildMap(width, height)
    {
    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    d.setAttribute("onclick","tileClicked(" + x + "," + y + ")")
    document.getElementById("divMap").appendChild(d);
    }
    }

    function tileClicked(x,y)
    {
    var tile = document.getElementById("tile_" + x + "_" + y)
    tile.style.background = "yellow"
    }


    </script>
    <body onload="buildMap(10,10)">

    <div id="divMap"></div>

    </body>
    </html>

    Monday, March 29

    Code: Simple 2D Javascript Map

    A friend of mine asked me what is the quickest, simplest way to create a 2d map. Think top down, grid like map.

    Here is 35 lines of javascript code that will do just that. Enjoy...


    <html>
    <style>
    .tile {
    background:#BBB;
    border:1px solid #999;
    position:absolute;
    top:0px;left:0px;
    width:5px;height:5px;
    overflow:hidden
    }
    </style>
    <script language=javascript>

    var originX = 0;
    var originY = 0;
    var tileSize = 32;

    function buildMap(width, height)
    {
    for(x=0;x<width;x++)
    for(y=0;y<height;y++)
    {
    var d = document.createElement("DIV");
    d.className="tile";
    d.setAttribute("ID","tile_" + x + "_" + y);
    d.style.top = originY + (y*tileSize);
    d.style.left = originX + (x*tileSize);
    d.style.width = tileSize;
    d.style.height = tileSize;
    document.getElementById("divMap").appendChild(d);
    }
    }


    </script>
    <body onload="buildMap(10,10)">

    <div id="divMap"></div>

    </body>
    </html>

    Friday, March 19

    Artificial Intelligence - Dreaming and Learning


    So last but most importantly, what does the Ai do when its sensors are detecting nothing?

    It has two options.

    Use Active/Aggressive sensors
    The first is to turn on some active sensors. We can assume that some sensor it has can “roam” or find new views of it’s environment. Let’s say we have a web parsing sensor, it reads HTML and brings back links. This sensor would not be active unless there is nothing else going on. The Ai could decide to activate it. There could be an array of these type sensors.

    Dream
    The second option that it has is to dream. When dreaming the Ai will:
    1. Search it’s Long term memory for a random data variable
    2. Use this data and feed it to a all Evaluators except for “the best”.
    3. Any evaluator that understood the data gets a chance to process it
    4. Any evaluator that understood the data would get passed additional data elements that are “similar” to the original”

    Doing this allows the Brain to evaluate if it’s truly has the best evaluator and sensor collaborated.

    Artificial Intelligence - Memory Management Example


    So, let’s image that its time to bring the Ai Online. We’ve built all the components and we’re ready to go.

    Here’s how the first 1,000 interactions with if the Ai was only attached to a “Textbox” sensor.

    Definitely take a look at the picture as it conveys a lot more but briefly:

    “Hi” comes through the textbox sensor, the Ai stumbles through Evaluators till it hits the Language one, which say “I understand this data”, the Ai says ok and lets the evaluator do its thing.

    The next time the textbox sensor fires off, the Ai gives it to the only Evaluator it knows Language, language handles things smoothly. This takes place 999 times.

    However on interaction 1,000 along comes a math request “3+9”. The language evaluator says…Ummm, no I can’t do anything with this. Again the Ai stumbles around until it hits the Math evaluator, who says, yeah…I can do something with it.

    Now when textbox fires off, the brain will randomly ask Math…Do you understand this stuff…If Math says yes, the Brain lets it handle the text. If the Math says “no”, brain turns to Language and passes stuff off.

    Summary
    This is not the most efficient way and if you notice it never actually pattern matches against what comes in on the textbox sensor.

    However, if a different sensor is setup for math functions this approach would lead to an efficient Ai. Math coming across a speech/text sensor would be like being asked does this spell BLUE. Humans can do it, its just…odd, thus we are more likely to answer incorrectly.

    Artificial Intelligence - Memory Management


    Let’s talk Memory,

    A Brain is going to have to recall things, store numbers, understand relationships and all sorts of stuff that deal with “recall”. This is where the memory module will come into play.

    Memory will consists of 4 pieces. All parts of the memory are available to the Brain and the Evaluators. Memory is not available to sensors and or motors.

    Short Term Memory
    Short term memory is for swift, context based data. It will consist of a Sensor ID, the data, a timestamp, and number of times accessed counter. At first this will be a straight copy of the sensor stack. However it will change over time. The number of items/rows in short term memory will be dictated by the amount of time it takes to search it and retrieve a specific piece of data. For example, if a query against short term memory takes longer than 100 milliseconds, the memory handler will move the 10 least accessed memories to long term memory.

    Long Term Memory
    Long term memory will look just like short term memory. Sensor ID, data and timestamp for when it came in. However Long term memory will have a different requirement, when searching long term memory takes more than 10,000 milliseconds, delete 10 of the least access memories.

    Note: This means that the Ai’s memory will only be as good as it’s architecture. The faster the searching the more memory that the Ai can have in it’s short term memory.

    Action Memory
    Action memory records the chain of events that occurred from the data coming in to the motor response. It’s purpose is to help the Ai choose which evaluator to send sensor data to.

    Every time the Brain sends data to an Evaluator it will check to see if the sensor and evaluator exists in the Action memory:
    If it does not the Brain will insert a new row
    If it does, the Brain will increment the number of attempts


    When an Evaluator receives data, during its processing it will:
    • Increment the understood column for all rows where the same sensor and evaluator are specified
    • If a row does not exist for the motor used, the evaluator will insert a new row with that motor in it.

    The success rate is simply a calculation of attempts/understood.

    Technical Note: The Action Memory table is not normalized (its flattened), this is intentional as the inserts will be few a frequent while the seeks or queries will be much greater. Thus this table needs to support quicker seeks. I will attempt to explain further later.
    Memory Data
    For storing variable or relationships or even more complex “thoughts” there is data memory. It’s just a key, data paring. Which seems too simple. However, I think it would be the responsibility of the Evaluator to make sense of the “dirty” nature of the memory. I must admit that there is most likely a better solution. However keep in mind that this memory bank has to server a variety of functions and Evaluators.

    Summary
    So all of this gives us the memory and storage of data. I would expect that storing this and accessing this would take up about 90% of the storage and processing capacity of our Ai brain.

    Followers