+
+
+
\ No newline at end of file
diff --git a/jquery_exercise/inserthtml.com.custom-checkbox-radio-2.zip b/jquery_exercise/inserthtml.com.custom-checkbox-radio-2.zip
new file mode 100644
index 00000000..f70655d2
Binary files /dev/null and b/jquery_exercise/inserthtml.com.custom-checkbox-radio-2.zip differ
diff --git a/lodash_exercise/lodash.js b/lodash_exercise/lodash.js
index 483d734a..5307ed03 100644
--- a/lodash_exercise/lodash.js
+++ b/lodash_exercise/lodash.js
@@ -1,85 +1,366 @@
-function drop(){
-
+// Create a slice of array with n elements dropped from the beginning
+// Starts at the element just past the dropped elements, adds to a temp array and returns when loop is done
+// This could also be implemented with slice but where is the fun in that?
+function drop(arr, num=1){
+ var temp = [];
+ if (num > arr.length) return temp;
+ for (var i = num; i < arr.length; i++) {
+ temp.push(arr[i]);
+ }
+ return temp;
}
-function fromPairs(){
-
+// Example inputs / outputs
+console.log(drop([1,2,3,4,5,6], 2)); // [3,4,5,6]
+
+// Changes an array of key-value pairs (represented as arrays, themselves) into an object and returns the object
+// Loop through the array, creating a key for the object from sub-array[0] and passing the value from sub-array[1]
+function fromPairs(arr){
+ var temp = {};
+ for (var i = 0; i < arr.length; i++) {
+ temp[arr[i][0]] = arr[i][1];
+ }
+ return temp;
}
-function head(){
+// Example inputs / outputs
+console.log(fromPairs([['a', 1], ['b', 2]])); // {'a': 1, 'b': 2}
+// Returns the first element of an array
+function head(arr){
+ return arr[0];
}
-function take(){
-
+console.log(head([1,2,3])); // 1
+console.log(head([])); // undefined;
+
+// Creates a slice of array with n elements taken from the beginning.
+// Start at the beginning, loop through add n elements to a temp array and return it.
+function take(arr, num=1){
+ var temp = [];
+ if (num > arr.length) return arr;
+ for (var i = 0; i < num; i++) {
+ temp.push(arr[i]);
+ }
+ return temp;
}
-function takeRight(){
+console.log(take([1, 2, 3]));// => [1]
+console.log(take([1, 2, 3], 2));// => [1, 2]
+console.log(take([1, 2, 3], 5));// => [1, 2, 3]
+
+// Creates a slice of array with n elements taken from the end.
+// Start at the length-1 - num, loop through add n elements to a temp array and return it.
+function takeRight(arr, num=1){
+ var temp = [];
+ if (num > arr.length) return arr;
+ for (var i = arr.length-num; i < arr.length; i++) {
+ temp.push(arr[i]);
+ }
+ return temp;
}
+console.log("takeRight: " + takeRight([1, 2, 3]));// => [3]
+console.log("takeRight: " + takeRight([1, 2, 3], 2));// => [2, 3]
+console.log("takeRight: " + takeRight([1, 2, 3], 5));// => [1, 2, 3]
+
+// Creates an array of unique values, in order, from all given arrays using SameValueZero for equality comparisons.
+// Create a lookup object. create a key for all found elements and push element into array, as long as the key didn't
+// previously exist
function union(){
+ var lookup = {}
+ var temp = [];
+ var newArr = [];
-}
+ if (arguments.length === 0) return newArr;
+ if (arguments.length === 1) return arguments[0];
-function zipObject(){
+ for (var i = 0; i < arguments.length; i++) {
+ temp = temp.concat(arguments[i]);
+ }
+ for (var j = 0; j < temp.length; j++) {
+ if (temp[j] in lookup === false) {
+ lookup[temp[j]] = true;
+ newArr.push(temp[j]);
+ }
+ }
+
+ return newArr;
}
-function includes(){
+console.log(union([2], [1, 2]));// => [2, 1]
-}
+function zipObject(arr1, arr2){
+ var temp = {};
+ if (arr1.length === 0 || arr2.length === 0) return null;
-function sample(){
+ for (var i = 0; i < arr1.length; i++) {
+ temp[arr1[i]] = arr2[i];
+ }
+ return temp;
+}
+console.log(zipObject(['a', 'b'], [1, 2]));// => { 'a': 1, 'b': 2 }
+
+//Checks if value is in collection. If collection is a string, it's checked for a substring of value,
+// otherwise SameValueZero is used for equality comparisons. If fromIndex is negative, it's used as the offset
+// from the end of collection.
+function includes(collection, value, fromIndex=0){
+ if (Array.isArray(collection)) {
+ // Do array things
+ if (fromIndex > collection.length) return false;
+ if (fromIndex >= 0) {
+ for (var i = fromIndex; i < collection.length; i++) {
+ if (collection[i] === value) return true;
+ }
+ } else {
+ for (var i = collection.length-fromIndex; i >= 0; i--) {
+ if (collection[i] === value) return true;
+ }
+ }
+
+ } else if (typeof collection === 'object') {
+ // Do object things
+ for (var key in collection) {
+ if (collection[key] === value) return true;
+ }
+ } else if (typeof collection === 'string') {
+ // Do string things
+ if (collection.search(value) > -1) return true;
+ }
+
+ // How did you get here?! You should not be here!
+ return false;
}
-function cloneDeep(){
+console.log("includes: " + includes([1, 2, 3], 1, -1));// => true
+console.log(includes('abcd', 'bc'));// => true
+// Gets a random element from collection.
+function sample(collection){
+ if (collection.length === 0) return collection[0];
+ return collection[Math.floor(Math.random() * collection.length)];
}
-function sumBy(){
+//
+function cloneDeep(value){
+ if (!value) return {};
+
+ if (Array.isArray(value)) {
+ var temp = [];
+ for (var i = 0; i < value.length; i++) {
+ temp.push(cloneDeep(value[i]));
+ }
+ return temp;
+ } else if (typeof value === 'object') {
+ var obj = {};
+ for (var key in value) {
+ obj[key] = cloneDeep(value[key]);
+ }
+ return obj;
+ } else {
+ return value;
+ }
+}
+var objectsToClone = [{ 'a': 1 }, { 'b': 2 }];
+var shallow = cloneDeep(objectsToClone);
+console.log("clone deep: " + JSON.stringify(shallow));
+console.log(shallow[0] === objectsToClone[0]);// => true
+
+var objey = {
+ stuff: "foo",
+ data: {
+ val: {
+ thing: {
+ info: "bar",
+ moreInfo: {
+ evenMoreInfo: {
+ weMadeIt: "baz"
+ }
+ }
+ }
+ }
+ }
}
-function inRange(){
+var cloney = cloneDeep(objey);
+console.log("clone deep: " + JSON.stringify(cloney));
+
+//This method is like _.sum except that it accepts iteratee which is invoked for each element in array to
+//generate the value to be summed. The iteratee is invoked with one argument: (value).
+function sumBy(arr, iteratee=null){
+ var sum = 0;
+ for (var i = 0; i < arr.length; i++) {
+ if (typeof iteratee === 'function') {
+ sum += iteratee(arr[i]);
+ } else if (typeof iteratee == 'string') {
+ sum += arr[i][iteratee];
+ } else {
+ sum += arr[i];
+ }
+ }
+
+ return sum;
+}
+var objects1 = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }];
+console.log("sumBy: " + sumBy(objects1, 'n'));
+// => 20
+
+//Checks if n is between start and up to, but not including, end. If end is not specified,
+// it's set to start with start then set to 0. If start is greater than end the params are
+// swapped to support negative ranges.
+function inRange(num, start, end){
+ if (arguments.length === 2) {
+ end = start;
+ start = 0;
+ } else if (start > end) {
+ var temp = end;
+ end = start;
+ start = temp;
+ }
+
+ if (num >= start && num < end)
+ return true;
+
+ return false;
+}
+console.log("inRange: " + inRange(4,8));
+
+// Checks if path is a direct property of object.
+// Base Case: if we do not find the object after all possible depth tests
+function has(object, path){
+ if (path.length === 0) return false;
+ if (Array.isArray(path)) {
+ if (path.length === 0) return false; // Can it ever really get here?
+ if (path.length === 1) return path[0] in object;
+
+ if (path[0] in object) {
+ return has(object[path[0]], path.slice(1));
+ } else {
+ return false;
+ }
+ } else if (typeof path === 'string') {
+ if (path.length > 1) {
+ return has (object[path[0]], path.split("."));
+ } else {
+ return path in object;
+ }
+ }
}
-function has(){
+var hasObject = { 'a': { 'b': 2 } };
+console.log("has: " + has(hasObject, ['a', 'b']));
-}
-function omit(){
+//this method creates an object composed of the own and inherited enumerable property
+//paths of object that are not omitted.
+// Base Case: we've run out of elements in paths
+function omit(object, paths){
+ var omitObj = {};
+ var newObj = {};
-}
+ for (var i = 0; i < paths.length; i++) {
+ omitObj[paths[i]] = true;
+ }
-function pick(){
+ for (var key in object) {
+ if (key in omitObj === false) {
+ newObj[key] = object[key];
+ }
+ }
+ return newObj;
}
-function pickBy(){
+function pick(object, paths){
+ var newObj = {};
+
+ for (var i = 0; i < paths.length; i++) {
+ newObj[paths[i]] = object[paths[i]];
+ }
+ return newObj;
}
-function omitBy(){
+function pickBy(object, testFunc){
+ var newObj = {};
+ if (typeof testFunc !== "function") return {};
+ for (var key in object) {
+ if (testFunc(object[key]))
+ newObj[key] = object[key];
+ }
+
+ return newObj;
}
-function padEnd(){
+function omitBy(object, testFunc){
+ var omitObj = {};
+ var newObj = {};
+ if (typeof testFunc !== "function") return {};
-}
+ for (var key in object) {
+ if (testFunc(object[key]))
+ omitObj[key] = object[key];
+ }
-function repeat(){
+ for (var key in object) {
+ if (key in omitObj === false) {
+ newObj[key] = object[key];
+ }
+ }
+ return newObj;
}
-function upperFirst(str){
+function padEnd(string='', length=0, chars=' '){
+ var paddedStr = string;
+ while (paddedStr.length < length) {
+ paddedStr = paddedStr.concat(chars);
+ }
+ if (length > string.length)
+ return paddedStr.slice(0, length);
+ else
+ return paddedStr;
+}
+
+console.log("padEnd: " + padEnd('abc', 6));
+// => 'abc '
+
+function repeat(string='', n=1){
+ var repeatedStr = string;
+ if (n === 0) return "";
+ for (var i = 1; i < n; i++) {
+ repeatedStr = repeatedStr.concat(string);
+ }
+
+ return repeatedStr;
}
-function flatten(){
+function upperFirst(str){
+ return str.charAt(0).toUpperCase() + str.slice(1);
+}
+// Flattens array a single level deep.
+// Base Case: We run out of elements in the aray (any)
+function flatten(array){
+ var flattenedArr = [];
+
+ for (var i = 0; i < array.length; i++) {
+ if (Array.isArray(array[i])) {
+ for (var j = 0; j < array[i].length; j++)
+ flattenedArr.push(array[i][j]);
+ } else {
+ flattenedArr.push(array[i]);
+ }
+ }
+
+ return flattenedArr;
}
function zip(){
diff --git a/oop_examples/flash-card-oop/Card.js b/oop_examples/flash-card-oop/Card.js
new file mode 100644
index 00000000..b4d98c26
--- /dev/null
+++ b/oop_examples/flash-card-oop/Card.js
@@ -0,0 +1,4 @@
+function Card(front, back) {
+ this.front = front;
+ this.back = back;
+}
diff --git a/oop_examples/flash-card-oop/CardView.css b/oop_examples/flash-card-oop/CardView.css
new file mode 100644
index 00000000..df39e982
--- /dev/null
+++ b/oop_examples/flash-card-oop/CardView.css
@@ -0,0 +1,41 @@
+.card {
+ width: 270px;
+ height: 400px;
+ padding: 5px;
+ background-color: grey;
+ border-radius: 15px;
+ text-align: center;
+ font-size: 2em;
+}
+
+.inner-card {
+ width: 100%;
+ height: 100%;
+ border-radius: 15px;
+ line-height: 400px;
+}
+
+.front-card {
+ background-color: white;
+}
+
+.back-card {
+ background-color: lightgrey;
+}
+
+.hidden {
+ display: none;
+}
+
+.next-button {
+ background-color: #008CBA;
+ border: none;
+ color: white;
+ padding: 15px 32px;
+ text-align: center;
+ text-decoration: none;
+ border-radius: 20px;
+ display: inline-block;
+ font-size: 16px;
+ margin: 10px;
+}
diff --git a/oop_examples/flash-card-oop/CardView.js b/oop_examples/flash-card-oop/CardView.js
new file mode 100644
index 00000000..879c7639
--- /dev/null
+++ b/oop_examples/flash-card-oop/CardView.js
@@ -0,0 +1,59 @@
+function CardView(containerDivId, card=null) {
+ this.containerDivId = containerDivId;
+ this.containerDiv = document.getElementById(containerDivId);
+ this.card = card;
+
+ this.cardDiv = document.createElement('div');
+ this.cardDiv.className = "card";
+
+ var front = document.createElement('div');
+ front.className = 'inner-card front-card';
+ if (this.card) {
+ front.innerHTML = this.card.front;
+ }
+ var back = document.createElement('div');
+ back.className = 'inner-card back-card hidden';
+
+ if (this.card) {
+ back.innerHTML = this.card.back;
+ }
+
+ this.cardDiv.append(front);
+ this.cardDiv.append(back);
+ this.containerDiv.append(this.cardDiv);
+
+ this.cardDiv.addEventListener('click', this.flip.bind(this));
+}
+
+CardView.prototype.flip = function() {
+ var divs = this.cardDiv.getElementsByTagName('div');
+ if (divs.length === 2 && divs[0].className.indexOf("hidden") >= 0) {
+ var index = divs[0].className.indexOf("hidden")
+ divs[0].className = divs[0].className.slice(0, index - 1);
+ divs[1].className = divs[1].className + " hidden";
+ } else if (divs.length === 2) {
+ var index = divs[0].className.indexOf("hidden")
+ divs[1].className = divs[1].className.slice(0, index - 1);
+ divs[0].className = divs[0].className + " hidden";
+ }
+}
+
+CardView.prototype.updateCard = function(card) {
+ this.card = card;
+ var divs = this.cardDiv.getElementsByTagName('div');
+ if (divs.length === 2) {
+ divs[0].innerHTML = this.card.front;
+ divs[1].innerHTML = this.card.back;
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/oop_examples/flash-card-oop/FlashCardDeck.js b/oop_examples/flash-card-oop/FlashCardDeck.js
new file mode 100644
index 00000000..4f032e58
--- /dev/null
+++ b/oop_examples/flash-card-oop/FlashCardDeck.js
@@ -0,0 +1,25 @@
+function FlashCardDeck(cards) {
+ this.cards = cards.map(function(c) {
+ return new Card(c[0], c[1]);
+ });
+}
+
+FlashCardDeck.prototype.shuffle = function() {
+ var currentIndex = this.cards.length,
+ temporaryValue,
+ randomIndex;
+ while (0 !== currentIndex) {
+ randomIndex = Math.floor(Math.random() * currentIndex);
+ currentIndex -= 1;
+
+ temporaryValue = this.cards[currentIndex];
+ this.cards[currentIndex] = this.cards[randomIndex];
+ this.cards[randomIndex] = temporaryValue;
+ }
+}
+
+FlashCardDeck.prototype.randomCard = function() {
+ var card = this.cards.shift();
+ this.cards.push(card);
+ return card;
+}
diff --git a/oop_examples/flash-card-oop/index.html b/oop_examples/flash-card-oop/index.html
new file mode 100644
index 00000000..fff7383c
--- /dev/null
+++ b/oop_examples/flash-card-oop/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
Flash Cards
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/oop_examples/flash-card-oop/main.js b/oop_examples/flash-card-oop/main.js
new file mode 100644
index 00000000..3fa0d66c
--- /dev/null
+++ b/oop_examples/flash-card-oop/main.js
@@ -0,0 +1,13 @@
+document.addEventListener("DOMContentLoaded", function() {
+ const data = [['Hello', 'Hola'], ['Food', 'Comida'], ['Cat', 'Gato'],
+ ['Moxie', 'La Gata Mejor'], ['Sun', 'Sol'], ['Blue', 'Azul']];
+ const deck = new FlashCardDeck(data);
+ deck.shuffle();
+ let card = deck.randomCard();
+ const cardView = new CardView("container", deck.randomCard());
+
+ const button = document.getElementById('next-button')
+ button.addEventListener('click', function() {
+ cardView.updateCard(deck.randomCard());
+ });
+});
diff --git a/oop_examples/tetris-oop/css/global.css b/oop_examples/tetris-oop/css/global.css
new file mode 100644
index 00000000..4e885db7
--- /dev/null
+++ b/oop_examples/tetris-oop/css/global.css
@@ -0,0 +1,6 @@
+#gameboard {
+ border:1px solid #d3d3d3;
+ background-color: black;
+ height: 600px;
+ width: 300px;
+}
\ No newline at end of file
diff --git a/oop_examples/tetris-oop/index.html b/oop_examples/tetris-oop/index.html
new file mode 100644
index 00000000..af0fece4
--- /dev/null
+++ b/oop_examples/tetris-oop/index.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
Tetris
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Tetris
+
+
+
\ No newline at end of file
diff --git a/oop_examples/tetris-oop/js/grid.js b/oop_examples/tetris-oop/js/grid.js
new file mode 100644
index 00000000..30d45f94
--- /dev/null
+++ b/oop_examples/tetris-oop/js/grid.js
@@ -0,0 +1,103 @@
+function Grid(context, squareSize, squaresX, squaresY) {
+ this.context = context;
+
+ this.squareSize = squareSize;
+
+ this.squaresX = squaresX
+ this.squaresY = squaresY;
+ this.gridData = [];
+
+ for (var y = 0; y < this.squaresY; y++) {
+ this.gridData.push((new Array(this.squaresX)).fill(null));
+ }
+}
+
+Grid.prototype.draw = function() {
+ for (var y = 0; y < this.squaresY; y++) {
+ for (var x = 0; x < this.squaresX; x++) {
+ this.context.beginPath();
+ this.context.lineWidth="2";
+ this.context.strokeStyle="white";
+ this.context.rect(x*this.squareSize+.25,y*this.squareSize+.25,this.squareSize,this.squareSize);
+ this.context.stroke();
+
+ if (this.gridData[y][x] !== null) {
+ this.context.fillStyle = this.gridData[y][x].color;
+ this.context.fillRect(x*this.squareSize+.5,y*this.squareSize+.5,this.squareSize-.25,this.squareSize-.25)
+ }
+ }
+ }
+};
+
+Grid.prototype.linesToClear = function() {
+ lines = [];
+ for (var y = 0; y < this.squaresY; y++) {
+ clearLine = true;
+
+ for (var x = 0; x < this.squaresX; x++) {
+ if (this.gridData[y][x] === null) {
+ clearLine = false;
+ break;
+ }
+ }
+
+ if (clearLine) {
+ lines.push(y);
+ }
+ }
+
+ return lines;
+};
+
+Grid.prototype.addToGrid = function(tetromino) {
+ tetromino.shape.points.forEach(function(p) {
+ this.gridData[p.y][p.x] = new GridItem(tetromino.shape.color());
+ }, this);
+};
+
+Grid.prototype.checkCollisions = function(tetromino) {
+ points = tetromino.shape.bottomPoints();
+
+ var collision = false;
+ for (var i = 0; i < points.length; i++) {
+ if (points[i].y + 1 === this.squaresY) {
+ collision = true;
+ console.log("COLLISION: with bottom")
+ break;
+ }
+ }
+
+ if (!collision) {
+ for (var j = 0; j < points.length; j++) {
+ var p = points[j];
+ var x = p.x;
+ var y = p.y + 1;
+ if (x < this.squaresX &&
+ y < this.squaresY &&
+ this.gridData[y][x] !== null) {
+ collision = true;
+ console.log("COLLISION: with other item")
+ break;
+ }
+ }
+ }
+
+ return collision;
+};
+
+Grid.prototype.validPosition = function(points) {
+ return points.reduce(function(acc, point) {
+ var inbounds = point.x >= 0 && point.x < this.squaresX &&
+ point.y >= 0 && point.y < this.squaresY;
+ if (inbounds) {
+ var gridItem = this.gridData[point.y][point.x];
+ if (gridItem !== null) {
+ acc = false;
+ }
+ } else {
+ acc = false;
+ }
+
+ return acc;
+ }, true);
+};
\ No newline at end of file
diff --git a/oop_examples/tetris-oop/js/gridItem.js b/oop_examples/tetris-oop/js/gridItem.js
new file mode 100644
index 00000000..a83f2243
--- /dev/null
+++ b/oop_examples/tetris-oop/js/gridItem.js
@@ -0,0 +1,3 @@
+function GridItem(color) {
+ this.color = color
+}
\ No newline at end of file
diff --git a/oop_examples/tetris-oop/js/main.js b/oop_examples/tetris-oop/js/main.js
new file mode 100644
index 00000000..fe435614
--- /dev/null
+++ b/oop_examples/tetris-oop/js/main.js
@@ -0,0 +1,7 @@
+window.addEventListener("load", function() {
+ var canvas = document.getElementById("gameboard");
+
+ var tetris = new Tetris(canvas);
+
+ tetris.play();
+});
\ No newline at end of file
diff --git a/oop_examples/tetris-oop/js/point.js b/oop_examples/tetris-oop/js/point.js
new file mode 100644
index 00000000..0c6278dc
--- /dev/null
+++ b/oop_examples/tetris-oop/js/point.js
@@ -0,0 +1,4 @@
+function Point(x, y) {
+ this.x = x;
+ this.y = y;
+}
\ No newline at end of file
diff --git a/oop_examples/tetris-oop/js/shape.js b/oop_examples/tetris-oop/js/shape.js
new file mode 100644
index 00000000..7b30a13d
--- /dev/null
+++ b/oop_examples/tetris-oop/js/shape.js
@@ -0,0 +1,42 @@
+function Shape(points, gridItem) {
+ this.gridItem = gridItem;
+ this.points = points;
+}
+
+
+Shape.copyShape = function(shape) {
+ s = new Shape([], new GridItem(shape.gridItem.color));
+ for (var i = 0; i < shape.points.length; i++) {
+ s.points.push(new Point(shape.points[i].x, shape.points[i].y));
+ }
+
+ return s;
+};
+
+
+Shape.prototype.color = function() {
+ return this.gridItem.color;
+};
+
+
+Shape.prototype.bottomPoints = function() {
+ var bottomPoints = [];
+ for (var i = 0; i < this.points.length; i++) {
+ var p = this.points[i];
+ var lowest = true;
+
+ // y values count downward, so the higher the number
+ // the lower the y value
+ for (var j = 0; j < this.points.length; j++) {
+ if (i !== j && p.x === this.points[j].x) {
+ if (p.y < this.points[j].y) {
+ lowest = false;
+ }
+ }
+ }
+
+ if (lowest) bottomPoints.push(p);
+ }
+
+ return bottomPoints;
+}
\ No newline at end of file
diff --git a/oop_examples/tetris-oop/js/tetris.js b/oop_examples/tetris-oop/js/tetris.js
new file mode 100644
index 00000000..4ad0e1a8
--- /dev/null
+++ b/oop_examples/tetris-oop/js/tetris.js
@@ -0,0 +1,106 @@
+function Tetris(canvas, fps=40) {
+ this.canvas = canvas;
+ this.fps = fps;
+ this.height = canvas.height;
+ this.width = canvas.width;
+ this.context = canvas.getContext('2d');
+ this.intervalId = undefined;
+
+ this.squareSize = Math.floor(this.height / 20)-.05;
+ this.squaresX = 10;
+ this.squaresY = 20;
+
+ this.nextTetromino = this.createRandomTetromino();
+
+ this.fallingTetromino = null;
+ this.fallingSpeed = this.fps;
+ this.frameCount = 0;
+
+ this.addKeyBindings();
+
+
+ this.grid = new Grid(this.context, this.squareSize, this.squaresX, this.squaresY);
+}
+
+
+Tetris.prototype.addKeyBindings = function() {
+ var keys = {up: 38, left: 37, right: 39, down: 40, space: 32}
+ document.addEventListener("keydown", function(event) {
+ if (this.fallingTetromino !== null) {
+ var moved = false
+ if (event.which === keys.left) {
+ this.fallingTetromino.move("l");
+ moved = true;
+ } else if (event.which === keys.right) {
+ this.fallingTetromino.move("r");
+ moved = true;
+ } else if (event.which === keys.down) {
+ this.fallingTetromino.move("d");
+ moved = true;
+ } else if (event.which === keys.up) {
+ this.fallingTetromino.rotate();
+ moved = true;
+ }
+
+ if (moved) {
+ this.checkCollisions();
+ }
+ }
+ }.bind(this));
+}
+
+Tetris.prototype.play = function() {
+ if (this.fallingTetromino === null) {
+ this.newFallingTetromino();
+ }
+ this.intervalId = setInterval(this.update.bind(this), 1000 / this.fps);
+};
+
+Tetris.prototype.createRandomTetromino = function() {
+ var shape = Tetromino.createRandomShape();
+ return new Tetromino(this.context,
+ shape,
+ this.squareSize,
+ this.squaresX,
+ this.squaresY);
+}
+
+Tetris.prototype.pause = function() {
+ clearInterval(this.intervalId);
+ this.intervalId = undefined;
+};
+
+Tetris.prototype.clear = function() {
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+};
+
+Tetris.prototype.update = function() {
+ this.clear();
+ this.draw();
+};
+
+Tetris.prototype.draw = function() {
+ this.frameCount++;
+ this.updateFall();
+ this.grid.draw();
+ this.fallingTetromino.draw();
+};
+
+Tetris.prototype.updateFall = function() {
+ if (this.frameCount % this.fps === 0 && this.fallingTetromino) {
+ this.fallingTetromino.move('d');
+ this.checkCollisions();
+ }
+}
+
+Tetris.prototype.checkCollisions = function() {
+ if (this.grid.checkCollisions(this.fallingTetromino)) {
+ this.grid.addToGrid(this.fallingTetromino);
+ this.newFallingTetromino();
+ }
+}
+
+Tetris.prototype.newFallingTetromino = function() {
+ this.fallingTetromino = this.nextTetromino;
+ this.nextTetromino = this.createRandomTetromino();
+}
\ No newline at end of file
diff --git a/oop_examples/tetris-oop/js/tetromino.js b/oop_examples/tetris-oop/js/tetromino.js
new file mode 100644
index 00000000..f5cd22c3
--- /dev/null
+++ b/oop_examples/tetris-oop/js/tetromino.js
@@ -0,0 +1,105 @@
+function Tetromino(context, shape, squareSize, squaresX, squaresY ) {
+ this.context = context;
+ this.shape = shape;
+ this.squareSize = squareSize;
+ this.squaresX = squaresX;
+ this.squaresY = squaresY;
+}
+
+
+Tetromino.prototype.possibleShapes = [];
+
+// L-left
+Tetromino.prototype.possibleShapes.push(
+ new Shape([new Point(4, 0), new Point(4, 1),
+ new Point(5, 1), new Point(6, 1)],
+ new GridItem("blue")));
+
+// L-right
+Tetromino.prototype.possibleShapes.push(
+ new Shape([new Point(4, 1), new Point(5, 1),
+ new Point(6, 1), new Point(6, 0)],
+ new GridItem("darkorange")));
+
+// T
+Tetromino.prototype.possibleShapes.push(
+ new Shape([new Point(4, 1), new Point(5, 1),
+ new Point(6, 1), new Point(5, 0)],
+ new GridItem("rebeccapurple")));
+
+// Z-left
+Tetromino.prototype.possibleShapes.push(
+ new Shape([new Point(4, 0), new Point(5, 0),
+ new Point(5, 1), new Point(6, 1)],
+ new GridItem("red")));
+
+// Z-right
+Tetromino.prototype.possibleShapes.push(
+ new Shape([new Point(4, 1), new Point(5, 1),
+ new Point(5, 0), new Point(6, 0)],
+ new GridItem("green")));
+
+// Square
+Tetromino.prototype.possibleShapes.push(
+ new Shape([new Point(4, 0), new Point(4, 1),
+ new Point(5, 0), new Point(5, 1)],
+ new GridItem("gold")));
+
+// Line
+Tetromino.prototype.possibleShapes.push(
+ new Shape([new Point(3, 0), new Point(4, 0),
+ new Point(5, 0), new Point(6, 0)],
+ new GridItem("dodgerblue")));
+
+Tetromino.createRandomShape = function() {
+ var index = Math.floor(Math.random() * Tetromino.prototype.possibleShapes.length);
+
+ var shape = Tetromino.prototype.possibleShapes[index];
+
+ return Shape.copyShape(shape);
+}
+
+Tetromino.prototype.draw = function() {
+ var point;
+ for (var i = 0; i < this.shape.points.length; i++) {
+ point = this.shape.points[i]
+ this.context.fillStyle = this.shape.color();
+ this.context.fillRect(point.x*this.squareSize+.5,point.y*this.squareSize+.5,this.squareSize-.25,this.squareSize-.25)
+ }
+}
+
+
+// "d", "l", "r"
+Tetromino.prototype.move = function(direction) {
+ var xAdd = 0;
+ var yAdd = 0;
+
+ if (direction === "d") yAdd = 1;
+ else if (direction === "l") xAdd = -1;
+ else if (direction === "r") xAdd = 1;
+
+ var newPoints = [];
+ var allowed = true;
+ var newPoint;
+ for (var i = 0; i < this.shape.points.length; i++) {
+ newPoint = new Point(this.shape.points[i].x + xAdd, this.shape.points[i].y + yAdd);
+ if (this.__validPoint(newPoint)) {
+ newPoints.push(newPoint);
+ } else {
+ allowed = false;
+ }
+ }
+
+ if (allowed) {
+ this.shape.points = newPoints;
+ }
+}
+
+Tetromino.prototype.rotate = function() {
+ console.log("rotate");
+}
+
+Tetromino.prototype.__validPoint = function(point) {
+ return point.x >= 0 && point.x < this.squaresX &&
+ point.y >= 0 && point.y < this.squaresY;
+}
\ No newline at end of file
diff --git a/oop_examples/tetris-oop/readme.md b/oop_examples/tetris-oop/readme.md
new file mode 100644
index 00000000..126bee0b
--- /dev/null
+++ b/oop_examples/tetris-oop/readme.md
@@ -0,0 +1,16 @@
+# Tetris
+
+The goal of the application is to create a javascript version of tetris that uses object oriented programming to solve the problem.
+
+### Requirements
+
+As a user, I should be able to:
+
+* Start the game
+* get a random falling shape every x seconds
+* rotate the shape
+* move the shape left, right, down
+* pause the game
+* see the next shape that will fall
+* clear complete lines from the board (no spaces across a row)
+* keep track of the number of lines cleared
\ No newline at end of file
diff --git a/prototypes_exercise/prototypes.js b/prototypes_exercise/prototypes.js
index e69de29b..84d1bdab 100644
--- a/prototypes_exercise/prototypes.js
+++ b/prototypes_exercise/prototypes.js
@@ -0,0 +1,110 @@
+function Person(firstName, lastName, favoriteColor, favoriteNumber) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.favoriteColor = favoriteColor;
+ this.favoriteNumber = favoriteNumber;
+ this.favoriteFoods = [];
+ this.family = [];
+}
+
+Person.prototype.fullName = function() {
+ return this.firstName + " " + this.lastName;
+}
+
+var p = new Person("Shana", "Malarkin", "Green", 38);
+console.log(p.fullName()); // Shana Malarkin
+
+Person.prototype.toString = function() {
+ return this.firstName + " " + this.lastName + ", Favorite Color: " + this.favoriteColor +
+ ", Favorite Number: " + this.favoriteNumber;
+}
+
+//var p1 = new Person("Shana", "Malarkin", "Green", 38);
+console.log(p.toString()); // Shana Malarkin, Favorite Color: Green, Favorite Number: 38
+
+Person.prototype.addToFamily = function(object) {
+ if (object instanceof Person)
+ if (this.family.indexOf(object) === -1)
+ this.family.push(object);
+
+ return this.family.length;
+}
+
+Array.prototype.map = function(fn, thisArg) {
+// if (thisArg) this = thisArg; How do we handle this?!
+ var newArray = [];
+
+ for (var i = 0; i < this.length; i++) {
+ newArray.push(fn(this[i], i, this));
+ }
+
+ return newArray;
+}
+
+String.prototype.reverse = function() {
+ var reverse = "";
+
+ for (var i = this.length - 1; i >= 0; i--) {
+ reverse = reverse.concat(this[i]);
+ }
+
+ return reverse;
+}
+
+Function.prototype.bind = function(thisArg, ...outerArgs) {
+ var _this = this;
+ return function() {
+ return _this.apply(thisArg, [...outerArgs, ...arguments]);
+ }
+}
+
+/*
+array = [1,2,3,4];
+array.reduce(function(acc, el) {
+ return `${acc}${el}`;
+}, "");
+// "1234"
+*/
+
+Array.prototype.reduce = function(fn, startVal) {
+ var startValue = startVal? startVal : this[0];
+ var i = startVal? 0 : 1;
+
+ for (i ; i < this.length; i++) {
+ startValue = fn(startValue, this[i], i, this);
+ }
+
+ return startValue;
+}
+
+
+var numArray = [1,2,3,4];
+console.log(numArray.reduce(function(acc, el) {
+ return `${acc}${el}`;
+}, ""));
+// "1234"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/prototypes_exercise/prototypesSpec.js b/prototypes_exercise/prototypesSpec.js
index 3a7eb030..d3f70815 100644
--- a/prototypes_exercise/prototypesSpec.js
+++ b/prototypes_exercise/prototypesSpec.js
@@ -99,6 +99,35 @@ describe("Array.prototype.map", function(){
});
});
+describe("Array.prototype.reduce", function(){
+ var arr = [1,2,3,4]
+ it("adds numbers without an initial value", function(){
+ expect(arr.reduce((acc,v) => acc+v)).to.equal(10)
+ });
+ it("adds numbers with an initial value", function() {
+ expect(arr.reduce((acc,v) => acc+v, 10)).to.equal(20)
+ })
+ it("has an index as the 3rd parameter", function() {
+ expect(arr.reduce((acc,v, i) => i === 3 ? "blah": null, 10)).to.equal("blah")
+ })
+ it("has an index array as the 4th parameter", function() {
+ expect(arr.reduce((acc,v, i, arr) => arr.length, 10)).to.equal(4)
+ })
+ it("can return any type of data", function() {
+ var init = {name: "Tim", age: "33", numbers: []};
+ var res = arr.reduce((acc,v, i, arr) => {
+ acc.numbers.push(v);
+ return acc;
+ }, init);
+ expect(res).to.equal(init)
+ expect(res.numbers).to.be.a("array");
+ expect(res.numbers).to.equal(init.numbers);
+ expect(res.numbers.length).to.equal(4);
+ expect(res.numbers).to.deep.equal(arr);
+ })
+
+});
+
describe("String.prototype.reverse", function(){
it("returns a new reversed string", function(){
expect("test".reverse()).to.equal("tset");
@@ -117,4 +146,4 @@ describe("Function.prototype.bind", function(){
expect(add.bind(this,3)(4)).to.equal(7);
expect(add.bind(this)(3,4)).to.equal(7);
});
-});
\ No newline at end of file
+});
diff --git a/recursion_exercise/recursion.js b/recursion_exercise/recursion.js
index e69de29b..9a65b831 100644
--- a/recursion_exercise/recursion.js
+++ b/recursion_exercise/recursion.js
@@ -0,0 +1,230 @@
+function productOfArray(arr) {
+ // base case: if arr length === 0, return 1
+ // return product of all
+ if (arr.length === 0) return 1;
+
+ return arr[0] * productOfArray(arr.slice(1));
+}
+
+console.log(productOfArray([1,2,3])); // 6
+console.log(productOfArray([1,2,3,10])) // 60
+
+function collectStrings(obj) {
+ var newArr = [];
+ // base case: The loop iteration ends!
+ // Loop through all keys
+ // Search each key of object
+ // Is value of type string? If so, add to array
+ // If type is objects, recurse!
+ // If type is neither, keep looking.
+ // Return new array
+ for (var key in obj) {
+ if (typeof obj[key] === 'string') {
+ newArr = newArr.concat(obj[key]);
+ } else if (typeof obj[key] === 'object' && Array.isArray(obj[key]) === false) {
+ newArr = newArr.concat(collectStrings(obj[key]));
+ }
+ }
+
+ return newArr;
+}
+
+var obj = {
+ stuff: "foo",
+ data: {
+ val: {
+ thing: {
+ info: "bar",
+ moreInfo: {
+ evenMoreInfo: {
+ weMadeIt: "baz"
+ }
+ }
+ }
+ }
+ }
+}
+
+console.log(collectStrings(obj)); // ["foo", "bar", "baz"])
+
+function contains(nestedObject, val) {
+ // base case: The loop iteration ends!
+ // Loop through all keys
+ // Search each key in the object
+ // If the value matches they element value, return true
+ // If not keep looking
+ // If never found, return false
+ var found = false;
+ for (var key in nestedObject) {
+ if (typeof nestedObject[key] === 'object' && Array.isArray(nestedObject[key]) !== true) {
+ found = contains(nestedObject[key], val);
+ } else if (nestedObject[key] === val) {
+ found = true;
+ }
+ if (found) return true;
+ }
+ return found || false;
+}
+
+var nestedObject = {
+ data: {
+ info: {
+ stuff: {
+ thing: {
+ moreStuff: {
+ magicNumber: 44
+ }
+ }
+ }
+ }
+ },
+ stuff2: {
+ info: {
+ foo: "foo"
+ }
+ }
+}
+
+//console.log(contains(nestedObject, 44)); // true
+console.log(contains(nestedObject, "foo")); // false
+
+function search(arr, val) {
+ var count = 0;
+
+ if (arr.length === 0) return -1;
+
+ if (arr[0] === val) {
+ return 0;
+ }
+
+ count += search(arr.slice(1), val);
+
+ if (count === -1) return -1;
+
+ return count + 1;
+}
+// Try thinking of it as a count. You increase the count until you find the thing then return!
+
+// base case: if the value is never found
+// create an index variable
+// traverse the array
+// if not found, return -1
+// if found, return index
+
+console.log(search([1,2,3,4,5],5)); // 4
+console.log(search([1,2,3,4,5],15)); // -1
+
+// This may only be called on a sorted array.
+// This also might be the ugliest code I've ever written. Yikes dat base case section...
+function binarySearch(arr, val) {
+ var foundIndex = 0;
+
+ function helpSearch(arr, val) {
+ var searchIndex = Math.floor(arr.length / 2);
+ if (arr.length === 1) {
+ if (arr[0] !== val) {
+ foundIndex = -1;
+ return;
+ } else {
+ return;
+ }
+ } else if (arr.length === 0) {
+ foundIndex = -1;
+ return;
+ }
+
+ if (val > arr[searchIndex]) {
+ foundIndex += searchIndex + 1;
+ helpSearch(arr.slice(searchIndex + 1), val);
+ } else if (val < arr[searchIndex]) {
+ foundIndex -= searchIndex - 1;
+ helpSearch(arr.slice(0, searchIndex), val);
+ } else if (val === arr[searchIndex]) {
+ foundIndex+=searchIndex;
+ return;
+ }
+
+ }
+
+ helpSearch(arr, val);
+
+ return foundIndex;
+}
+
+// base case: if the value is never found, return -1
+// Helper Function
+ // foundIndex should be set to offsets combined
+console.log(binarySearch([1],5)); // -1
+console.log(binarySearch([1,2,3,4,5],5)); // 4
+console.log(binarySearch([1,2,3,4,5],15)); // -1
+
+
+
+function stringifyNumbers(obj) {
+ var newObj = {};
+
+ for (var key in obj) {
+ if (typeof obj[key] === 'number') {
+ newObj[key] = obj[key].toString();
+ } else if (typeof obj[key] === 'object' && Array.isArray(obj[key]) === false) {
+ newObj[key] = stringifyNumbers(obj[key]);
+ } else {
+ newObj[key] = obj[key];
+ }
+ }
+
+ return newObj;
+}
+
+// Store data in a new object
+// No need for Helper
+// Base Case: when the for loop ends!
+// Examine each key's type
+ // If the type is object, set the element to the result of recurse!
+ // If not, add the key,value pair to the object and move on
+ // Of course, if it's a number, stringify that crap!
+ // Return the new object!
+
+var obj1 = {
+ num: 1,
+ test: [],
+ data: {
+ val: 4,
+ info: {
+ isRight: true,
+ random: 66,
+ nested: {
+ inner: {
+ nestedInner: {
+ another: {
+ num: 10
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+console.log(JSON.stringify(stringifyNumbers(obj1)));
+/*/
+{
+ num: "1",
+ test: [],
+ data: {
+ val: "4",
+ info: {
+ isRight: true,
+ random: "66"
+ }
+ }
+}
+/*/
+
+
+
+
+
+
+
+
+
diff --git a/recursion_exercise/recursionSpec.js b/recursion_exercise/recursionSpec.js
index 5bc841fe..ac7f80a8 100644
--- a/recursion_exercise/recursionSpec.js
+++ b/recursion_exercise/recursionSpec.js
@@ -78,8 +78,8 @@ describe("#search", function(){
expect(search([1,2,3,4,5,6,7],6)).to.equal(5)
});
it("should return -1 if the value is not found", function(){
- expect(search([1,2,3,4]),0).to.equal(-1)
- expect(search([1,2]),11).to.equal(-1)
+ expect(search([1,2,3,4],0)).to.equal(-1)
+ expect(search([1,2],11)).to.equal(-1)
});
});
@@ -191,4 +191,4 @@ describe("#stringifyNumbers", function(){
}
expect(stringifyNumbers(obj)).to.deep.equal(answer)
});
-});
\ No newline at end of file
+});
diff --git a/testing_exercise/testing.js b/testing_exercise/testing.js
index e69de29b..1e7e9271 100644
--- a/testing_exercise/testing.js
+++ b/testing_exercise/testing.js
@@ -0,0 +1,46 @@
+function replaceWith(str, rep, repWith) {
+ var newStr = "";
+ for (var i = 0; i < str.length; i++) {
+ if (str[i] === rep) {
+ newStr += repWith;
+ } else {
+ newStr += str[i];
+ }
+ }
+ return newStr;
+}
+
+function expand(arr, repeat) {
+ var expandedArr = [];
+ for (var i = 0; i < repeat; i++) {
+ expandedArr = expandedArr.concat(arr);
+ }
+ return expandedArr;
+}
+
+function acceptNumbersOnly() {
+ for (var i = 0; i < arguments.length; i++) {
+ if (typeof arguments[i] !== 'number' || isNaN(arguments[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function mergeArrays(arr1, arr2) {
+ if (Array.isArray(arr1) === false || Array.isArray(arr2) === false) {
+ return false;
+ }
+
+ return arr1.concat(arr2).sort();
+}
+
+function mergeObjects(obj1, obj2) {
+ if (!obj1 || !obj2 || typeof obj1 !== 'object' || typeof obj2 !== 'object') {
+ return false;
+ }
+ for (var key in obj2) {
+ obj1[key] = obj2[key];
+ }
+ return obj1;
+}
\ No newline at end of file
diff --git a/testing_exercise/testingSpec.js b/testing_exercise/testingSpec.js
index aef56b1d..633250b1 100644
--- a/testing_exercise/testingSpec.js
+++ b/testing_exercise/testingSpec.js
@@ -1,3 +1,48 @@
var expect = chai.expect;
-// WRITE YOUR TESTS HERE!
\ No newline at end of file
+// WRITE YOUR TESTS HERE!
+describe("ReplaceWith", function() {
+ it ("is case sensitive", function() {
+ expect(replaceWith("awesome", "e", "z")).to.equal("awzsomz");
+ expect(replaceWith("Foo", "F", "B")).to.equal("Boo");
+ });
+});
+
+describe("Expand", function() {
+ it ("is expected output", function() {
+ expect(expand([1,2,3], 3)).to.deep.equal([1,2,3,1,2,3,1,2,3]);
+ expect(expand(["foo", "test"], 1)).to.deep.equal(["foo", "test"]);
+ });
+});
+
+describe("AcceptNumbersOnly", function () {
+ it ("is passed numbers only", function() {
+ expect(acceptNumbersOnly(1, "foo")).to.equal(false);
+ expect(acceptNumbersOnly(1,2,3,4,5,6,7)).to.equal(true);
+ expect(acceptNumbersOnly(1,2,3,4,5,6,NaN)).to.equal(false);
+ });
+});
+
+describe("MergeArrays", function() {
+ it ("is passed valid arrays", function() {
+ expect(mergeArrays([2,1],3)).to.deep.equal(false);
+ expect(mergeArrays([2,1],[3,4])).to.deep.equal([1,2,3,4]);
+ });
+});
+
+var obj1= {
+ name: "Foo",
+ num: 33
+}
+var obj2 = {
+ test: "thing",
+ num: 55
+}
+
+describe("MergeObjects", function() {
+ it ("is passed valid objects", function() {
+ expect(mergeObjects(obj1, null)).to.deep.equal(false);
+ expect(mergeObjects(obj1, obj2)).to.deep.equal({name: "Foo",test: "thing",num: 55});
+ });
+
+})
\ No newline at end of file