Collision detection of each side not working - javascript

I've been recently working on a 2D platformer that has multiple characters you can change between. I wanted to implement collision detection that cycles through each character and depending on which side they're touching stop it from moving. Basically so the characters could jump on top of one another and couldn't pass through each other. I used an AABB Intersect collision detection system and then to determine which side of the character I've hit I've used this code:
for (var a = 0; a < this.characters.length; a++) {
for (var b = 0; b < this.characters.length; b++) {
if (a !== b) {
if (this.characters[a].collision(this.characters[b])) {
var ab = this.characters[a].y + this.characters[a].height,
ar = this.characters[a].x + this.characters[a].width,
bb = this.characters[b].y + this.characters[b].height,
br = this.characters[b].x + this.characters[b].width;
var tc = ab - this.characters[b].y,
bc = bb - this.characters[a].y,
lc = ar - this.characters[b].x,
rc = br - this.characters[a].x;
// Bottom is touching something
if (tc < bc && tc < lc && tc < rc) {
this.characters[a].isJumping = false;
this.characters[a].vel.y = 0;
this.characters[a].y = this.characters[b].y - this.characters[a].height;
}
// Top is touching something
if (bc < tc && bc < lc && bc < rc) {
this.characters[a].isJumping = false;
this.characters[a].y = this.characters[b].y + this.characters[b].height;
}
// Right side is touching something
if (lc < rc && lc < tc && lc < bc) {
this.characters[a].x = this.characters[b].x - this.characters[a].width;
}
// Left side is touching something
if (rc < lc && rc < tc && rc < bc) {
this.characters[a].x = this.characters[b].x + this.characters[b].width;
}
}
}
}
}
It appears to work fine for the first character (block; I didn't mention this, but, all of the characters are squares) but this system glitches if the I attempt to use the second character (the characters are in an array) and test the collision on the first character. Things such as the jumping on top of not working and the walking left into the character causing the first character to move left (I know why that's happening, but, I'm not sure how to fix it). The third character doesn't work for the first two and then by the fourth it's complete chaos. Does anyone have any suggestions?

A single collision will cause two hits in your loop, first when a = 0 and the collided object b = 1 (for example), and then when a = 1 and b = 0, the same collision will be detected again.
I think it's better to have an array of inactive characters that you check against one active character.
You can create those variables at the moment the player switches characters. This way you only need a single loop and you don't need the if a != b check
This code illustrates the idea:
function characterChange(){
activeCharacter = objA;
inActiveCharacters = [objB, objC, objD];
}
function checkCollisions(){
for(let i = 0; i<inActiveCharacters.length; i++){
checkHit(activeCharacter, inActiveCharacters[i]);
}
}
function checkHit(a,b) {
// your a b check here
}

Related

Javascript print square using for loop and conditional statement only

Just started my uni course, struggling a little with javascript. I have been asked to display a square using any character, however, the solution must combine for loops and if statements.
This is what I have so far and I feel pretty close but I just can't get the second line to display. I know this can be done via two for loops, (one for iteration of the variable and another for spaces). But this is not how I have been asked to solve this problem.
Here is my code:
var size = 3;
let i;
for(i = 0; i < size; i++) {
print ("*");
if (size === i){
println ("");
}
}
For context, this is all taking place int he professors homemade learning environment.
You could use nested for loops and take a line break after each filled line.
function print(s) { document.getElementById('out').innerHTML += s; }
function println(s) { document.getElementById('out').innerHTML += s + '\n'; }
var size = 5,
i, j;
for (i = 0; i < size; i++) {
for (j = 0; j < size; j++) {
print("*");
}
println("");
}
<pre id="out"></pre>
Single loop with a check if i is unequal to zero and if the remainder is zero, then add a line break.
Using:
=== identity/strict equality operator checks the type and the value, for example if both are numbers and if the value is the same,
!== non-identity/strict inequality operator it is like above, but it checks the oposite of it,
% remainder operator, which returns a rest of a number which division returns an integer number.
&& logical AND operator, which check both sides and returns the last value if both a truthy (like any array, object, number not zero, a not empty string, true), or the first, if it is falsy (like undefined, null, 0, '' (empty string), false, the oposite of truthy).
function print(s) { document.getElementById('out').innerHTML += s; }
function println(s) { document.getElementById('out').innerHTML += s + '\n'; }
var size = 5,
i;
for (i = 0; i < size * size; i++) {
if (i !== 0 && i % size === 0) {
println("");
}
print("*");
}
<pre id="out"></pre>
Well the for loop is only iterating 3 times, printing the first line. If you want a square you'll have to print 9 stars total, right? So i'm assuming, is this is the approach you'd go for, you would need to iterate not until size, but until size * size.
I'm using console.log to 'print' the square:
var dimension = 10;
var edge = '*';
var inside = ' ';
var printLine;
for (var i = 1; i <= dimension; i++) {
if (i === 1 || i === dimension) {
printline = Array(dimension + 1).join(edge);
} else {
printline = edge + Array(dimension - 1).join(inside) + edge;
}
console.log(printline);
}
Note that in the following example, an array of length 11 gets you only 10 "a"s, since Array.join puts the argument between the array elements:
Array(11).join('a'); // create string with 10 as "aaaaaaaaaa"
You wanna make a square of * where the size is the number of * on its sides?
Let's split a task into 3 parts:
where you print top side like *****
where you print middle (left and right sides) like * *
where you print bottom (same as top)
Now let's code that, I kept the code as simple as possible, this can be done in fewer lines but I think this will be easier to understand for beginners:
var size = 5;
var i = 0;
// top
for (i = 0; i < size; i++)
console.log("*");
//middle
for (var j = 0; j < size - 2; j++){
console.log("\n"); // go to next row
// middle (2 on sides with size-2 in between)
console.log("*");
for (i = 0; i < size-2; i++)
console.log(" ");
console.log("*\n"); // goes to new row as well
}
// same as top
for (i = 0; i < size; i++)
console.log("*");
Full square is even simpler:
var size = 5;
var i = 0;
for (var i = 0; i < size; i++){ // iterates rows
for (var j = 0; j < size; j++) // iterates * in row
console.log("*");
console.log("\n") // moves to new row
}
In order to print a row, you print same sign X times. Well, to print X rows we can use just that 1 more time (only this time we are iterating over a different variable (j is * in a row, i is a number of rows).
After a row is made we go to go to next row with \n.
As for
it must contain if statement
Put this at the end:
if (youCanHandleTheTruth) console.log("It's a terrible practice to tell students their solution MUST CONTAIN CODEWORDS. If you need them to showcase something, write appropriate task that will require them to do so.");

More Efficient Way to Accomplish This?

I need to know the level of a player using the amount of exp he has and the exp chart. I want to do it the most efficient way possible. This is what I got. Note: The real expChart has thousands of levels/index. All the values are in increasing order.
var expChart = [1,10,23,54,65,78,233,544,7666,22224,64654,456456,1123442];
/*
lvl 0: //0-1[ exp
lvl 1: //[1-10[ exp
lvl 2: //[10-23[ exp
*/
getLvlViaExp = function(exp){
for(var i = 0 ; i < expChart.length ; i++){
if(exp < expChart[i]) break;
}
return i;
}
This is a more efficient way to do it. Every x steps, (6 i the example, probably every hundreds with real chart), I do a quick comparation and jump to approximative index, skipping many indexes.
getLvlViaExp = function(exp){
var start = 0;
if(exp > 233) start = 6;
if(exp > 1123442) start = 12;
for(var i = start ; i < expChart.length ; i++){
if(exp < expChart[i]) break;
}
return i;
}
Is there an even better way to do this?
SOLUTION:
Array.prototype.binarySearch = function(value){
var startIndex = 0,
stopIndex = this.length - 1,
middle = Math.floor((stopIndex + startIndex)/2);
if(value < this[0]) return 0;
while(!(value >= this[middle] && value < this[middle+1]) && startIndex < stopIndex){
if (value < this[middle]){
stopIndex = middle - 1;
} else if (value > this[middle]){
startIndex = middle + 1;
}
middle = Math.floor((stopIndex + startIndex)/2);
}
return middle+1;
}
The best algorithm for searching is binary search which is O(lg n) (unless you can do it with a hashing search which is O(c).
http://www.nczonline.net/blog/2009/09/01/computer-science-in-javascript-binary-search/
Basically jump to the middle of your chart ( n / 2). Is you experience higher or lower from that number. If higher jump to the middle higher half. If lower jump to the middle of the lower half: Compare and repeat until you find what you're looking for.
var expChart = [1,10,23,54,65,78,233,544,7666,22224,64654,456456,1123442];
getLvlViaExp = function(exp){
var min=0;
var max=expChart.length-1;
var i;
while(min <=max){
i=Math.round((min+max)/2);
//document.write("<br />"+i+":"+min+":"+max+"<br />");
if(exp>=expChart[i] && exp <=expChart[i+1]) {
break;
}
if(exp>=expChart[i]){
min=i+1;
}
if(exp<=expChart[i]){
max=i-1;
}
}
return i;
}
document.write(getLvlViaExp("10"));
I have tested it and it seems to work pretty well. If you want to see how many steps it actually goes through to get to the answer, uncomment the document.write in the while loop. It was kind of fascinating watching it.

Algorithm of the greatest intersect of word in set of words

The story behind
I am creating a voice controlled application using x-webkit-speech which is surprisingly good (the feature, not my app), but sometimes the user (me) mumbles a bit. It would be nice to accept the command if some reasonable part of the word matches some reasonable part of some reasonable command. So I search for the holy grail called Algorithm of the Greatest Intersect of Word in Set of Words. Could some fresh bright mind drive me out of the cave of despair?
Example
"rotation" in ["notable","tattoo","onclick","statistically"]
should match tattoo because it has the longest intersect with rotation (tat_o). statistically is the second best (tati intersect), because longer part of the word needs to be ignored (but this is bonus condition, it would be acceptable without it).
Notes
I use Czech language where the pronunciation is very close to its written form
javascript is the preffered language, but any pseudocode is acceptable
the minimal length of the intersect should be a parameter of the algorithm
What have I tried?
Well, it is pretty embarassing....
for(var i=10; i>=4; --i) // reasonable substring
for(var word in words) // for all words in the set
for(var j=0; j<word.length-i; ++j) // search for any i substring
// aaargh... three levels of abstraction is too much for me
This is an algorithm that seems to work. I have no idea how good it performs compared to other already established algorithms (I suspect it perform worse) but maybe it gives you an idea how you could do it:
FIDDLE
var minInt = 3;
var arr = ["notable","tattoo","onclick","statistically"];
var word = "rotation";
var res = [];
if (word.length >= minInt) {
for (var i = 0; i < arr.length; i++) {
var comp = arr[i];
var m = 0;
if (comp.length >= minInt) {
for (var l = 0; l < comp.length - minInt + word.length - minInt + 1; l++) {
var subcomp = l > word.length - minInt ? comp.substring(l - word.length + minInt) : comp;
var subword = l < word.length - minInt ? word.substring(word.length - minInt - l) : word;
var minL = Math.min(subcomp.length, subword.length);
var matches = 0;
for (var k = 0; k < minL; k++) {
if (subcomp[k] === subword[k]) {
matches++;
}
}
if (matches > m) {
m = matches;
}
}
}
res[i] = m >= minInt ? m : null;
}
}
console.log(res);
What happens is, that it compares the two strings by "moving" on against the other and calculates the matching letters in each position. Here you see the compared "sub"words for rotation vs. notable:
ion / notable --> one match on index 1
tion / notable --> no match
ation / notable --> no match
tation / notable --> one match on index 2
otation / notable --> no match
rotation / notable --> three matches on index 1,2,3
rotation / otable --> no match
rotation / table --> no match
rotation / able --> no match
rotation / ble --> no match
As you see, the maximum number of matches is 3 and that is what it would return.
Here's an implementation of a Levenshtein Distance Calculator in Javascript.
It returns an object containing the matching command and distance.
var commandArr = ["cat", "dog", "fish", "copy", "delete"]
var testCommand = "bopy";
function closestMatch(str, arr)
{
//console.log("match called");
var matchDist = [];
var min, pos;
for(var i=0; i<arr.length; i++)
{
matchDist[i]=calcLevDist(str, arr[i]);
console.log("Testing "+ str + " against " + arr[i]);
}
//http://stackoverflow.com/questions/5442109/how-to-get-the-min-elements-inside-an-array-in-javascript
min = Math.min.apply(null,matchDist);
pos = matchDist.indexOf(min);
var output = { match : arr[pos],
distance : matchDist[pos]
};
return output;
}
function calcLevDist (str1, str2)
{
//console.log("calc running");
var cost = 0 , len1, len2;
var x = 1;
while(x > 0)
{
len1 = str1.length;
console.log("Length of String 1 = " + len1);
len2 = str2.length;
console.log("Length of String 2 = " + len2);
if(len1 == 0)
{
cost+= len2;
return cost;
}
if(len2 == 0)
{
cost+= len1;
return cost;
}
x = Math.min(len1,len2);
if(str1.charAt(len1 -1) != str2.charAt(len2 -1))
{
cost++;
}
else
console.log(str1.charAt(len1-1) + " matches " + str2.charAt(len2-1));
str1 = str1.substring(0, len1 -1 );
str2 = str2.substring(0, len2 -1 );
console.log("Current Cost = " + cost);
}
}
var matchObj = closestMatch(testCommand, commandArr);
var match = matchObj["match"];
var dist = matchObj["distance"];
$("#result").html("Closest match to " + testCommand + " = " + match + " with a Lev Distance of " + dist + "." )
You can mess around with the fiddle here.
Thank you basilikum and JasonNichols and also Mike and Andrew for the comments, it really helped me to finish the algorithm. I come up with my own brute force O(n^3) solution in case someone runs into this question with the same problem.
Anyone is invited to play with the fiddle to improve it.
The algorithm
/**
* Fuzzy match for word in array of strings with given accurancy
* #param string needle word to search
* #param int accurancy minimum matching characters
* #param array haystack array of strings to examine
* #return string matching word or undefined if none is found
*/
function fuzzyMatch(needle,accurancy,haystack) {
function strcmpshift(a,b,shift) {
var match=0, len=Math.min(a.length,b.length);
for(var i in a) if(a[i]==b[+i+shift]) ++match;
return match;
}
function strcmp(a,b) {
for(var i=0,max=0,now; i<b.length; ++i) {
now = strcmpshift(a,b,i);
if(now>max) max = now;
}
return max;
}
var word,best=accurancy-1,step,item;
for(var i in haystack) {
item = haystack[i];
step = Math.max(strcmp(item,needle),strcmp(needle,item));
if(step<=best) continue;
best=step, word=item;
};
return word;
}
Example
var word = "rotation";
var commands = ["notable","tattoo","onclick","statistically"];
// find the closest command with at least 3 matching characters
var command = fuzzyMatch(word,3,commands);
alert(command); // tattoo

Trying to read this Javascript loop

I'm trying to read this code. As far as I can tell, it's checking if the child element is "at position" (but it's equal to a string? That makes no sense to me). If it isn't, then it adds 2 to the offset, if it is, it does nothing.
var p = document.getElementById(parent);
var c = document.getElementById(child );
var top = (c["at_position"] == "y") ? p.offsetHeight+2 : 0;
var left = (c["at_position"] == "x") ? p.offsetWidth +2 : 0;
And then here, this for loop does not make sense to me - why does it start with a semicolon?
for (; p; p = p.offsetParent)
{
top += p.offsetTop;
left += p.offsetLeft;
}
Can anyone clarify how to read this a little bit better? I'm trying to reduce the distance between the parent and the child to a limited extent (Only for "left")
It loops while p is truethy that is, p is not null because offsetParent will likely be null once your reach the top.
It's equivalent to:
var p = document.getElementById(parent);
while (p !== null) {
top += p.offsetTop;
left += p.offsetLeft;
p = p.offsetParent;
}
Normally a for loop looks like this:
for (var i = 0; i < array.length; i += 1) {
the first bit var i = 0 is the initialization part. You can leave it empty like:
var i = 0;
for (; i < array.length; i += 1) {
You can also do this:
for (var i = 0, j = 10 - i; i < array.length; i += 1, j -= 1) {
Although there are perhaps better ways to do it. Just showing you the syntax :)
The second part is the part that must be truethy, if it's false the for loop will stop. This can't be empty of course.
The last part is the incrementer part, which can also be empty. So you could have:
var p = document.getElementById(parent);
for (; p ;) {
p = p.offsetParent;
}
It looks weird, but works. I wouldn't recommend this though because it looks weird, and by that I mean that it looks like a bug.

Javascript regular expressions problem

I am creating a small Yahtzee game and i have run into some regex problems. I need to verify certain criteria to see if they are met. The fields one to six is very straight forward the problem comes after that. Like trying to create a regex that matches the ladder. The Straight should contain one of the following characters 1-5. It must contain one of each to pass but i can't figure out how to check for it. I was thinking /1{1}2{1}3{1}4{1}5{1}/g; but that only matches if they come in order. How can i check if they don't come in the correct order?
If I understood you right, you want to check if a string contains the numbers from 1 to 5 in random order. If that is correct, then you can use:
var s = '25143';
var valid = s.match(/^[1-5]{5}$/);
for (var i=1; i<=5; i++) {
if (!s.match(i.toString())) valid = false;
}
Or:
var s = '25143';
var valid = s.split('').sort().join('').match(/^12345$/);
Although this definitely can be solved with regular expressions, I find it quite interesting and educative to provide a "pure" solution, based on simple arithmetic. It goes like this:
function yahtzee(comb) {
if(comb.length != 5) return null;
var map = [0, 0, 0, 0, 0, 0];
for(var i = 0; i < comb.length; i++) {
var digit = comb.charCodeAt(i) - 48;
if(digit < 1 || digit > 6) return null;
map[digit - 1]++;
}
var sum = 0, p = 0, seq = 0;
for(var i = 0; i < map.length; i++) {
if(map[i] == 2) sum += 20;
if(map[i] >= 3) sum += map[i];
p = map[i] ? p + 1 : 0;
if(p > seq) seq = p;
}
if(sum == 5) return "Yahtzee";
if(sum == 23) return "Full House";
if(sum == 3) return "Three-Of-A-Kind";
if(sum == 4) return "Four-Of-A-Kind";
if(seq == 5) return "Large Straight";
if(seq == 4) return "Small Straight";
return "Chance";
}
for reference, Yahtzee rules
For simplicity and easiness, I'd go with indexOf.
string.indexOf(searchstring, start)
Loop 1 to 5 like Max but just check indexOf i, break out for any false.
This also will help for the small straight, which is only 4 out of 5 in order(12345 or 23456).
Edit: Woops. 1234, 2345, 3456. Sorry.
You could even have a generic function to check for straights of an arbitrary length, passing in the maximum loop index as well as the string to check.
"12543".split('').sort().join('') == '12345'
With regex:
return /^([1-5])(?!\1)([1-5])(?!\1|\2)([1-5])(?!\1|\2|\3)([1-5])(?!\1|\2|\3|\4)[1-5]$/.test("15243");
(Not that it's recommended...)
A regexp is likely not the best solution for this problem, but for fun:
/^(?=.*1)(?=.*2)(?=.*3)(?=.*4)(?=.*5).{5}$/.test("12354")
That matches every string that contains exactly five characters, being the numbers 1-5, with one of each.
(?=.*1) is a positive lookahead, essentially saying "to the very right of here, there should be whatever or nothing followed by 1".
Lookaheads don't "consume" any part of the regexp, so each number check starts off the beginning of the string.
Then there's .{5} to actually consume the five characters, to make sure there's the right number of them.

Categories

Resources