Project Rock, P, S 'userChoice is not a function'


#1

I tried to write the Rock, Paper Scissors game like this - code below - but I get the error:
"userChoice is not a function". I think it goes wrong in my second function, but I don't see it right now.
Can anybody help or explain what I did wrong?

function getUserChoice(){
  userInput = prompt('Rock, Paper, or Scissors?');
  userInput = userInput.toLowerCase();;
  if(userInput === 'rock' || userInput === 'paper' || userInput === 'scissors' ){
    return userInput;
  } else {
    console.log('This is not correct');
  }
}

function getComputerChoice(){
  var randomNumber = Math.floor(Math.random()*7);
  switch (randomNumber){
    case 0:
      return 'rock';
    case 1:
      return 'paper';
    case 2:
      return 'scissors';
                      }
}
function determineWinner(userChoice, computerChoice){
  if(userChoice === computerchoice){
    return "The game is a tie";
  }
  if(userChoice === 'rock'){
    if(computerChoice === 'paper'){
      return 'The computer won!';
    } else{
      return 'You won!';
    }
  }
  if(userChoice === 'paper'){
    if(computerChoice === 'scissors' || computerChoice === 'rock'){
      return 'The computer won!';
    } else {
      return 'You win!';
    }
  }
  if(userChoice === 'scissors'){
    if(computerChoice === 'rock' || computerChoice === 'scissors'){
      return 'The computer won!';
    }
    else {
      return 'You won!';
    }
  }
  if(userChoice === 'bomb'){
    return 'You won!';
  }
}

function playGame(){
  var userChoice = getUserChoice();
  var computerChoice = getComputerChoice();
  console.log('You have thrown: ' + userChoice());
  console.log('The computer has thrown: ' + computerChoice);
  console.log(determineWinner(userChoice, computerChoice));
}

playGame();

Thanks a lot in advance!


#2

Remove the () since it makes the variable look like a function call.


#3

Sorry if this if getting a bit long, please have a look on my comment inside your code with "//"

I will go through the code with what error I found.

1.

Firstly your code resulted "TypeError: userChoice is not a function". The answer is provided by @mtf

console.log('You have thrown: ' + userChoice());

Explanation:
You have assigned var userChoice = getUserChoice(); therefore, userChoice, the variable itself is already a function without the bracket ().

When you call for userChoice(), the computer search for userChoice(), but you have not defined a userChoice function.

userChoice is a variable with getUserChoice() function inside it. Thus, calling userChoice will call getUserChoice() function. But calling userChoice() will result to userChoice is not a function, as the computer call for userChoice() (which you have not defined), instead of calling the variable userChoice.

You might have already understood this, cause I can see you make this right at the following code. This is correct:

console.log('The computer has thrown: ' + computerChoice);

2.

After changing userChoice, you will get this error:
ReferenceError: computerchoice is not defined

Check the code, do you have any var computerchoice not defined?
I think you know computerchoice and computerChoice are two different variables, maybe it's just a typo error which lead to this reference error.

Here:

  if(userChoice === computerchoice){  // <=== is the code correct?
    return "The game is a tie";
  }

By changing that, your reference error will go away.

3.

When testing your game, you will notice a few times the computer will throw out undefined occasionally, regardless of 'paper' or 'rock' or 'scissors'.

Example:

You have thrown: paper
The computer has thrown: undefined
You win!

Why when computer goes undefined, you win? Because it is coded inside your code,

if(userChoice === 'paper'){:wink:
    if(computerChoice === 'scissors' || computerChoice === 'rock'){
      return 'The computer won!';
    } else {
      return 'You win!';
    }
  }

When computerChoice is undefined, it does not match the if statement, thus leading to the else statement, which is return 'You win!'; Therefore everytime when computer throws out undefined in the console, player will always win. :wink:

HOW to check why computer throws out undefined?
First, we know:
console.log('The computer has thrown: ' + computerChoice);

Therefore something must be wrong with computerChoice, and
var computerChoice = getComputerChoice();

So nothing wrong with computerChoice, since it's just a variable, something must be wrong withgetComputerChoice()

Now we go through the getComputerChoice() function:

function getComputerChoice(){
  var randomNumber = Math.floor(Math.random()*7);  //<=== why multiply with 7?
  switch (randomNumber){
    case 0:
      return 'rock';
    case 1:
      return 'paper';
    case 2:
      return 'scissors';
    }
}

My question, why multiply with 7? (I hope you can explain your idea to me)
To avoid undefined, there is THREE ways to solve this:

  • A) State the cases for the rest possibilities
    You only have 3 choices for computer, (rock paper scissors) so you would only want the number generated is between 0 to 2 (0, 1, 2), just like your switch statement's case. But when you multiply with 7, what is the case for computer if it gets 3,4,5,6 (7 not included because of the Math.floor() function)
    • That is why the computer throws out undefined. Because when it rolled a 6, it could not find any switch case for it to refer, thus underfined.

    • My 2cents Note: by having 0 to 6 cases in switch for computer (total 7 choices), do you think it is fair to either computer or player?_
  • B) Change the multiply number for Math.random()
    I think this is self explanatory.

  • C) Lower the multiply number and also write a default case for the switch statement.
    (if the computer rolled >3, what would you like it to say)
    Example:

      default:
        return 'Computer is getting tired, it surrenders';
    }
    • Thus, it will lead to the very end, the player will get "You win!" if you follow your code's logic.

4.

I think most errors are gone now. But I still have a few observations to mention here.

An extra ;

function getUserChoice(){
  userInput = prompt('Rock, Paper, or Scissors?');
  userInput = userInput.toLowerCase();;   //<=== Check here
  if(userInput === 'rock' || userInput === 'paper' || userInput === 'scissors' ){
    return userInput;
  } else {
    console.log('This is not correct');
  }
}

The syntax:

function determineWinner(userChoice, computerChoice){
  if(userChoice === computerchoice){
    return ......;
  }
  if(userChoice === 'rock'){
    if(computerChoice === 'paper'){
      return .......;
    } else{
      return .......;
    }
  }
  if(userChoice === 'paper'){
    if(computerChoice === 'scissors' || computerChoice === 'rock'){
      return ........;
    } else {
      return .......;
    }
  }
  if(userChoice === 'scissors'){
    if(computerChoice === 'rock' || computerChoice === 'scissors'){
      return ........;
    }
    else {
      return.........;
    }
  }
  if(userChoice === 'bomb'){
    return ........;
  }
}

I have learnt you can have:

if ( ) {
    if ( ) {
        return ...;
    } else {
        return ...;
    }
}

But your code: if () then if () then if () etc... I don't know if this is correct in JS? anyone? @mtf, I think the syntax is not meant to be that way, and it just shows how lenient JS will let you go.

if ( ) {
    return ....;
}
if ( ) {
    if ( ) {
        return ...;
    } else {
        return ...;
    }
}
if ( ) {
    if ( ) {
        return ...;
    } else {
        return ...;
    }
}

I think it should be if(), then else if(), then else if (), else ....

if ( ) {
    return ....;
}
else if ( ) {
    if ( ) {
        return ...;
    } else {
        return ...;
    }
}
else if ( ){
    if ( ) {
        return ...;
    } else {
        return ...;
    }
} 
else {
    return ....;
}

Last one, if you're expecting player to input 'bomb', what I noticed from your code:

  if(userChoice === 'bomb'){
    return 'You won!';
  }
}

Remember to add another condition inside:

  if(userInput === 'rock' || userInput === 'paper' || userInput === 'scissors' ){
    return userInput;
  } else {
    console.log('This is not correct');
  }

If you don't, when player input 'bomb', it will console "This is not correct".


Sorry for really tl;dr, just want to share my observations and I really like how you try to create different situations to improve the game! Such as:

  • userInput.toLowerCase();
  • if user type other than what expected, a console.log will tell them input not correct.
  • using a switch statement replacing if/else statements. A variation of code style.

Keep it up! Cheers! :smiley:


#4

Thank you for your answer @mtf ! This solved indeed my userChoice problem.
Greetings by Sophie


#5

Wow thank you @codexthon for your extended explanation about my errors! I have learned a lot thanks to this :).
Indeed a typo with the computerChoice but nice how you explained the undefined problem as well.
I think I just had put 7 because 7 is a nice number, but it should stand indeed for the maximum of choices you have. I did not realize that yet.
And indeed, but should you always add a default case to a switch statement? I have read somewhere that it is a good thing to do but i was wondering if it's like a undefined must or something, to tackle everything that is not mentioned in one of the cases.
And true it should be else if's!

Thanks a lot again for your clear explanation!

Greetings by Sophie


#6

@sophievda, you're welcome :slightly_smiling_face: Hope you're not disturbed with my too-long-to-read-reply.

Like I said, I enjoyed reading your new codes, that is why I tested it out and soon to realize the undefined situation. So 7 must be your lucky number then? :wink:

Yes or No, depending on how you want your game to be. If you want it simple, you can make it only 3 choices. But seeing your 'bomb' idea, I could suggest maybe give it 5 choices? It could be fun to see computer throwing bomb and getting tired & surrender. :smile:

so it would be:
var randomNumber = Math.floor(Math.random()*5);

continue from case 2

case 3:
      return 'Computer throws a bomb! You\'re blasted! You lose!';
    }

  default:
      return 'Computer is getting tired, it surrenders';
}

Output number 0 to 4, a total of 5 choices. Case 0 to 3 plus one default

But if you decide to add the case 3, you need to remember to add another if condition under the functiondetermineWinner() function, if not when computer throws a bomb, the player still get You win!

I don't think it is necessary to have default case. Only when you want to to tackle everything that is not mentioned in one of the cases, just like what you mentioned. If you have all cases considered, I don't think default case is needed.

This, I am a bit unsure. But usually what I see is if() { }/else if() { } / else { } syntax, or
if () { if () { if() { } } }, nested if statement, not sure about if () { } if () { } if { } , maybe JS will interpret it as 3 separated if statements and it is considered correct, but I'm still unsure what complication it has versus using if/else if .

Maybe others can help explaining this part. :slightly_smiling_face:


#7

Not so, The variable is a value, the return value of the function call expression. It is not a function and would only be one if it was written as,

variable = some_function

where some_function is actually a function. The above would be a reference to the function, and itself invocable...

variable()

Variables are not containers (though at a learning level we told to think of them as containers). They are actually only pointers to the address in memory where the data resides. Memory is the container.

Again, in, variable = some_function(), variable references only the return value or object.

for the reason that JS, as with most programming languages, is case sensitive.

There are only two instances where undefined will surface...

  1. var some_variable;
  2. return

In the first case, we have declared the variable so have reserved a slot for it in the namespace. As yet it has no defined value, hence, undefined.

console.log(some_variable)    // undefined

In the second case, when a function terminates with no return or no return value, the caller gets undefined.

    function foo() {
        // some code
    }
    function bar() {
        // some code
        return;
    }

    console.log(foo())    // undefined
    console.log(bar())    // undefined

If this results in a win for the user, then there is a flaw in the logic.

Recall also, that console.log() does not return anything.

    console.log(console.log())    // undefined

We see this occasionally when a function does some printing and the call expression itself is logged, as well.

    function foo(bar) {
        console.log(bar);
    }
    console.log(foo('some text'));

    // some text
    // undefined

Concerning switch... This is a useful tool when there are multiple possible states and if..else if..else is put to extremes with all the cases. When the number of cases is small (say two or three conditionals) the if statement is more than adequate.

As for the computerChoice, it could be written as one compact pure function with no conditionals...

return ['rock', 'paper', 'scissors'][Math.floor(Math.random() * 3)];

For this exercise that is how the course author intended it. But in the case of return we don't need any else if.

if () {
    return 
}
if () {
    if () {
        return
    } else {
        return
    }
}
if ...

The above is perfectly legitimate since return has carried away some value and it is no longer trapped in the flow. We only really need else if when the value is still in the flow.

function foo() {
    if () {
        a = ...
    }
    else if () {
        a = ...
    } else {
        a = ...
    }
    return a;

#9

Thanks for the clear clarification @mtf, so the correct chain of thought will be:
When we input a variable referenced with a function name inside a console.log, the computer will call for the variable, thus return the value assigned for the variable. The return value is the output of the referenced function for the variable residing in the memory?

I appreciate how you take time to explain for my statement:

the variable itself is already a function without the bracket ().

I was thinking to explain it mathematically as in X = Y + 5, I would assumed X can be directly replacing Y+5. So when var A = B() , I thought A could be replacing B(). But that doesn't seem to be that case, function is the relationship assigned, B() is pointed to A. Hope I got this right this time.

My question though, when a function is defined through
var getUserChoice = function () { }; This makes getUserChoice a variable.

How about function getUserChoice () { } ? without declaring a variable, does getUserChoice still considered a variable?

I will try to read more on referencing a variable function to another variable.

Just a curious note:
I tried changing the code at the very end by referencing var startGame with playGame() function as follows:
var startGame = playGame();
without calling startGame, the game would still work. Any insight?

Thank you for the undefined instances. It's a good read. Correct me if I'm wrong.
Since, I found all the if's, there's no condition for when computerChoice === undefined

therefore the code will run the else statement, which is

} else {
      return 'You win!';
}

leading player wins everytime the computer throws an undefined,

@sophievda, I just noticed this, sorry. Why do you have the || scenario like below?

  if(userChoice === 'paper'){
    if(computerChoice === 'scissors' || computerChoice === 'rock'){
      return 'The computer won!';

And

if(userChoice === 'scissors'){
    if(computerChoice === 'rock' || computerChoice === 'scissors'){
      return 'The computer won!';

I think the condition after || (the second condition) could be deleted.

Noted.

Ah, okay, since there are return in every if statement it serves like a different exit nodes, unless we wanted just a single return at the very end.

So the code could be using if() if () if () such as:

function foo() {
    if () {
         a = ...
         return a;
    }
    if () {
        a = ...
        return a;
    } else {
        a = ...
        return a;
    }
    if () {
         b = ...
         return b;
    }

Thank you for taking the time to share your insight upon this matter @mtf. Greatly appreciated! :smile:


#10

Not quite sure what this question is asking, but will take a stab at it...

  • When we pass a value to the console.log() method, the value gets logged directly and nothing else happens.

  • When we pass a function call expression to the console.log() method, the call is invoked, first, and any return value sent back by that function is then logged. Again, nothing else happens after that.

  • When we call a function and assign the return value to a variable, then, and only then is the value stored in memory. We can process some more with it, pass it to other functions or log it out. After logging, the varable is still an active reference to the value it held.

Consider,

y = x + 5

The above expresses x + 5 as a function. More implicitly,

y = f(x) where f(x) = x + 5

We would say that y is a function of x. x in this case is the parameter of the function, the independent variable, and y is dependent variable.

Program functions are analogous.

Only transiently. Once B() has been invoked and returns a value, A only sees that value, not the function that returned it. B() is not aware of A, only the parameter, if one exists locally in function scope, such as in,

A = B(x)

which depends upon B having a parameter to receive x.

function B(param) {}

Remember the parameter list in this expression...

var getUserChoice = function () {};

In the above getUserChoice is a reference, and the expression on the right of the equals is an anonymous function. Only the variable is hoisted at runtime, not the function expression. If we check the type for the variable, it will come back as a function, but that is slightly misleading. It only references a function. In JS, with loose types we are free to assign any new value to the variable.

function getUserChoice () { }

is a function declaration that gets hoisted to the top of its scope at runtime. It should be treated as immutable (though that may not necessarily be the case) and the function name should never be re-used in the code. This type is function meant to be more permanent (as the session is concerned).

*All this is doing is creating a reference to the function. Both work the same since they both are essentially the same function. *

startGame
// invokes
playGame()

Correction: (The above (*) is wrong; don 't know what I was thinking.)

startGame sees the return value of playGame() and since that function has no return value there is nothing loggable, only undefined. It works because the function is invoked at time of assignment.

We would never write such a condition. undefined is not an object, but an outcome.

To test for undefined, rely upon its falsiness...

if (computerChoice)

will be false if computerChoice is not defined, and true if it has a non-zero value, a string value (that is not the empty string) or is a reference object such as an array or object. A boolean value would be moot.

Generally speaking, this is correct. For this game, though, we still need the nested if's.


#11

Consider the above. Will the last conditional ever be reached?

Concerning, else, do we need it if we are using return?

if () return a;
return b;

#12

Ah, thank you for explaining it through 3 stages making it easily comprehensible.

Just noticed my careless syntax mistake. Edited it.

This paragraph explains my doubts on why variable is just a variable referencing a function var name = function () { } vs function name () { } , as we were taught initially to declare a variable with function in JS course, we were explained that the variable is a function, to quote this, I traced back to the old JS course:

https://www.codecademy.com/courses/javascript-beginner-en-6LzGd/0/4

Let’s break down exactly how a computer thinks when it sees the code for a function.

 var functionName = function( ) {
     // code code code
     // code code code
     // (more lines of code)
 };

The var keyword declares a variable named functionName.
The keyword function tells the computer that functionName is a function and not something else.
Parameters go in the parentheses. The computer will look out for it in the code block.
The code block is the reusable code that is between the curly brackets { }. Each line of code inside { } must end with a semi-colon.
The entire function ends with a semi-colon.

By then, I would think that variable is like a container, having variable name as the label of the container, with the working function inside it (something to imagine in real things). Definitely not blaming, it could be my own misinterpretations OR I totally understand if the author want to make it simple for beginners. Just like how we were not taught about negative numbers in Maths during pre-school/ early primary school, something are meant to be clarified at later stage with higher comprehension level.

Here, I tend to differ on this part,

Based on the code:

function playGame(){
  var userChoice = getUserChoice();
  var computerChoice = getComputerChoice();
  console.log('You have thrown: ' + userChoice);
  console.log('The computer has thrown: ' + computerChoice);
  console.log(determineWinner(userChoice, computerChoice));
}

Yes, playGame() does not has any return value inside it. But it contains var userChoice in the console.log and the var computerChoice in the console.log where function getUserChoice() and getComputerChoice() are invoked and assigned return values to both aforementioned variables. Then, both of the returned value were inserted as arguments in the determineWinner() function, which still contains several nodes of return values.

Understood. I tested it out and with your explanation, I understand the mechanism more now.

var A = function () {
    console.log('test A');
};

var B = A;

Produce nothing in the console. Because A() is not called, nor B() is not called. In current state, A is only assigned to B with reference to a function.

var A = function () {
    console.log('test A');
};

var B = A;

B();

Produces the expected console as B() function is called.

var A = function () {
    console.log('test A');
};

var B = A();

The function is invoked at time of assignment. Just like you said.

Thanks for the clarification, frankly, I would never intend to put undefined as a condition in my code, just wrote the sentence that way of when computerChoice --> lead to undefined. I would avoid using undefined in my code as it is confusing now to me, and I would not know what category it be, what type of data, how to write it into code without not misinterpret it.

The most I can think of a way to use it if, there is a situation where I want it to be evaluated like this:

  if(user === computer){  //user resulted undefined as well as computer resulted undefined
    return "The code detects error in this game, please restart";

Upon researching further,

The example it showed:

var x;
if (x === undefined) {
   // these statements execute
}
else {
   // these statements do not execute
}

I guess it probably works. Maybe computerChoice === undefined will work. (unsure)


For discussion purpose, I have edited @sophievda’s code:

Full edited code
function getUserChoice(){
  userInput = prompt('Rock, Paper, or Scissors?');
  userInput = userInput.toLowerCase();
  if(userInput === 'rock' || userInput === 'paper' || userInput === 'scissors' ){
    return userInput;
  } else {
    console.log('This is not correct');
  }
}

function getComputerChoice(){
  var randomNumber = Math.floor(Math.random()*7);
  switch (randomNumber){
    case 0:
      return 'rock';
    case 1:
      return 'paper';
    case 2:
      return 'scissors';
                      }
}
function determineWinner(userp, computerp){
  if(userp === computerp){
    return "The game is a tie";
  }
  if(userp === 'rock'){
    if(computerp === 'paper'){
      return 'The computer won!';
    } else{
      return 'You won!';
    }
  }
  if(userp === 'paper'){
    if(computerp === 'scissors'){
      return 'The computer won!';
    } else {
      return 'You win!';
    }
  }
  if(userp === 'scissors'){
    if(computerp === 'rock'){
      return 'The computer won!';
    }
    else {
      return 'You won!';
    }
  }
  if(userp === 'bomb'){
    return 'You won!';
  }
}

function playGame(){
  var userChoice = getUserChoice();
  var computerChoice = getComputerChoice();
  console.log('You have thrown: ' + userChoice);
  console.log('The computer has thrown: ' + computerChoice);
  console.log(determineWinner(userChoice, computerChoice));
}

playGame();

Edits:

  • edited the userChoice()
  • edited parameter computerchoice error, changing it to computerChoice [<== this parameter is gone as I changed its name to computerp]
  • leaving the switch case as it is, which is only case 2 without a default
  • deleted the extra ;
  • deleted the extra second conditions after the || in
if(userChoice === 'paper'){
    if(computerChoice === 'scissors' || computerChoice === 'rock'){

if(userChoice === 'scissors'){
    if(computerChoice === 'rock' || computerChoice === 'scissors'){
  • leaving the if statements as it is, without using if/elseif
  • Change the all parameter above in function into determineWinner(userp, computerp), so it won’t be too confusing with the var userChoice, var computerChoice created.

You can see:

  1. By clicking OK at the prompt without typing [resulted undefined]
  2. The console.log will print You have thrown: undefined.
  3. The undefined is the result in console.log where userChoice referenced with the getUserChoice() function invoked and return undefined as there is no return value except for if (userInput === 'rock' || userInput === 'paper' || userInput === 'scissors' )
  4. Repeat step 1-3 a few times until you get, The computer has thrown: undefined, because of the incomplete cases for switch statement
  5. You will see the console.log printed The game is a tie, because userChoice and computerChoice both arguments equals undefined, thus it fulfills the parameter condition
  if(userp === computerp){
    return "The game is a tie";

Full console print:

This is not correct
You have thrown: undefined
The computer has thrown: undefined
The game is a tie

6. If you repeat step 1-3 a few times, you will also notice computer has thrown something, but you don’t win or loose:

This is not correct
You have thrown: undefined
The computer has thrown: rock    // <--- rock
undefined
This is not correct
You have thrown: undefined
The computer has thrown: paper  // <--- paper
undefined
This is not correct
You have thrown: undefined
The computer has thrown: scissors // <--- scissors
undefined

The last line all ended undefined because there is no return value for determineWinner() function if userp ---> leads to undefined

7. If you enter same choice ‘paper’ or ‘rock’ or ‘scissors’ several times, you occasionally will get The computer has thrown: undefined as the switch cases are not all described. But you will still win because if computer thrown undefined, it would fall into else category. Thus, you won!.

  if(userp === 'paper'){
    if(computerp === 'scissors'){
      return 'The computer won!';
    } else {
      return 'You won!';
    }

The console will print:

You have thrown: paper
The computer has thrown: undefined
You won!

Therefore, I come to conclusion, solving the switch cases so that computer won’t throw undefined, will in the end solve the problems of the situation above, except for very first scenario of user click “OK” without typing anything, and that will be some additional code for some other time.

Thank you for taking time to share your in-depth thoughts @mtf. I too learn a lot. :smiley:


#13

The name is arbitrary. We start out with a namespace, and everything we create gets added to all the stuff that is there already. The interpreter knows how to locate everything. Names are labels, yes, but they are also pointers. They point to where in memory the data is. Labels are references to locations, variables are references to objects. We think in terms of objects, the computer acts in terms of locations. Hence, Abstract Program Interface, API.

function foo() {}

When the page loads and this script is parsed in, this function is hoisted to the top of global scope. Each function will have been parsed for arguments and code body. An arguments list will be standing by, ready to receive calls.

On the second pass (JS boot takes two passes) function expressions in the code will only ever be parsed if they are referred to in a call expression. If it never gets called, it never gets parsed. In a larger framework this is more than a distinct possibility but you want the code there, on the off chance there is a need for it.

That is why we hide code from the boot process using function expressions. Those that are declared are taking up memory, and always will. Expressions give back the memory they use once they are exited. They go back to being strings in the source code.


#14

As I earlier stated, and regardless that it is a primitive, it is falsy and that is all we need to focus upon. undefined is a primitive because it is not an object. It is the only primitive (besides null) that can never be evaluated as true simply because it represents something that does not exist. It can never be a value since it represents the absence of a defined value. In other words,

var x;
if (x) {
    // this statement will never execute
} else {
    console.log("`x` is not defined");
}

It is never necessary to compare to undefined. null on the other hand can be a value; but,that is another story.


#15

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.