"Yes! The Great Cthulhu requires groupies!"
Programming is my craft. Most of my experience is with the
web, but my budding loves are 2D game development,
simulation, and algorithm implementation.
A more formal resume is available upon request, but please
consider my public projects and code as the canonical
demonstration of my ability.
My area of residency is flexible.
Samuel Ferrell, 2012
/*
A* Optimal Graph Search
*/
function Path(graph, start, end, heuristic) {
var openSet, begin;
// Out of graph bounds
if (!graph.inBounds(start) || !graph.inBounds(end)) {
return false;
}
// Heuristic function
heuristic = heuristic || Path.manhattan;
// Open set
openSet = new BinaryHeap(function (element) {
return element.fscore;
});
// First node
begin = graph.nodes[start];
begin.gscore = 0;
begin.hscore = heuristic(begin.index, end);
begin.fscore = begin.gscore + begin.hscore;
openSet.push(begin);
while (openSet.heap.length) {
var current = openSet.pop();
if (current.index == end) {
return Path.reconstruct(current);
} else {
current.closed = true;
var neighbors = graph.neighbors(current);
var i;
for (i = 0; i < neighbors.length; i += 1) {
var neighbor = neighbors[i];
if (neighbor.closed === true) {
continue;
}
var cost = current.gscore + 1;
var visited = neighbor.visited;
if (!visited || cost < neighbor.gscore) {
neighbor.visited = true;
neighbor.parent = current;
neighbor.hscore = heuristic(neighbor.index, end);
neighbor.gscore = cost;
neighbor.fscore = neighbor.hscore + neighbor.gscore;
if (!visited) {
openSet.push(neighbor);
} else {
openSet.rescoreElement(neighbor);
}
}
}
}
}
return false;
}
/*
2D Vector
*/
function Vec2(x, y) {
this.x = x || 0;
this.y = y || 0;
this.magcache = Vec2.magnitude(this);
}
/*
Addition
a + b = c
*/
Vec2.add = function (a, b) {
return new Vec2(a.x + b.x, a.y + b.y);
};
/*
Subtraction
a - b = c
*/
Vec2.subtract = function (a, b) {
return new Vec2(a.x - b.x, a.y - b.y);
};
/*
Multiplication
a * t = b
*/
Vec2.multiply = function (a, t) {
return new Vec2(round(t * a.x, 4), round(t * a.y, 4));
};
/*
Direction
arc tangent (a.x / a.y)
*/
Vec2.direction = function (a) {
return round(Math.atan(a.y / a.x), 4);
};
/*
Distance
a^2 + b^2 = c^2
*/
Vec2.distance = function (a, b) {
return round(Math.sqrt(Math.pow(a.x - b.x, 2)
+ Math.pow(a.y - b.y, 2)), 4);
};
/*
Negation
a = -a
*/
Vec2.negative = function (a) {
return Vec2.multiply(a, -1);
};
/*
Linear Interpolation
t * ((b - a) + a) = c
*/
Vec2.interpolate = function (a, b, t) {
return Vec2.multiply(Vec2.add(Vec2.subtract(b, a), a), t);
};
/*
Normalization
a * (1 / magnitude(a))
*/
Vec2.normalize = function (a) {
return Vec2.multiply(a, 1 / (Vec2.magnitude(a)));
};
/*
Magnitude
a^2 = c^2
*/
Vec2.magnitude = function (a) {
// Use raw object to avoid cache recursion
return Vec2.distance(a, {x: 0, y: 0});
}
/*
Linear Vector Interpolation Tween
*/
return {
queue: [],
lerp: function (p, a, b, duration, callback, target) {
if (Vec2.isEqual(a, b)) {
if (typeof callback === 'function' &&
typeof target !== 'undefined') {
callback.apply(target);
}
return;
}
this.queue.push({
p: p,
a: a.clone(),
b: b.clone(),
duration: duration,
distance: Vec2.distance(a, b),
fn: callback,
trgt: target,
t: 0
});
},
update: function (dt) {
if (this.queue.length) {
var i, q, t = 0;
for (i = 0; i < this.queue.length; i += 1) {
// Shorthand Identifier
q = this.queue[i];
// Calculate step
t = 1 / (q.distance / (q.distance / (q.duration * dt)));
// Offset queued step
q.t += t;
if ((q.t) >= 1) {
// We're home
q.p.place(q.b);
this.queue.remove(i);
if (typeof q.fn === 'function') {
return q.fn.apply(q.trgt);
}
} else {
// Not there yet
q.p.place(Vec2.interpolate(q.a, q.b, q.t));
}
}
}
}
}
/*
Chess Board Move Generation
*/
function generate(piece) {
var i, j, n,
offsets, distance,
occupying,
offset,
generated = [],
offsets = piece.getOffsets();
distance = piece.getDistance();
for (i = 0; i < offsets.length; i += 1) {
offset = offsets[i];
n = piece.position;
for (j = 0; j < distance; j += 1) {
n = mailbox[mailbox64[n] + offset];
// Illegal - Offboard
if (n === -1) {
break;
}
// Pawn - Movement + Capture Fixes
if (piece.isType("Pawn")) {
if (offset === 10 || offset === -10) {
if (this.isOccupied(n)) {
break;
}
} else {
if (!this.isOccupied(n)) {
break;
}
}
}
if (piece.isType("King")) {
var enemy = piece.isColor("White") ? "Black" : "White";
if (_.contains(this[enemy], n)) {
break;
}
}
// Capture
if (this.isOccupied(n)) {
occupying = this.fetch(n);
if (piece.isEnemy(occupying)) {
generated.push(n);
break;
}
break;
}
// Open
generated.push(n);
}
}
return generated;
};