Rock Paper Scissors

To solve this problem recursively, think of the game as a tree and you want to traverse all possible solutions depth-first.  That means going all the way down one branch first before moving back up to evaluate to the previous level(s).

Screen Shot 2017-12-10 at 6.19.47 PM

Recursive Solution:

var rockPaperScissors = function(n) {
  var output = [];
  var choices = ['rock', 'paper', 'scissors'];
  n = n || 3;

  var makeCombo = function(movesPlayed, roundsLeft) {
    if (roundsLeft === 0){
      output.push(movesPlayed);
    } else {
       for (var i = 0; i < choices.length; i++) {
         makeCombo(movesPlayed.concat(choices[i]), roundsLeft - 1);
       }
    }
  }
  makeCombo([], n);
  return output;
};

 

Order of execution: 

1. makeCombo([], 3)
2. makeCombo([‘rock’], 2);
3. makeCombo([‘rock rock’], 1);
4. makeCombo([‘rock paper’], 1);
5. makeCombo([‘rock scissors’], 1);
6. makeCombo([‘rock rock rock’], 0);
7. push [‘rock rock rock’] to output
8. makeCombo([‘rock rock paper’], 0);
9. push [‘rock rock paper’] to output
10. makeCombo([‘rock rock scissors’], 0);
11. push [‘rock rock scissors’] to output

12. makeCombo([‘paper’], 2);

..

…. makeCombo([‘scissors’], 2);

 

 

 

Advertisements
Rock Paper Scissors

‘this’ keyword

‘this’ in Global Context:

  • ‘this’ refers to window.

‘this‘ in a function call: 

  • In most cases, the value of a function’s ‘this’ argument is determined by how the function is called.  This means the ‘this’ value may be different each time a function is executed.
  • In “use strict” mode, ‘this’ is defaulted to undefined and prevents us from assigning values to undefined, and accidentally creating global variables.

‘this’ in a constructor call:

  • When a function is called as a constructor (with a ‘new’ keyword), a new object is created and set as the function’s ‘this’ argument.
function Person (firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

const person = new Person('John', 'Doe');

console.log(person) // { firstName: 'John', lastName: 'Doe' }

‘this’ in method call:

  • When a function is called as a method of an object, that function’s this argument is set to the object the method is called on. That object is called the receiver of the function call.
  • Oftentimes, the receiver gets lost when we invoke a method as a function. This happens particularly often when passing a method as a callback to another function. (example below – ‘this’ context is lost when assigning method to greet)
const person = {
   firstName: 'John',
   sayHi() {
     console.log(`Hi, my name is ${this.firstName}!`);
   }
};

const greet = person.sayHi;
greet(); // Hi, my name is undefined!
  • Use a wrapper function or .bind method to workaround this problem.

Specify ‘this’ using .call() or .apply():

  • Use .call and .apply to call a function with an explicit ‘this’ argument
  • .call accepts arguments as a comma separated list
  • .apply accepts arguments as an array.
const numbers = [10, 20, 30, 40, 50]

const slice1 = numbers.slice(1, 4); //syntactic sugar for the following:
const slice2 = numbers.slice.call(numbers, 1, 4);
const slice3 = numbers.slice.apply(numbers, [1, 4]);

// slice1, slice2, slice3 all return [20, 30, 40]

Hard-bind a function’s ‘this’ using .bind():

  • permanently tie a function’s ‘this’ argument to a specific value. It creates a new bound function that calls the original function with the provided ‘this’ argument, no matter how that bound function is called.
const person = {
   firstName: 'John',
   sayHi() {
     console.log(`Hi, may name is ${this.firstName}!`);
   }
};

const greet = person.sayHi.bind(person);
greet(); // Hi, my name is John!

const otherPerson = {
   firstName: 'Jane'
};

greet.call(otherPerson); // Hi, my name is John! 

‘this’ can be captured with an Arrow Function (ES6):

  • An arrow function doesn’t have its own ‘this’. Instead, it uses the ‘this’ value from its enclosing execution context.
  • When an arrow function is created, it permanently captures the surrounding ‘this’ value. Neither .call() nor .apply() can change the ‘this’ value later.
  • Arrow functions cannot be used as constructors.
  • Solves issues of incorrect ‘this’ values within callback functions.
const counter = {
  count: 0,
  incrementPeriodically() {
    setInterval(() => {
      console.log(this.count++);
    }, 1000);
  }
};

counter.incrementPeriodically();

// 'this' stays bound to counter object inside setInterval.

‘this’ in classes:

  • Within the constructor of a class, this refers to the newly created object.  Within a method, however, this might refer to another value if the method is called as an ordinary function. Just like any other object method, class methods can lose their intended receiver this way.
class Person {
  constructor (firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.sayHi = this.sayHi.bind(this);
  } 

  sayHi() {
    console.log(`Hi, my name is ${this.firstName}!`);
  } 
}

const person = new Person('John', 'Doe'); 
const greet = person.sayHi; 

greet(); // Hi, my name is John 
// ('this' is bound to object in constructor)
  • You can also bind this using class fields with arrow functions:
class Person {
  // class field with arrow function
  sayHi = () => {
    console.log(`Hi, my name is ${this.firstName}!`);
  } 
  constructor (firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  } 
}

const person = new Person('John', 'Doe'); 
const greet = person.sayHi; 

greet(); // Hi, my name is John 
// ('this' is bound to instance in class fields w/ arrow functions)

*Use a transpiler like Babel to translate ES6 into ES5 if using class fields and arrow functions.

‘this’ keyword

Pseudoclassical Subclassing

// Class constructor
var Class = function(arg1, arg2) {
  this.arg1 = arg1;
  this.arg2 = arg2;
};

Class.prototype.method = function () {
};
// Subclass constructor
var Subclass = function(arg1, arg2) {
  Class.call(this, arg1, arg2);
  this.arg1 = arg1;
  this.arg2 = arg2;
}

// Delegates fallback prototype to Class's prototype
Subclass.prototype = Object.create(Class.prototype);

// Reseting subclass's constructor since we just overrode it 
Subclass.prototype.constructor = Subclass; 

* Use Object.create to delegate to Class prototype allows us to make a copy of it rather than modifying Class’ prototype when we’re making changes to Subclass’ prototype.

Pseudoclassical Subclassing

Sprint: Subclass Dance Party

Takeaways (to be updated):

  • Always bind event handlers (because you will lose context of ‘this’).
  • If using pseudoclassical pattern, make sure it’s being used in all your files. (e.g. calling new dancerMakerFunction() in init.js).
  • Use jQuery plugins and libraries for special effects.
  • Debugging:
    • Use debugger; inside function to be tested
    • Trace the call stack
    • Hover over variables or code to see what they’re referencing
    • Remember to stop the debugger to execute the rest of the app
Sprint: Subclass Dance Party

Time Complexity

Time complexity:  describes how many operations does an algorithm has to do relative to its input size.

slow-growing: runtime does not increase with additional input
fast-growing: runtime increases super fast with additional inputScreen Shot 2017-12-03 at 8.28.33 PM.png
Learn.jpg

O(1):  An algorithm is run in constant time if it requires the same amount of time regardless of the input size.  (e.g. array lookup).

O(logN): runtime decreases at a rate that’s inversely proportional to input size.  (e.g. binary search – narrowing search).

O(n): runtime increases at a rate that’s directly proportional to the input size.  (e.g. a for loop to iterate over every item in an array).

O(n^2): runtime increases at an order of magnitude proportional to the square of its input size.  (e.g. two nested for loops)

O(c^n): With each step the function performs, it’s subsequent step will take longer by an order of magnitude equivalent to a factor of N. (e.g. guessing password combos)

Time Complexity

Data Modeling & Classes

Functional:

  • constructor and methods are wrapped in one class function.
  • return instance object at the end.
var ClassName = function {
  var newInstance = {};
  newInstance.methodOne = function () {
  };

  newInstance.methodTwo = function () {
  };

  return newInstance {}; 
};

Functional-shared:

  • Methods are stored in a separate object outside constructor function.
  • Uses extend to give instance access to method object.
  • Return newInstance inside constructor.
  • Methods uses ‘this’ keyword to reference newInstance.
var ClassName = function {
  var newInstance = {};
  extend(newInstance, classMethods);
  return newInstance; 
}

var classMethods = {};

classMethods.methodOne = function () {
};

classMethods.methodTwo = function () { 
}; 

Prototypal:

  • Methods are stored in a separate object outside constructor function.
  • Uses Object.create to access methods
  • Return newInstance inside constructor.
var ClassName = function {
  var newInstance = Object.create(classMethods);
  return newInstance; 
}

var classMethods = {};

classMethods.methodOne = function () {
};

classMethods.methodTwo = function () { 
}; 

Pseudoclassical (most commonly used pattern)

  • Uses the ‘new’ keyword to instantiate a new instance.  (‘new’ replaces the need for Object.create and returning the object inside the constructor).
  • Methods stored on the prototype chain.
  • Uses ‘this’ keyword to reference the new instance.
var ClassName = function {
  this.length = 5;
}

ClassName.prototype.methodOne = function () {
};

ClassName.prototype.methodTwo = function () { 
};

var newInstance = new Class();

 

Data Modeling & Classes

Partial Application (Currying)

Partial Application or currying is a feature that Function.prototype.bind offers.

This allows you to pass in some arguments at bind time and get back a new function that invokes the original function using the rest of the arguments at call time.

For example:

Screen Shot 2017-12-01 at 8.02.50 PM

  • null is passed in because we’re not working with ‘this’ in this example.
  • the first argument we pass in at bind time is 2.
  • doubler is the function we get back after binding.
  • every invocation of doubler is invoking the original multiply function with the remaining arguments.

 

Partial Application (Currying)