Explanation needed for highlanders


#1

I didn’t understand a single instruction in this level, can someone explain it please.
I wrote quite literally what the comments said because I couldn’t figure out any other way.
I’m sure you’ll find no sense in this code, but here it is anyway:

// You must defeat the ogres
// But they are using black magic!
// Only the highlander soldiers are immune.
// Find highlanders, their names always contain "mac"

var highlanderName = "mac";

// This function should search for a string inside of a word:
function wordInString(string, word) {
    var lenString = string.length;
    var lenWord = word.length;
    // Step through indexes (i) from 0 to (lenString - lenWord)
    for ( var i = 0; i < (lenString - lenWord); i++) {
        // For each of them through indexes (j) of the word length
        for (var j = 0; j < lenWord; j++) {
            // If [i + j]th letter of the string is not equal [j]th letter of world, then break loop
            if ( [i + j] != [j]) {
                break;
            }
            // if [j]th is the last letter of the word (j == lenWord - 1), then return True.
            if ([j] == (j == lenWord - 1)) {
                return true;
            }
        }
    }
    // If loops are ended then the word is not inside the string. Return False.
    
    return false; // ∆ Remove this when the function is written.
}

// Look at your soldiers and choose highlanders only
var soldiers = hero.findFriends();
for (var i = 0; i < soldiers.length; i++) {
    var soldier = soldiers[i];
    if (wordInString(soldier.id, highlanderName)) {
        hero.say(soldier.id + " be ready.");
    }
}

// 
hero.say("ATTACK!!!");

#2

@mattgamer This would be a level with a reference to the 1986 movie “Highlander” from 20th Century Fox.

The main character was Connor MacLeod. So the level plays on this theme with an idea that Scottish family names have “Mac” in them like Macleod. The names of the soldiers soldier.id would thus contain a “mac” if they were highlanders (mythical swordsmen). Therefore if we use string manipulation we should be able to search for a sub-string within the string that is equal to “mac” if they are good soldiers to send into battle.

Make sense?

In order to find the ideal soldiers to win the battle we are to search through all of the friends and find the ones with names that match. Such as MacGregor, MacNeir etc - more Gaelic names found here.

We are sending in the soldier.id (name) and “mac” to the function wordInString() and then using manipulation to determine if soldier.id has mac in it. For instance the name “Cunningham” would not, and the function would return false however “MacLennan” does have a mac in it so the function would return true.

The solution is quite simple if you ignore all the comments and just ask yourself how would you find out if a name contained the word “mac”.

They want us to start at the first character and compare it with “m”, then try the second character in the name and compare it to “a”, and then the third letter in the name and compare it to “c”. If all 3 letters match, then the name must contain the word “mac”. Because mac can be anywhere in the name from the start to end we need to repeat this process starting at the next character in the name. So if the name was “Berrymickymac” it would take us 11 iterations of the outer loop before we reached the mac at the end. i in this case would equal 10 (11th array spot). Also keep in mind that because the word “mac” is three characters, we don’t need to search the last 2 characters as they cannot by definition be a 3 character word.


Now take a look back at your function and look at your code under the

// If [i + j]th letter of the string is not equal [j]th letter of world, then break loop

comment. Ask yourself, how are you suppose to access different array spots? If you have to, you can separate out the math from the array by using a new variable. Again the inner loop will check 3 characters in the name against the three characters in the word “mac”. So think about how that will happen and the math should begin to make more sense. We first (outer loop) get to the place in the name where we want to start checking and then (inner loop) check the three characters “m”, “a”, “c” to see if the are equal.

Your code is very close good job so far!


#3

Yes! I’ve been waiting this :slight_smile: Thanks that you noticed this :slight_smile: I LOVE that movie (the 1st one, the second is awful)


#4

OMG @Harry_the_Wanderer thank you so much for taking the time to write all of that!
I do understand the outer-inner loop stuff the way you put it, however I can’t think of a way to write it in code,
maybe because I do not fully understand array and names handling.
and also you mean that

 if ( [i + j] != [j]) {
                break;
            }

and

 if ([j] == (j == lenWord - 1)) {
                return true;
            }

are actually correct?
if so, then I’m surprised because these lines are the first thing that came into my mind and I do not understand them either.
thanks again for your help :grinning:


#5

@mattgamer no problem. I understand how some times having things explained a little different can help.

ok, so I will copy some of the help content from the game on arrays:

From the Desert Campaign World, Sarven Savior level


#6

ok, that clears arrays, but I still don’t get how to define the string in the word.
having that i think i could compare it with the highlanderName.
I tried this, but my hero does nothing:

 for ( var i; i < lenWord; i++) {
        for (var j; j < lenString; j++) {
            if (lenString[j] == highlanderName) {
                return true;
            }
        }
    }
    return false;
}

#7

You didn’t define i and j start values.


#8

@mattgamer

Take into consideration a few things.

  1. if (wordInString(soldier.id, highlanderName)) { is being called later in the code, so we are already passing the value of highlanderName to the function wordInString(). Since it is being passed in as the second parameter then when we look at the function definition function wordInString(string, word) { word would have the value of “mac”. So we should use word instead of highlanderName.

  2. Like @Bryukh mentioned a for loop must initialize its lcv in order to function. Rather like a car without gas if you do not write the (var i = 0 part the loop just doesn’t do anything. Here is a sandbox at w3schools you can use to test it out. Try removing the i = 0 part of for (i = 0; i < 5; i++) { so that it states for (i; i < 5; i++) { and see what happens. Also try initializing the variable i outside of the loop where it is declared var i = 0; and see what happens.

  3. Know what you are comparing. After you initialize each lcv (i and j) to 0 try changing the following code so that you can see what values you are comparing:

     hero.say("lenString = " + lenString[j] + ", highlanderName = " + highlanderName );
     if (lenString[j] == highlanderName) {
         return true;
     }
    

    this will allow you to debug your code. What is the value of lenString[j] and what is the value of highlanderName? And does it make sense to compare them in this way, and why?

  4. Also, Look at your original posted code and make sure your loops are in the correct order. The original was in the correct order.

 

*lcv stands for “loop control variable”


#9

still nothing

 for ( var i = 0; i < lenString; i++) {
        for (var j = 0; j < lenWord; j++) {
           
            if (lenWord[j] == word) {
                return true;
            }
        }
    }
    return false;
}

also, it says that lenWord[j] is undefined


#10

Add in the hero.say() here:

for ( var i = 0; i < lenString; i++) {
        for (var j = 0; j < lenWord; j++) {
            hero.say("lenWord = " + lenWord + ", word = " + word );
            if (lenWord[j] == word) {
                return true;
            }
        }
    }
    return false;
}

And as you noticed lenWord[j] may be undefined but what is lenWord?


#11

ok, now it says that lenWord = 3 and Word = mac
it makes sense because if Word = mac, then the length of the word is obviously 3 (letters)


#12

@mattgamer I am going to add a bit here to the concept of a string as I am not sure if it was gone over in the explanations of the levels.

Character

A character is just “a” or “g”, one letter.

String

A string is an array of characters. So “mac” is a string and an array of characters. Does that help?

if you add a code segment: hero.say(highlanderName[1]) what do you expect would be the output?

Q: how would you compare each “character” inside the strings?


#13

I think highlanderName[1] woul be "a"
would it be correct if I compare string[i] with with word[j] ?


#14

I believe you are getting there! Try it.


#15
for ( var i = 0; i < lenString; i++) {
        for (var j = 0; j < lenWord; j++) {
           
            if (string[i] == word[j]) {
                return true;
            }
        }
    }
    return false;
}

OMG so close!
my hero calls all MACs but also 2 normal soldiers ( “stormy” “earl”)
because of that I end up losing anyway
I think “earl” got passed because it has “a” but why “stormy” ?


#16

@mattgamer so you are just comparing one character. We want to iterate through all 3 characters to ensure “m” “a” “c” in that order exists in a name.

stor m y - the m will cause it to be valid.

Currently your code will exit after validating any of the characters in the “mac” against the soldiers name. Make it return true only after checking if all three characters exist.

Look back to the first code you posted at the top of this thread. It has the original comments and requirements, just update the code with your new understanding of how characters, strings and arrays work so that it will return properly.


#17

you mean these lines?

if ( [i + j] != [j]) {
                break;
            }
 if ([j] == (j == lenWord - 1)) {
                return true;
            }

I still don’t find any sense in them, starting with the - 1


#18

@mattgamer When we look at if ( [i + j] != [j]) { it docent seem like correct syntax now that you know how to use arrays, right?

When we are dealing with arrays, any numeric equation can be substituted for the number. So for instance we can specify an array index as string[0] or string[ 1 - 1], as 1 - 1 = 0.

[i+j] doesn’t make much sense but if we look at the array that uses the i iterator and add that to it, things begin to take form.

also [j] by itself doesn’t make sense so we can add the array that uses j to it.

Just like where you created this statement if (string[i] == word[j]) { we want proper syntax for the arrays.

When you have corrected the syntax, read the statement again and tell me what the code is doing


Now for the code if ([j] == (j == lenWord - 1)) { this may or may not be correct.

re-read the instructions
// if [j]th is the last letter of the word (j == lenWord - 1), then return True.

We know that when we deal with arrays that we start with position 0, such as word[0] which gives us a “m” character. But the length of the string is 3. So to access the last character in the array we would subtract 1, right?

word[ lenWord - 1 ] is equivalent to writing   word[2]

Both statements would give you the “c” character.

 

Try adding this statement to your inner loop: hero.say("i = " + i + " j = " + j + " (i + j) = " + (i + j)); And watch the values of i and j change.

How can we tell when we have checked all 3 values of the word “mac” ? This is what that statement is trying to do.

Change the statement so that it will properly check when you have gotten to the last character


#19

I see the i and j values change, and I understood it this way:

knowing that the string is the soldier’s name and the word is mac, I see that the code is checking each character in the string ( i ) and seeing if that character is “m” then “a” then “c”

if one character matches then the string is passed, if no character matches then i increases (goes to the next character in the string) and the process repeats itself.

I also get that i + j returns a number equal to the sum of both indexes, but I don’t get what that number represents

in other words I don’t see the utility of adding the character position in the string with one of the characters of the word

( PS: I’m sorry that this is taking so long, but I swear that I’m doing my best in understanding)


#20

OMG I FINALLY DID IT!!!

for ( var i = 0; i < lenString; i++) {
        for (var j = 0; j < lenWord; j++) {
           
            if (string[i] == word[lenWord - 1]) {
                return true;
            }
        }
    }
    return false;
}

I wondered what would lenWord - 1 do, and it was correct!!!

but if that is the same as word[2] then it is only checking if i matches “c”, not the entire word.

I’m so happy but also still confused…

anyway @Harry_the_Wanderer , I couldn’t be more thankful for all your help ( and patience) :grinning: