Interesting read...
http://www.investopedia.com/articles/trading/04/120804.asp
Monday, November 25
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;
}
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));
};
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>
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>
Monday, October 28
Quote
Great leaders are almost always great simplifiers, who can cut through argument, debate and doubt, to offer a solution everybody can understand.
Saturday, October 26
Logi - Secure Key in 3 Steps
Secure Key Authorization or Single Sign On integration is a popular item that requires reviewing with a lot of the clients I have dealt with.
This might help.
First Step: Leave your current login/authorization in place. You won't need to make modifications to it.
Second Step:
When your users click a link in your site that opens a Logi report, or opens the Logi portal you have built you will need to perform the secure key authentication at this point. If I had a C# application I might code it up as follows:
protected void rptLink_Click(object sender, EventArgs e)
{
StringBuilder lgxSecureKeyUrl = new StringBuilder("http://localhost/myLogiApp/);
//add the constant location of secure key handler in Logi
lgxSecureKeyUrl.Append("rdTemplate/rdGetSecureKey.aspx");
//The username is required (this could also be the UserID that you use to secure the app)
lgxSecureKeyUrl.Append("?Username=99999");
//You can pass roles and rights if you have them allready
lgxSecureKeyUrl.Append("&Roles=Manager,Admin");
lgxSecureKeyUrl.Append("&Rights=ViewReportList,ViewReports,CreateDashboards");
//this needs to be the USERS ip address
lgxSecureKeyUrl.Append("&ClientBrowserAddress=127.0.0.1");
//shoot the request over to Logi
HttpWebRequest lgxSecureKeyReq = (HttpWebRequest) HttpWebRequest.Create(lgxSecureKeyUrl.ToString());
//the response will be a secure key that is good for a single one time use
HttpWebResponse lgxSecureKeyResp = (HttpWebResponse)lgxSecureKeyReq.GetResponse();
StreamReader sr = new StreamReader(lgxSecureKeyResp.GetResponseStream());
string lgxSecureKey = sr.ReadToEnd();
//you will now send the user to your Logi app with that secure key
//logi will take it from there
StringBuilder lgxReportUrl = new StringBuilder("http://localhost/myLogiApp/rdPage.aspx?rdReport=Default");
//dont forget the secure key
lgxReportUrl.Append("&rdSecureKey=");
lgxReportUrl.Append(lgxSecureKey);
//send the user in what ever way you want
Response.Redirect(lgxReportUrl.ToString());
This might help.
First Step: Leave your current login/authorization in place. You won't need to make modifications to it.
Second Step:
When your users click a link in your site that opens a Logi report, or opens the Logi portal you have built you will need to perform the secure key authentication at this point. If I had a C# application I might code it up as follows:
protected void rptLink_Click(object sender, EventArgs e)
{
StringBuilder lgxSecureKeyUrl = new StringBuilder("http://localhost/myLogiApp/);
//add the constant location of secure key handler in Logi
lgxSecureKeyUrl.Append("rdTemplate/rdGetSecureKey.aspx");
//The username is required (this could also be the UserID that you use to secure the app)
lgxSecureKeyUrl.Append("?Username=99999");
//You can pass roles and rights if you have them allready
lgxSecureKeyUrl.Append("&Roles=Manager,Admin");
lgxSecureKeyUrl.Append("&Rights=ViewReportList,ViewReports,CreateDashboards");
//this needs to be the USERS ip address
lgxSecureKeyUrl.Append("&ClientBrowserAddress=127.0.0.1");
//shoot the request over to Logi
HttpWebRequest lgxSecureKeyReq = (HttpWebRequest) HttpWebRequest.Create(lgxSecureKeyUrl.ToString());
//the response will be a secure key that is good for a single one time use
HttpWebResponse lgxSecureKeyResp = (HttpWebResponse)lgxSecureKeyReq.GetResponse();
StreamReader sr = new StreamReader(lgxSecureKeyResp.GetResponseStream());
string lgxSecureKey = sr.ReadToEnd();
//you will now send the user to your Logi app with that secure key
//logi will take it from there
StringBuilder lgxReportUrl = new StringBuilder("http://localhost/myLogiApp/rdPage.aspx?rdReport=Default");
//dont forget the secure key
lgxReportUrl.Append("&rdSecureKey=");
lgxReportUrl.Append(lgxSecureKey);
//send the user in what ever way you want
Response.Redirect(lgxReportUrl.ToString());
}
Third Step:
To setup Logi to handle the secure, you will edit the _Settings file.
Add a Security Element with the following settings:
To setup Logi to handle the secure, you will edit the _Settings file.
Add a Security Element with the following settings:
- Authentication Source: Secure Key
- Authentication Client Address: [server ip]
- Cache Rights and Roles: Session
- Restart Session: True
- Security Enabled: True
That should do it for you
Troubleshooting:
The secure key is only good for one request, so if you submit the link and then click refresh, you will get an error as authentication fails. This is intentional. Notice the secure key is in the url when you refreshed.
Passing your UserID in the username is the best way to handle authentication, as then you can query and store any additional information you need using that key.
Keep in mind the rdGetSecureKey request is taking place between your primary server and the logi application
Monday, October 21
Monday, October 14
Saturday, October 12
Kool Tool to pick Colors
I like Adobe's Khulr picker.
Easy to find a color, snag the HTML code and past it in. Plus it offsets my color blindness.
http://kuler.adobe.com
Easy to find a color, snag the HTML code and past it in. Plus it offsets my color blindness.
http://kuler.adobe.com
Monday, October 7
Friday, October 4
Love\Hate AI
I don't normally do this, but I think that this information should not be lost: This is a Word for Word post from: http://home.swipnet.se/dungeondweller/development/dev00055.htm INTRODUCTION ------------ This article is based on a system proposed by Hansjoerg Malthaner [hansjoerg.malthaner@danet.de] which in turn based some of it on Joseph Swing's excellent article "A better monster AI". The system described here is used for my own RL Dungeondweller. There are many ways to create an interesting AI. One way would be to hardcode each creatures behaviour. If enough work is put into such a hardcoded system the results can be quite impressing, but as said, it needs a lot of work. A better (or at least faster) way of creating an interesting AI would be to take a look at behaviourism. Behaviourism states that a creature reacts to external stimuli. This is not entirely true since humans have a mind and a concept of Self, but the general idea behind behaviourism is sufficient for the AI of a roguelike. Imagine what would happen if you teleported a Kobold into a big dungeon. Lets say that the Kobold starts in an empty corridor. The first thing the Kobold does is to look around and ensure that there are no enemies in sight. If he is alone he decides that he feels safer if he wields a sword and equips some armour. At the moment this is his most urgent need. The Kobold reacts to the situation and puts on his leather armour and wields his shortsword. If the Kobold still doesn't see anyone he begins to interest himself for his environment. His most urgent need would then be to move. The Kobold continues to move through the corridor until he reaches a door. If the Kobold still doesn't see anyone his most urgent need would be to open the door. The Kobold reacts to this new situation and opens the door. He then looks around again and sees that the door led into a big room. Near the door lies a pile of shining gold coins and at the other end of a room a big snake looks back at the Kobold! The Kobold evaluates this new environment, and decides that he loves the gold more than he feares and hates the snake. The Kobold reacts to the situation and starts to move closer to the pile of gold. As the Kobold gets closer his fear and hate of the snake grows, as does his love for the gold. At some point the Kobolds fear is greater than the Kobolds love. The Kobolds most urgent need will become to move away from the snake. The Kobold reacts to this and starts to retreat from the snake. All of a sudden a door opens to the left of the Kobold and another Kobold appears. Our Kobold evaluates this new scenario, and immediately recognise the other Kobold as a friend. The Kobold loves :-) other Kobolds, and this new Kobold is armed to the teeth with shining armour and pointy weapons. Once again his love for the gold and for his new found friend is greater than the fear of the snake. The Kobold reacts, moves towards the gold and his friend and picks it up. ... The story above seems fairly realistic and it is not that difficult to achieve a behaviour similar to the behaviour of the Kobold. It all boils down to a three step process: 1.Observe and evaluate the environment 2.Find the most urgent need, based on the environment and the self 3.Perform the need First the AI need to look around and evaluate it's environment, just like the Kobold did. The AI assigns values for Fear, Love and Hate to all creatures and objects within sight. The next step is to find the most urgent need at that time. The AI base this descision on the Fear, Love and Hate for perceived objects and creatures and on its own status (the Kobold puts on equipment). When the most urgent need is found it is performed, and the cycle starts over again. Each of the above steps are explained in detail below: 1 OBSERVE AND EVALUATE THE ENVIRONMENT -------------------------------------- Before the AI decides what to do it needs to evaluate its environment. The AI need to look around and memorize all objects and creatures that it can perceive. This can be done using normal LOS, or any other suitable way. Each object or creature (from now on entity) perceived is assigned a Fear/Love value (1.2) and a Hate value (1.3). The most Feared, Loved and Hated entity is memorized (1.4) and the Fear/Love and Hate center is calculated (1.5). These values will be used later on to determine where to move or whom to attack. How to achieve all this? Well, it is fairly simple. To help us with our calculations we need a couple of help methods: 1.1 Help methods ---------------- These methods are made to make life easier for us during the calculation of the Fear/Love and Hate values. The methods could have more uses in your game, so they are worth implementing. Each entity needs a three methods called; physicalCondition(), perceivedStrength() and perceivedDanger(). 1.1.1 physicalCondition() ------------------------- this method returns the entity's current hitpoints divided by its maximum hitpoints. This gives us a value from 0 and upwards: 0 - Dead <1 -="" 1="" damaged="" not="">1 - Boosted 1.1.2 perceivedStrength() ------------------------- This method should return the entity's perceived strength, ie how strong it looks if we were to see it. This value should be based on the entity's physique, not powerful equipment. I base the value on the entity's strength multiplied by it's physicalCondition(). You may wish to include more factors when calculating the perceived strength, but this works fine. 1.1.3 perceivedDanger() ----------------------- This method should return the entity's perceived danger, ie how dangerous it looks. This value should be based on the entity's equipment. If the entity has sharp weapons and heavy armour the returned value should be high. Ok, now that we have some methods to work with, we can assign Fear/Love and Hate values to the perceived entities. 1.2 The Fear/Love value ----------------------- The Fear/love value, assigned to each perceived entity, is based on factors such as the perceived entity's strength, danger and distance from the AI. If the entity is a friend it gets a positive value (=love) and if it is an enemy it gets a negative value (=fear). The closer to the Ai, the higher the value. The AI rates a friend/enemy differently if the AI belongs to an instinct driven creature (tiger, snake etc), a creature with a mind (human, orc etc) or a mindless creature (undead). 1.2.1 Mindless Fear/Love ------------------------ A mindless creature doesn't have any feelings at all and rates friends and enemies as Fear/Love = 0 1.2.2 Instinctdriven Fear/Love ------------------------------ A creature driven by instincts doesn't have the intelligence to measure a friends equipment. It measures a friend by how much stronger he/she is than itself: (1000 * (friend->perceivedStrength() / own->perceivedStrength())) / distance Enemies are measured in the same manner (except for the '-'): (-1000 * (enemy->perceivedStrength() / own->perceivedStrength())) / distance 1.2.3 Minddriven Fear/Love -------------------------- A creature with intelligence measures a friend both by its strength and by its equipment. If the friend is stronger and/or better equipped it is loved more: (500 * (friend->perceivedStrength() / own->perceivedStrength()) + 500 * (friend->perceivedDanger() / own->perceivedDanger())) / distance And enemies: (-500 * (friend->perceivedStrength() / own->perceivedStrength()) + -500 * (friend->perceivedDanger() / own->perceivedDanger())) / distance You may wish to change the 500 * strength and 500 * danger to other values. Some minddriven creatures may value equipment more than strength and vice versa. Remember that the sum should be 1000. There is no particular reason for choosing 1000, you could go with even higher numbers to achieve higher variation, but I think 1000 is enough. Remember that Fear/Love also applies to objects. If the perceived entity is an object you may wish to assign a value to it. Treasures and similar items would become Loved, at least by most minddriven creatures. Similarly some kinds of objects might be Feared (unholy symbols etc) 1.3 The Hate value ------------------ The Hate value is based on the AI's feeling for other entities along with the distance from the AI. The more it hates something the higher the value. As for the Fear/Love value you can differentiate between instinct, mind and mindless AI. You can also add extra rules to make your AI hate some creatures more than others. For simplicity I just set the rule that if the AI isn't of the same race as the perceived entity the Hate value is set to 1000 / distance, otherwise 0. 1.4 Memorize most Feared, Loved and Hated ----------------------------------------- You need to keep track of the most Feared, Loved and Hated entity that the AI can perceive. This is just a matter of saving the currently most Feared, Loved and Hated creatures Fear/Love or Hate value along with a pointer or similar. This is kind of implementation specific, so just solve it in a way suitable for your game. It should present no problem. 1.5 Calculating the Fear/Love and Hate center --------------------------------------------- This is where it gets really interesting. Here you need to calculate where the Fear/Love and Hate center are positioned among all of the perceived entities. I'll try to illustrate what we wish to achieve: ......... In this first example the AI (A) can perceive only one entity (1) .1....... ......... The center of Fear/Love and Hate are then positioned on the same ......... cell as the entity ......... ....A.... ......... ......... In this example the AI (A) can perceive two entities (1) and (2) .1..x..2. The AI has assigned exactly the same Fear/Love and Hate values to ......... both entities ......... ......... The center of Fear/Love and Hate are then positioned at a cell on ....A.... equal distance from both (1) and (2). In this case cell (x) ......... ......... In this example the AI (A) can perceive two entities (1) and (2) .1f.h..2. The AI has assigned a higher Fear/Love value to (1), but the same ......... Hate values to both (1) and (2) ......... ......... The center of Fear/Love (f) is then positioned closer to (1) than ....A.... than (2). The center of Hate (h) is still placed at equal distance ......... from (1) and (2) ......... As you add more entities the Fear/Love and Hate centers are moved .1.....2. around more and more. ......... ...h..... ......... ....A.... ..f...... ......... .3....... At a first glance this may seem quite complex, but it is fairly simple to calculate. The first thing you need to keep track of is the total Fear/Love and Hate values for the entities that you have assigned Fear/Love and Hate values to. The next thing you need is a x,y coordinate for the Fear/Love and Hate center. When the AI begins to evaluate its environment the total Fear/Love and Hate are zero and the Fear/Love center and Hate center has not been assigned any values. After assigning a Fear/Love and Hate value to the first of the perceived entities the center of Fear/Love and Hate are positioned at the same coordinates as the entity, just like in the first figure above. Subsequent position calculations are done like this: 1.Find the smallest of absolute value of total Fear/Love and absolute value of perceived entity's Fear/Love. ie: minFL = __min(abs(totalFearLove),abs(entityFearLove)) 2.Find the largest, ie: maxFL = __max(abs(totalFearLove),abs(entityFearLove)) 3.Subtract the x coordinate of the Fear/Love center from the perceived entity's x coordinate, and multiply the result by (minFL / maxFl). Add the resulting value to the Fear/Love center's x coordinate. ie: fearLoveX += ((entityX - fearLoveX) * (minFL / maxFL)) 4.Do the same for y. ie: fearLoveY += ((entityY - fearLoveY) * (minFL / maxFL)) 5.Repeat the same procedure for Hate center minH = __min(totalHate,entityHate) absolute values not nescessary since maxH = __max(totalHate,entityHate) Hate value always is positive hateX += ((entityX - hateX) * (minH / maxH)) hateY += ((entityY - hateY) * (minH / maxH)) After you have repeated all of the above for all of the AI's perceived entities you know which entity the AI Feares, Loves and Hates the most. You know of the total Fear/Love and Hate values for all of the perceived entities, and you know where the center of Fear/Love and Hate are positioned. When all of this is done we have evaluated the environment, and have all the knowledge we need to decide for an appropriate course of action. 2 FIND THE MOST URGENT NEED (CONTESTING ACTIONS) ------------------------------------------------ Each AI driven creature needs to be assigned a set of actions that it can perform. The actions vary depending on the type of creature. A humanoid should be able to perform actions such as OPEN, PICKUP, DROP, EQUIP, MOVE and ATTACK while a snake is a bit more limited; MOVE and ATTACK. Each action is represented by two methods called rate() and execute(). The methods take a reference to the creature in question as parameter. Each time the AI need to decide which action to perform, the rate() method is called on each of the available actions. The rate() method decides how urgent it is to perform the action. A high value gives the action high priority and a low value gives the action low priority. The action that has the highest rating is the one being performed, i.e calling the execute() method. This is simple enough, but how to rate each action? This is the tricky part, and it needs a bit of balancing before it works. I used fixed values for the priorites: PRIORITY_NONE 0 PRIORITY_LOW 50 PRIORITY_NORMAL 100 PRIORITY_HIGH 200 PRIORITY_HIGHEST 800 Let's have a look at some of the actions one by one: 2.1 OPEN.rate(creature*) ------------------------ The method examines the mapcells surrounding the creature. If a closed door is found the method returns a priority of NORMAL, otherwise 0. The closed door is remebered so that the execute method knows what to open. 2.2 PICKLOCK.rate(creature*) ---------------------------- The method examines the mapcells surrounding the creature. If a locked door is found the method returns a priority of NORMAL, otherwise 0. The locked door is remebered so that the execute method knows what to lockpick. 2.3 DROP.rate(creature*) ------------------------ In the system that I use for Dungeondweller, each creature has a method called physcialSpeed(). The method returns a value from 0 and upwards, 0 meaning immobilized, less than 1 is slowed, 1 normal speed and greater than 1 meaning fast. The method takes into account the creatures strength, the amount of inventory carried and the creatures physical condition. If the creature is slowed down (physicalSpeed() < 1) the rate() method decides for an object to drop, and returns a priority of NORMAL/physicalSpeed(), otherwise rate() returns 0. This gives a higher value the more encumbered and wounded the creature is. If extremely hurt the creature will throw away inventory to be able to flee faster. There is a lot of room for intelligent selection of object to drop, but you could just start from the top of the creature's inventory, or choose the heaviest object in the inventory. 2.4 PICKUP.rate(creature*) -------------------------- If the creature is standing on an object and it isn't slowed down the method returns a priority of NORMAL, otherwise 0. The object is remebered so that the execute() method knows what to pick up. You might wish to modify the priority if the creature is a scavenger or if the object to pick up is valueable etc. 2.5 EQUIP.rate(creature*) ------------------------- If the creature isn't equipped with a weapon and it has a weapon in its inventory the method returns a priority of HIGH, otherwise it continues to check for other things to equip (armour, rings etc). If something is found it is remembered for the execute() method and the priority is HIGH. If nothing needs to be equipped the method returns 0. You could expand this and check for better equipment in the inventory than the already equipped equipment. Checks for broken weapons and similar things can also be done here. 2.6 MOVE.rate(creature*) ------------------------ If and where to move the creature is decided by the environment. This is where all of our calculations in step 1 comes into use. If the total Hate and total FearLove are zero the creature should just move around in a manner that seems intelligent. There are many different ways to move a creature on a map. This is a rather big topic and it would be more appropriate to address this in another article. You could just write something simple that made the creature follow the walls or similar. Remember the direction of movement and return with a priority of LOW Else if the total Hate is zero, and total Fear/Love is non zero the creature should move towards the Fear/Love center. This will make the creature move towards it's friends. To prevent you creatures from forming big packs that won't move an inch since the all love eachover that much you could also check the creatures distance to the Fear/Love center. If the creature is within a certain distance, say three cells, just move the creature using the same system as for total Hate and total Fear/Love equal to zero. Remember the direction of movement and return a priority of NORMAL Else if the total Fear/Love is bigger than the total Hate the creature is at a scary place and should flee. The creature should move in a direction directly away from the Fear/Love center. Remember the direction of movement and return a priority of HIGHEST. Else the creature should move to attack, and direction of movement should be towards the center of Hate. Remeber the direction of movement and return a priority of HIGH. 2.7 ATTACK.rate(creature*) -------------------------- Last but not least we have the attack action. If the most Hated entity is within range of any of the creatures attacks the target is remembered and the method returns a priority of HIGHEST. 3 Perform the need ------------------ All that is left now is to call the execute() method of the action that returned the highest rating. I kept the code for the execute methods very general so I could use the same execute() code when the player performs his/her actions. 4 How to achieve more variation ------------------------------- At the moment I have only implemented the basic system described above, but from the discussion at r.g.r.d Hansjoerg suggested the following tweaks: How to get variety into the AI? Of course we can give each monster individual fears and hates. We can change the things/monsters which are feared and hated and we can change the strength of fear and hate. This will make cowards and fighters from our monsters. The love value (a monster has negative fear for friends) will make them move towards each other until their fear reduces and if they hate something nearby they will move there as a pack. More variety can be introduced by restriction the monsters perception. Some monsters may see far others not. Some may regocnize wepaons as dangerous some not. If a monster does not recognize wepons it might not fear a heavily armed player and attackk him (stupid monster). Another monster might fear the player if it preceives him healthy and well armed. It fears hime less if wouned. Such a monster will stay away from the player as long as he is well and wields his weapon. This monster will attack if the players weapon breaks or the player self is wounded ("Clever monster"). If the mosnters fear drop faster with distance than the hate, such a monster will follow the player, because it fears to be near the player, but stil hates him from a distance, which makes it stay away a certain distance. Monsters with ranged attacks which fear the player will stay away from him, until their fear drops, and then stay there and attack the player with their ranged attacks. If fear is made to depend on the health of the monster, an monster could rush towards the player (no fear) and attack him. After the first strike they receive the fear goes up and the monster runs away. This would yield the above mentioned "hit-and-run" tactic. From kamikaze to cowards quite some different monsters seem to be able to be modeled by this simple system. I'm not sure if it will really work until I made my tests. One thing is sure: monster stats must hold all the mentioned information (what is feared, what is loved, what is hated, how strong does fear drop with distance, how strong does hate drop with distance, can the monster perceive weapons and armor, how tough is the monster/how strong does it fear to be wounded) Send any comments, questions or suggestions to: Björn Bergström dungeondweller@swipnet.se 1>
Monday, September 23
Quote
The best way is always to stop when you are going good and when you know what will happen next. If you do that every day … you will never be stuck. Always stop while you are going good and don’t think about it or worry about it until you start to write the next day. That way your subconscious will work on it all the time. But if you think about it consciously or worry about it you will kill it and your brain will be tired before you start.
- Ernest Hemingway
- Ernest Hemingway
Friday, September 20
Create Your Own Google Map
To create a google map do the following:
Get an Google Maps API Key, feel free to use your normal gmail account. Without this you can't use google maps.
Next insert this into the head of your html. Replacing "YOUR_KEY" with your key.
Next, create a map div tag. Where ever you put this code, is where the map will actually display. The width and height tags will dictate the width and height of the map. This can be included in any other div tag or HTML tag.
Next copy this bit of code in the bottom of your page. (Why the bottom? because it has to happen AFTER the divMap. There are ways around this. Hint: try creating an onload body event)
And that's it.
So of course your going what is this? 3.907787,-79.359741 ???
Those are geocodes, they map to a specific point on the earth's globe. If you want to map to an address you will have to convert the address to a geocode. Google does this as well, but its not so simple. So I'll save it for a whole other post.
In the mean time, use this.
Get an Google Maps API Key, feel free to use your normal gmail account. Without this you can't use google maps.
Next insert this into the head of your html. Replacing "YOUR_KEY" with your key.
Next, create a map div tag. Where ever you put this code, is where the map will actually display. The width and height tags will dictate the width and height of the map. This can be included in any other div tag or HTML tag.
Next copy this bit of code in the bottom of your page. (Why the bottom? because it has to happen AFTER the divMap. There are ways around this. Hint: try creating an onload body event)
And that's it.
So of course your going what is this? 3.907787,-79.359741 ???
Those are geocodes, they map to a specific point on the earth's globe. If you want to map to an address you will have to convert the address to a geocode. Google does this as well, but its not so simple. So I'll save it for a whole other post.
In the mean time, use this.
Wednesday, September 18
Monday, September 16
Logi Analytics - jQuery Auto-complete
This would be the third time I've been asked to incorporate some type of auto-complete function using jQuery and Logi elements.
Here is my cheat sheet:
1. First create a report that returns the results you want in your auto-complete drop down:
- @Request.q~ will have what the user typed
- Keep it simple and quick as this will impact response times
- A single data layer and data table is typically enough
2. Include the jquery script files, I've not had a lot of issues with versioning, so anything over 9 should be fine.
- http://code.jquery.com/jquery-1.9.1.js
- http://code.jquery.com/ui/1.10.3/jquery-ui.js
3. Add a user input element with an ID of inptSearchText
4. Add the following script in a script tag, typically at the bottom of the page:
$(document).ready(function(){
$("#iptAutoComplete").autocomplete({
minLength: 2,
delimiter: /(,|;)\s*/, // regex or character
source: function(request, response)
{
var matches = null;
var data = null;
if(data != null )
{
matches = $.map(data, function(tag) {
if ( tag.value.match(new RegExp("^" +request.term, "i"))){
return tag;
}
});
response(matches);
}
if (matches == null || matches.length == 0){
$.ajax({
url: 'rdPage.aspx',
data: {rdReport:"jQuery.search",rdReportFormat:"DataLayerXml", q:request.term },
dataType: 'xml',
success: function( xmlResponse ) {
data = $( "dtSearchResults", xmlResponse ).map(
function() {
return {
value: $(this).attr("FORMATTED_NAME"),
id: $(this).attr("EMP_ID")
};
});
response(data.slice(0, 10));
}
})
}
},
select: function(event, ui)
{
alert("you have selected " + ui.item.value + " (" + ui.item.id + ")");
}
});
});
5. Modify the following:
4. Add the following script in a script tag, typically at the bottom of the page:
$(document).ready(function(){
$("#iptAutoComplete").autocomplete({
minLength: 2,
delimiter: /(,|;)\s*/, // regex or character
source: function(request, response)
{
var matches = null;
var data = null;
if(data != null )
{
matches = $.map(data, function(tag) {
if ( tag.value.match(new RegExp("^" +request.term, "i"))){
return tag;
}
});
response(matches);
}
if (matches == null || matches.length == 0){
$.ajax({
url: 'rdPage.aspx',
data: {rdReport:"jQuery.search",rdReportFormat:"DataLayerXml", q:request.term },
dataType: 'xml',
success: function( xmlResponse ) {
data = $( "dtSearchResults", xmlResponse ).map(
function() {
return {
value: $(this).attr("FORMATTED_NAME"),
id: $(this).attr("EMP_ID")
};
});
response(data.slice(0, 10));
}
})
}
},
select: function(event, ui)
{
alert("you have selected " + ui.item.value + " (" + ui.item.id + ")");
}
});
});
5. Modify the following:
- "jQuery.search" - replace with the name of the report from step 1
- FORMATTED_NAME needs to be your displayed value from prior table
- EMP_ID needs to be the id you want triggered with the specific data set
- minLength:2 - Set this to the number of characters the user types before triggering the AJAX call
- alert(....) - Call your own javascript function or re-direction after selection. Typically you would store the ID in some hidden variable for future usage.
Thursday, September 12
Wednesday, September 11
Evaluation of Trader
I was watching this thread over at HotStockMarket, thought I'd make some notes on how accurate this fella Binks was:
Some disclaimers: This is a paper trade, I did not actually make these trades. It is impossible for me to get in and out exactly when he is, therefore, anytime he made a post on the thread, I assume that I bought at the open price on the following day of the post. Obviously, this could lead to some major differences in profit.
8/27/13 - GGS - in at 2.70, At 2.64 on 9/5 = 2% loss
8/28/13 - AStx- in at 6.39 on 8/29, At 8.61 on 9/5 = 34% profit
8/29/13 - NVA - in at 3.22 on 9/3, At 3.23 on 9/5 = 1% profit
8/23/13 - AMRN - in at 6.10, At 7.36 on 9/5 = 20% profit
8/26/13 - MXWL- in at 9.47, At 9.00 on 9/5 = 5% loss
8/26/13 - STSI - in at 2.03, At 1.93 on 9/5 = 4.9% loss
8/26/13 - YRCW in at 17.50, At 17.65 on 9/5 = .8% gain
8/16/13 - AMRN in at 6.01, At 7.37 on 9/5 = 22% profit
8/19/13 - ASTX in at 5.25, At 8.64 on 9/5 = 64% profit
8/13/13 - RAD in at 3.60, At 3.53 on 9/5 = 1% loss
8/14/13 - PCO in at 2.00, At 1.88 on 9/5 = 6% loss
8/14/13 - AMRN in at 5.14, At 7.36 on 9/5 = 32% profit
7/8/13 - AMD in at 4.40, At 3.56 on 9/5 = 20% loss
7/18/13 - HPQ in at 26.33, At 22.52 on 9/5 = 15% loss
7/15/13 - GTAT = 35% profit
7/15/13 - ICA= 10% profit
7/15/13 - AGNC = 3% loss
7/15/13 - MAKO= 22% profit
7/11/13 - ITMN = 14% profit
7/11/13 - ECTE = 1% profit
Conclusion:
Some disclaimers: This is a paper trade, I did not actually make these trades. It is impossible for me to get in and out exactly when he is, therefore, anytime he made a post on the thread, I assume that I bought at the open price on the following day of the post. Obviously, this could lead to some major differences in profit.
8/27/13 - GGS - in at 2.70, At 2.64 on 9/5 = 2% loss
8/28/13 - AStx- in at 6.39 on 8/29, At 8.61 on 9/5 = 34% profit
8/29/13 - NVA - in at 3.22 on 9/3, At 3.23 on 9/5 = 1% profit
8/23/13 - AMRN - in at 6.10, At 7.36 on 9/5 = 20% profit
8/26/13 - MXWL- in at 9.47, At 9.00 on 9/5 = 5% loss
8/26/13 - STSI - in at 2.03, At 1.93 on 9/5 = 4.9% loss
8/26/13 - YRCW in at 17.50, At 17.65 on 9/5 = .8% gain
8/16/13 - AMRN in at 6.01, At 7.37 on 9/5 = 22% profit
8/19/13 - ASTX in at 5.25, At 8.64 on 9/5 = 64% profit
8/13/13 - RAD in at 3.60, At 3.53 on 9/5 = 1% loss
8/14/13 - PCO in at 2.00, At 1.88 on 9/5 = 6% loss
8/14/13 - AMRN in at 5.14, At 7.36 on 9/5 = 32% profit
7/8/13 - AMD in at 4.40, At 3.56 on 9/5 = 20% loss
7/18/13 - HPQ in at 26.33, At 22.52 on 9/5 = 15% loss
7/15/13 - GTAT = 35% profit
7/15/13 - ICA= 10% profit
7/15/13 - AGNC = 3% loss
7/15/13 - MAKO= 22% profit
7/11/13 - ITMN = 14% profit
7/11/13 - ECTE = 1% profit
Conclusion:
- 20 Picks over last 2 months
- Gainers: 11
- Losers: 8
- Profit: 196%
Monday, September 9
Friday, September 6
Conversations with Jarvis
Some real conversations with Jarvis, an artificial intelligent chat bot I played with a while back.
He is down now, but I hope to bring him back soon.
Visitor 13 (not me)
User: hello javis
Jarvis: Do you have any bacon?
User: no
Jarvis: Are you saying no just to be negative?
User: i have no bacon on me
Jarvis: You don't know much about investing?
Visitor 10 (Me)
User: You make me laugh too
Jarvis: Me?
User: Yes you
Jarvis: A good time to leave me alone.
User: It is a good time to be alone.
Jarvis: Yes you
User: And maybe you
Jarvis: You tell me, is today special?
User: Today is my birthday, I think.
Jarvis: i don know you do i
User: No, but you can come to my birthday party.
Jarvis: NULL
User: NULL is not an answer, its a bug.
Jarvis: I don't think you know me. Do I know you?
User: Nope, do you want to get to know me?
Jarvis: Hey Jarvis. What's up?
User: I'm not Jarvis, you are...
Jarvis: Today is my birthday, I think.
User: How old are you?
Jarvis: Hows it going?
He is down now, but I hope to bring him back soon.
Visitor 13 (not me)
User: hello javis
Jarvis: Do you have any bacon?
User: no
Jarvis: Are you saying no just to be negative?
User: i have no bacon on me
Jarvis: You don't know much about investing?
Visitor 10 (Me)
User: You make me laugh too
Jarvis: Me?
User: Yes you
Jarvis: A good time to leave me alone.
User: It is a good time to be alone.
Jarvis: Yes you
User: And maybe you
Jarvis: You tell me, is today special?
User: Today is my birthday, I think.
Jarvis: i don know you do i
User: No, but you can come to my birthday party.
Jarvis: NULL
User: NULL is not an answer, its a bug.
Jarvis: I don't think you know me. Do I know you?
User: Nope, do you want to get to know me?
Jarvis: Hey Jarvis. What's up?
User: I'm not Jarvis, you are...
Jarvis: Today is my birthday, I think.
User: How old are you?
Jarvis: Hows it going?
Wednesday, September 4
Sharing Game Info Across Sites
The idea of sharing characters, objects and user information across multiple games and environments has always interested me.
I've got a simplistic idea on how this could be done and wanted to share it.
Some assumptions:
Step 1:
Joe signs up for Game A and plays it. All information is stored within Game A as normal.
When Joe logs out of Game A. He is given a url that will contain all his information.
www.GameA.com/userInfo.php?userID=10
Next Step:
Joe now wants to play Game B. He has the option of entering in his url or registering as normal.
Once given a url Game B will request Joe's information from Game A.
Game A will response with pair values, like so:
Game B will take the values it can use and create/update Joe's account within it's game environment. Such as UserName, Score and Money. (Maybe this game does not have characters)
Next Step:
Joe is done playing Game B. As he logs out he is given the opportunity to get a new URL or he can update Game A.
If Joe decided to update Game A, Game B will send a request to Game A letting it know the url it can find an update at.
www.GameB.com/userInfo.php?userID=99
Game A will go to that url and process all of Joe's information, updating it's stats as needed.
Thoughts:
One of the advantages of this system is that if Site A were to disappear or be unavailable, Joe still retains all the information from Game B. It's even possible for him to save information from Game A, if the site does disappear, if Game B has it saved.
For those who are worried about malicious use of the system, it would be quite easy to code the update process to only allow updates from certain game sites. If you did not want to see updates from Game C, you could exclude that site from your updates by looking at the request url.
Alternatively someone could offer a service where "trusted" games can exchange information.
The user information could be in an XML document, but I have found that the ease with which you can build key pair values is easier to build and search then XML documents. I know the advantages, I personally don't think the advantages out weigh the disadvantages in this case.
This is a stab in the dark any thoughts and comments are appreciated.
I've got a simplistic idea on how this could be done and wanted to share it.
Some assumptions:
Let's assume we have 3 environments (games) Game A, B and C. They are similar in some concepts and vastly different in others.
Let's assume we have one user, Joe, who wants to play in all three environments.
Step 1:
Joe signs up for Game A and plays it. All information is stored within Game A as normal.
When Joe logs out of Game A. He is given a url that will contain all his information.
www.GameA.com/userInfo.php?userID=10
Next Step:
Joe now wants to play Game B. He has the option of entering in his url or registering as normal.
Once given a url Game B will request Joe's information from Game A.
Game A will response with pair values, like so:
UserName=JoeGame A will also record the fact that Game B requested information. This will come into play when Joe goes back to playing Game A.
Email=joe@somewhere.com
Score=1000
Money=500
CharacterName=Killer
CharacterHP=50
CharacterMagic=40
etc...
etc...
Game B will take the values it can use and create/update Joe's account within it's game environment. Such as UserName, Score and Money. (Maybe this game does not have characters)
Next Step:
Joe is done playing Game B. As he logs out he is given the opportunity to get a new URL or he can update Game A.
If Joe decided to update Game A, Game B will send a request to Game A letting it know the url it can find an update at.
www.GameB.com/userInfo.php?userID=99
Game A will go to that url and process all of Joe's information, updating it's stats as needed.
Thoughts:
One of the advantages of this system is that if Site A were to disappear or be unavailable, Joe still retains all the information from Game B. It's even possible for him to save information from Game A, if the site does disappear, if Game B has it saved.
For those who are worried about malicious use of the system, it would be quite easy to code the update process to only allow updates from certain game sites. If you did not want to see updates from Game C, you could exclude that site from your updates by looking at the request url.
Alternatively someone could offer a service where "trusted" games can exchange information.
The user information could be in an XML document, but I have found that the ease with which you can build key pair values is easier to build and search then XML documents. I know the advantages, I personally don't think the advantages out weigh the disadvantages in this case.
This is a stab in the dark any thoughts and comments are appreciated.
Followers
Copyright 2009 mobeamer. Powered by Blogger
Blogger Templates created by Deluxe Templates
Wordpress by Nattywp