Functions within a function


#1


Click Here for exercise!

I tried to use a function to keep all variables inside two functions local in order to maintain each value and allow the player and dragon take turns attacking each other until the first falls and even though to me I see no flaws, my end result after running the script is blank. I get no errors but I also do not get my planned result.


var slaying = true;
youHit = Math.floor(Math.random() * 2);
damageAdd = Math.floor(Math.random() * 5 + 1);
totalDamage = 0;
dragonHit = Math.floor(Math.random()*2);
dragonAdd = Math.floor(Math.random()*5 + 1);
totalDragon = 0;




var letsPlay = function(){  //Function to start game//
    var playerTurn = function(){    //Function of Players combat turn//
        while(slaying){
            if(youHit === 1){
                console.log("Direct hit!");
                totalDamage += damageAdd;
                if(totalDamage >= 4){
                    console.log("Player defeated the dragon!");
                    slaying = false;
                    }
                    else{
                        console.log("It's not over yet!");
                        toggleDragon(); //Function call to Dragons combat turn//
                    }
            }
        }
    }
        
var toggleDragon = function(){  //Function of Dragons combat turn//
    while(slaying){
        if(dragonHit ===1){
            console.log("The dragon burns you!");
            totalDragon += dragonAdd;
            if(totalDragon >= 4){
                console.log("The dragon melted you like butter!");
                slaying = false;
                }
                else{
                    console.log("The dragon grazes you!");
                    playerTurn();   //Function call to Players combat turn//
                    }
        }
    }
}
}


letsPlay()  //Function call to start game//.


#2

All your function letsPlay does is to create two other functions and exit, nothing else happens, those two functions are not called.

After it has created those functions, perhaps it should do something with them? Return them?

If you mean to create a closure then you'll need to create variables that are local to letsPlay (and make sure they are created with var statements, otherwise they become global anyway)

function createGame() {
    game variables local to this function
    
    create functions to control the game

    return functions  // perhaps create an object to contain references to them
}

create a game
call functions appropriately

If that's not the design pattern you were going for, forget the code structure I suggested above, that was just my interpretation of what you were doing. That's not exactly the most straight-forward way of doing it but it's certainly valid and a nice way to better understand functions and closures. Closures are one of few ways to create truly private fields of an object in JS.


#3

I'm glad you understood what I was going for on this exercise! I took what you said to heart, did some research on objects, recursions, closures and decided to rewrite my whole script adding a few things I learned in the process. The game works now, but I am left with new questions I couldn't find elsewhere. My first concerns another issue with my game, I noticed when the function is being recalled the original variables of my Math.random() become fixed until the end which led to an infinite loop issue in my game until I spotted it by adding a console.log to show me the values when being called, is there a method I could use in my new script that will produce new 'rolls' on my Math.random() variables each time they are called? My next question is, I noticed you said "straight-forward", could you show me how you would condense what I have into a more straight-forward way so it is more manageable and byte friendly? Sorry to ask this much of you, I appreciate all of it!
Here is my new script:


//Object Player
var player = {
    damage : Math.floor(Math.random() * 5 + 1),
    hit : Math.floor(Math.random()*2),
    health: 5,
};

//Object Dragon
var dragon = {
    damage: Math.floor(Math.random()*5 + 1),
    hit: Math.floor(Math.random()*2),
    health: 5,
};


//Battle Function
var battleDragon = function(){
    
      //Determines end of battle
    var victory = true;
    
    //Players turn Function
    var playerTurn = function(){
        if(player.hit >= 1){
            //Attack hits target Function
            console.log("you hit the dragon")
            return playerHit()
            }
        else{
            console.log("you miss");
            dragonTurn();
            }
        }
        
        //Dragons turn Function
    var dragonTurn = function(){
        if(dragon.hit >= 1){
            //Dragon hits player Function
            console.log("the dragon hits you")
            return dragonHit()
            }
            //I discovered since my variables are fixed if both miss, an infinite loop     happens here so this line is to prevent that until I learn more.//
        else if(dragon.hit,player.hit == 0){
            console.log("both missed and Ive prevented an inf loop");
        }
        //End of infinite loop prevention
        else{
            console.log("the dragon misses")
            playerTurn();
            }
        }
   
    //When player hits Function
    var playerHit = function(){
         dragon.health -= player.damage;
            //Attack kills target
            if(dragon.health <= 0){
                console.log("congrats you killed the dragon!");
                var victory = false;
            }
            //Attack does not kill target
            else{
                console.log("but it wasn't enough to kill the dragon");
                console.log(dragon);
                dragonTurn();
            }
    }
    //When dragon hits player Function
    var dragonHit = function(){
        //Dragon attacks
            player.health -= dragon.damage;
            //Attack kills player
            if(player.health <= 0){
                console.log("The dragon destroyed you!");
                var victory = false;
            }
            else{
                console.log("It hurt but now you can counter!");
                console.log(player);
                playerTurn();
            }
    }
    
    //Health
    
    //Battle loop
    return playerTurn();
    
}
battleDragon()


#4

Math.random returns a float, if you need another float you'll need to call it again. You could have a property that returns a random number each time it is read by using a getter (google mdn getter) but you could also just make it a function to the same effect.

The direction I was going in was to create an object that represents the state of the game that has functions that can be called to progress and that the internals would be stored in a closure.

What you have isn't a closure, you're just creating local variables and a few functions, calling those functions and then exiting without a return value, or at least not a meaningful one or one that comes with a closure.

A closure is a scope that is kept alive even after execution has exited it:

function f() {
    var x = 5;
    function g() {
        return x;
    }
    return g;
}

f()();  // returns 5

In the code above, f returns another function. And then that function is called (by this time f has been exited). That function is keeping the scope around it, so when it is called, it is still able to return x which is 5.

More straight-forward would be to just have a loop and split it up a bit into functions for readability's sake, just repeat turn until somebody wins.

Your comments are super-redundant, they aren't adding or clarifying anything!

//Players turn Function
var playerTurn = function(){

Comments are useful for explaining concepts, adding information needed to understand something, explaining intentions of non-obvious code.

You might want to see what this condition of yours evaluates to for different values of dragon/player.hit
(dragon.hit,player.hit == 0)
Because that doesn't do what I think you think it does.


#5

Whew! Thanks for your help, I didn't realize I could create functions within objects! I also now understand that I misinterpreted what a closure was as it is not just a function with local variables. I condensed my code into a more organized manner and used a while loop to help keep it neat. I have no errors with the new code and my end result was what I wanted :slight_smile: Thanks for all your help!
Here is my new script for anyone who wanted to see the final changes made and compare to the previous posts:


var player = {
    damage :function() {var x = Math.floor(Math.random() * 5 + 1); return x;},
    hit : function() {var x = Math.floor(Math.random()*2); return x;},
    health: 5,
};
var dragon = {
    damage: function() {var x = Math.floor(Math.random()*5 + 1); return x;},
    hit: function() {var x = Math.floor(Math.random()*2); return x;},
    health: 5,
};
var battleDragon = function(){    
    var playerTurn =function(){
        if(player.hit()>=1){
            dragon.health-=player.damage();
            console.log("player hits");
            }
            else{
                console.log("player missed, don't give up!");
            }
        }
    var dragonTurn =function(){
         if(dragon.hit()>=1){
             player.health-=dragon.damage();
             console.log("dragon hits");
             }
             else{
                 console.log("dragon missed, turn the battle around!");
             }
        }
    while(player.health>=1 && dragon.health>=1){
        playerTurn();
        if(dragon.health<=0){
            break;
        }
        dragonTurn();
    }
    if(player.health<=0){
        console.log("dragon wins");
    }
    else if(dragon.health<=0){
        console.log("player wins");
    }
}
battleDragon()