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

Monday, April 5

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.

Artificial Intelligence - The Brain


So this picture paints the whole picture, except for the most important part, which is in the next post. But let’s make sure we have it all covered.

Here’s how it works

  • When the “brain” is ready to start a new tas
  • It will remove the top item from the sensor stack
  • It will determine the best sensor (this is the unexplained important part)
  • The Evaluator will send commands to the motor stack
  • It will also send memory objects and requests to Memory Handler

This continues till there is nothing to sense. We will talk about what the brain does when there is no data on the sensor, but not right now.

So, we have a little gray area, but the picture is getting clearer right?

Let’s continue…

Friday, March 12

Artificial Intelligence - Evaluators



So we discussed how the Ai will sense the world and we have discussed how it will interact with the world. How will it bridge the gap and make intelligent interactions based on what it has sensed?

Before we can cover that you have to understand that the Ai Brain is actually made up of a number of parts. Some folks would consider each of these parts as Agents or AI themselves. I will leave that debate to others that are wiser than me.

For my purposes I call them Evaluators. The Ai Brain consists of many of them. Each Evaluator is responsible for performing the following:

  • It takes data in, the data will always be in a rawest form
  • It attempts to understand this data
    • It will understand the data if it knows an motor action that can be given performed based on the input
    • Or it knows a memory object that needs to get created based on this action
    • Or it knows another evaluator that will understand this data
  • If it knows a motor action to perform based on the input, it will place the motor action on the motor stack
  • If it knows a memory object that needs to get created it will create the memory object*
  • If it knows another evaluator that will understand this data it will pass this “suggestion” along. **

Note that a sensor stack item may be sent to multiple evaluators. Some evaluators will be better at some things then others and usually there is a “best” evaluator for each type of data on the sensor stack. We will talk about this more later.

Example evaluators:
  • Language evaluator - responsible for holding conversations
  • Math Evaluator – responsible for performing calculations
  • Image Evaluator – responsible for identifying an image
  • Knowledge Evaluator – responsible for identifying relationships between objects

I would imagine that at some point the Ai brain may create its own evaluators by combining or altering existing evaluators. We will talk on this further as well.

* See memory creation post (tbd)
** See brain evaluator selection post (tbd)

Followers