diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..2764d300 Binary files /dev/null and b/.DS_Store differ diff --git a/ajax_with_jquery_exercise/.DS_Store b/ajax_with_jquery_exercise/.DS_Store new file mode 100644 index 00000000..d146cfec Binary files /dev/null and b/ajax_with_jquery_exercise/.DS_Store differ diff --git a/ajax_with_jquery_exercise/app.css b/ajax_with_jquery_exercise/app.css new file mode 100644 index 00000000..c3ced98b --- /dev/null +++ b/ajax_with_jquery_exercise/app.css @@ -0,0 +1,131 @@ +.container { + display: flex; + justify-content: center; +} + +.content_column { + background-color: #f4deab; +} + +.header { + height: 48; + display: inline-flex; + align-items: center; + background-color: #f9571b; + width: 800px; +} + +.border_round { + border-radius: 4px; +} + +.border_round_sm { + border-radius: 2px; + border-width:1px; + border-style: solid; +} + +#logo { + width: 32px; + height: 32px; + margin-left: 12px; +} + +#title_page { + margin-left: 12px; + margin-right: 12px; +} + +#fav_links { + margin-left: 24px; + padding-left: 12px; +} + +.loginSignup_div { + margin-left: 128px; + padding-left: 12px; + margin-bottom: 32px; +} + +#form_submit { + margin-top: 12px; + margin-bottom: 12px; +} + +.input { + margin-top: 8px; + margin-bottom: 8px; +} + +.inputA { + position: relative; + text-align: right; + left: 0px; +} + +.inputB { + position: relative; + text-align: right; + left: 0px; +} + +.input_textA { + position:relative; + left: 24px; + width: 128px; + height: 24px; +} + +.input_textB { + position:relative; + left: 24px; + width: 128px; + height: 24px; +} + +.button_submit { + position:relative; + left: 70px; + width: 64px; + height: 32px; +} + +.visible { + visibility: visible; +} + +.invisible { + visibility: hidden; +} + +.displayNone { + display:none; +} + +.displayBlock { + display:block; +} + +.tiny_text { + font-size: 11px; +} + +.link_menu{ + text-decoration: none; +} + +.signup_link { + position:relative; + right: -410px; +} + +.login_link { + position:relative; + right: -420px; +} + +.text_by { + position: relative; + left:22px; + font-size: 12px; +} \ No newline at end of file diff --git a/ajax_with_jquery_exercise/index.html b/ajax_with_jquery_exercise/index.html new file mode 100644 index 00000000..6f001f8c --- /dev/null +++ b/ajax_with_jquery_exercise/index.html @@ -0,0 +1,64 @@ + + + + + Document + + + + +
+
+
+
+

Hack or Snooze

+
+ + + +
+ + +
+ + +
+
+ + \ No newline at end of file diff --git a/ajax_with_jquery_exercise/index.js b/ajax_with_jquery_exercise/index.js new file mode 100644 index 00000000..ba875bd4 --- /dev/null +++ b/ajax_with_jquery_exercise/index.js @@ -0,0 +1,271 @@ +$(document).ready(function() { + var loggedIn = JSON.parse(localStorage.getItem("hos_loggedIn")); + console.log("Logged In: " + loggedIn); + + var $list = $("#list_links"); + var $lnkLogin = $("#link_login"); + var $lnkSignup = $("#link_signup"); + var $loginDiv = $("#login_div"); + var $signupDiv = $("#signup_div"); + var $btnLogin = $("#login_form"); + var $btnSignup = $("#signup_form"); + + // FAVORITES + var $favs = $("#list_favs"); + var $lnkFavorites = $("#link_fav"); + var favContainer = {}; + + /* DEV SETTINGS */ + //localStorage.removeItem("hos_top10Cached"); + var top10Cached = JSON.parse(localStorage.getItem("hos_top10Cached")); + //loggedIn=true; + //localStorage.setItem("hos_loggedIn", false); + /* DEV SETTINGS */ + + /* Initialization */ + + // A few things need to change if user is logged in... + toggleLoginMenuMode($lnkLogin, $lnkSignup, loggedIn); + + if (top10Cached) { + $list.append(buildList(top10Cached, loggedIn)); + } else { + createTopStoryList(10).then(function(response) { + $list.append(buildList(response, loggedIn)); + + /* DEV SETTINGS */ + localStorage.setItem("hos_top10Cached", JSON.stringify(response)); + /* DEV SETTINGS */ + }) + .fail(function(err) { + console.log("ERROR: " + err); + }); + } + + $lnkLogin.on('click', function() { + if (loggedIn === false) { + $loginDiv.eq(0).slideToggle(250); + } else { + loggedIn = false; + localStorage.setItem("hos_loggedIn", loggedIn); + toggleLoginMenuMode($lnkLogin, $lnkSignup, loggedIn); + setChecksVisible($list, loggedIn); + } + }); + + $lnkSignup.on('click', function() { + $signupDiv.eq(0).slideToggle(250); + }); + + // Combine these two into a single function and pass whether login or signup? + $btnLogin.on('click', function(event) { + var username = $("#input_text3").val(); + var password = $("#input_text4").val(); + + if ($(event.target).attr("id") === "login_submit") { + $.ajax({ + method: "POST", + headers: { + "Content-Type": "application/json" + }, + url: "https://hn-favorites.herokuapp.com/login", + data: JSON.stringify({ + email: username, + password: password + }) + }) + .then(function(data) { + loginMaintenance(data); + }) + .fail(function(err) { + console.warn("Auth Token Error: " + err); + alert("Log In: Please enter a valid email address and password!"); + }); + } + }); + + $btnSignup.on('click', function(event) { + var username = $("#input_text3").val(); + var password = $("#input_text4").val(); + + if ($(event.target).attr("id") === "signup_submit") { + $.ajax({ + method: "POST", + headers: { + "Content-Type": "application/json" + }, + url: "https://hn-favorites.herokuapp.com/signup", + data: JSON.stringify({ + email: username, + password: password + }) + }) + .then(function(data) { + loginMaintenance(data); + }) + .fail(function(err) { + console.warn("Auth Token Error: " + err); + alert("Sign Up: Please enter a valid email address and password!"); + }); + } + }); + + function loginMaintenance(data) { + console.log(data.auth_token); + localStorage.setItem("hos_autoToken", data.auth_token); + localStorage.setItem("hos_loggedIn", true); + loggedIn = true; + $loginDiv.eq(0).slideUp(250); + $signupDiv.eq(0).slideUp(250); + toggleLoginMenuMode($lnkLogin, $lnkSignup, loggedIn); + setChecksVisible($list, loggedIn); + } + + $list.on('click', function(event){ + if ($(event.target).attr('type') === 'checkbox') { + if ($(event.target).is(':checked')) { + + var $siblings = $(event.target).siblings(); // Get Parent + var $key = $(event.target).attr('id'); + // Add this checkbox to the local container of favorites. + favContainer[$key] = { + $url: $($siblings[1]).text().replace("(", "").replace(")", ""), + $by: $($siblings[2]).text().slice(3), + $title: $($siblings[0]).text() + } + + // FAIL + $.ajax({ + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": localStorage.getItem("hos_autoToken") + }, + url: "https://hn-favorites.herokuapp.com/stories.json", + data: { + hacker_news_story: { ///// THIS IS FAILING. JSON Turns this into a bad string + by: favContainer.$by, + story_id: favContainer.$key, + title: favContainer.$title, + url: favContainer.$url + } + } + }) + .then(function(data) { + console.log("Favorite Data Returned: " + data); + }) + .fail(function(err) { + var loc = localStorage.getItem("hos_autoToken") + console.warn("Add Favorite Error: " + err); + //alert("Log In: Please enter a valid email address and password!"); + }); + + + + } else { + // REQUEST IT BE REMOVED FROM FAVORITES + } + } + }); + + $lnkFavorites.on('click', function() { + $.ajax({ + method: "GET", + headers: { + "Authorization": localStorage.getItem("hos_autoToken") + }, + url: "https://hn-favorites.herokuapp.com/stories.json", + }) + .then(function(response) { + console.log(response); + + // HERE IS WHERE WE WILL FILL THE FAVORITES LIST + // Also hide the regular list and show the favorites only. + }) + .fail(function(response) { + // What happens when we fail? + }); + }); + + +}); + +function createTopStoryList(n) { + // This function needs to pull the list of top stories, then iterate through the top n, + // doing a get request for each + + return $.get("https://hacker-news.firebaseio.com/v0/topstories.json?") + .then(function(response) { + var promises = response.slice(0, n).map(function(currentVal) { + return $.get(`https://hacker-news.firebaseio.com/v0/item/${currentVal}.json`) + }); + + return Promise.all(promises) + }); +} + +function buildList(responseList, loggedIn) { + var $listItems = []; + var $listItem; + var url; + var index; + var visible = loggedIn? "visible" : "invisible"; + + responseList.forEach(function(val) { + $listItem = $("
  • ", { + }); + + index = val.url.indexOf("://"); + url = val.url.slice(index+3); + index = url.indexOf("/"); + url = url.slice(0, index); + + $listItem.html(` + ${val.title} + (${url}) +
    by ${val.by}
    + `); + + $listItems.push($listItem); + }); + return $listItems; +} + +// NOT CURRENTLY WORKING +function setChecksVisible($list, visible) { + var $children = $list.children(); + + $children.each(function(index) { + var $subChildren = $children.eq(index).children(); + + if (visible) { + $($subChildren[0]).addClass("visible"); + $($subChildren[0]).removeClass("invisible"); + } else { + $($subChildren[0]).removeClass("visible"); + $($subChildren[0]).addClass("invisible"); + } + }); +} + +function toggleLoginMenuMode($lnkLogin, $lnkSignup, loggedIn) { + if (loggedIn === true) { + // The formatting is super ugly when it goes to log out... + $lnkLogin.text("log out"); + $lnkSignup.addClass("displayNone"); + $lnkSignup.removeClass("displayBlock"); + } else { + $lnkLogin.text("log in"); + $lnkSignup.addClass("displayBlock"); + $lnkSignup.removeClass("displayNone"); + } +} + + + + + + + + + diff --git a/ajax_with_jquery_exercise/readme.md b/ajax_with_jquery_exercise/readme.md index 25afa52e..7dd5c354 100644 --- a/ajax_with_jquery_exercise/readme.md +++ b/ajax_with_jquery_exercise/readme.md @@ -90,9 +90,9 @@ curl -H "Content-Type: application/json" \ https://hn-favorites.herokuapp.com/stories.json ``` -__Stories API - DELETE /stories/:story_id.json__ +__Stories API - DELETE /stories/:id.json__ -To delete a favorite story, you will need the id created by the api. For example, a delete request might to the following url: +To delete a favorite story, you will need the id created by the api. For example, a delete request might be to the following url: ``` https://hn-favorites.herokuapp.com/stories/2.json diff --git a/call_apply_bind_exercise/callApplyBind.js b/call_apply_bind_exercise/callApplyBind.js index e69de29b..cd51dbe3 100644 --- a/call_apply_bind_exercise/callApplyBind.js +++ b/call_apply_bind_exercise/callApplyBind.js @@ -0,0 +1,107 @@ +// - Write a function called `sumEvenArguments` which takes all of the arguments passed to a function and returns the sum of the even ones. +function sumEvenArguments() { + var args = [].slice.call(arguments); + console.log(args); + var sum = 0; + for (var i = 0; i < args.length; i++) { + if (args[i] % 2 === 0) sum += args[i]; + } + return sum; +} + +// ```javascript +console.log(sumEvenArguments(1,2,3,4)); // 6 +console.log(sumEvenArguments(1,2,6)); // 8 +console.log(sumEvenArguments(1,2)); // 2 +// ``` + + +// - Write a function called `arrayFrom` which converts an array-like-object into an array. + +function arrayFrom() { + return [].slice.apply(arguments); +} + + +// ```javascript +// function sample(){ +// var arr = arrayFrom(arguments) +// if(!arr.reduce) throw "This is not an array!" +// return arr.reduce(function(acc,next){ +// return acc+next; +// },0) +// } +// ``` + +// Write a function called `invokeMax` which accepts a function and a maximum amount. `invokeMax` should return a +// function that when called increments a counter. If the counter is greater than the maximum amount, the inner +// function should return "Maxed Out" + +function invokeMax(fn, maximum) { + var counter = 0; + + return function () { + + if (++counter > maximum) + return "Maxed Out!"; + + return fn.apply(this, arguments); + } +} + +function sayHi(count) { + console.log("Hi! Count: " + count); +} + + +// ```javascript +// function add(a,b){ +// return a+b +// } + +// var addOnlyThreeTimes = invokeMax(add,3); +// addOnlyThreeTimes(1,2) // 3 +// addOnlyThreeTimes(2,2) // 4 +// addOnlyThreeTimes(1,2) // 3 +// addOnlyThreeTimes(1,2) // "Maxed Out!" +// ``` + + +function guessingGame(amount) { + var answer = Math.floor(Math.random() * 11); + var guesses = 0; + var alreadyWon = false; + + return function(guess) { + if (alreadyWon) return "Dude you already won! Restart this beast." + if (guesses >= amount) return "Sorry buddy, you have exceeded your guesses!"; + guesses++; + if (guess === answer) { + alreadyWon = true; + return "You got it! Guesses: " + guesses;; + } + else if (guess < answer) { + return "You're too low! Guesses Remaining: " + (amount - guesses); + } else { + return "You're too high! Guesses Remaining: " + (amount - guesses); + } + } +} + +var game = guessingGame(5); +console.log(game(1)); +console.log(game(2)); +console.log(game(7)); +console.log(game(8)); +console.log(game(9)); +console.log(game(4)); + + + + + + + + + + diff --git a/call_apply_bind_exercise/readme.md b/call_apply_bind_exercise/readme.md index cbc3cd03..1cdfee71 100644 --- a/call_apply_bind_exercise/readme.md +++ b/call_apply_bind_exercise/readme.md @@ -5,17 +5,15 @@ Fix the following code: ```javascript var obj = { fullName: "Harry Potter", - person: { - sayHi: function(){ - return "This person's name is " + this.fullName - } + sayHi: function(){ + return "This person's name is " + this.fullName } } ``` - List two examples of "array-like-objects" that we have seen. - - - - + - arguments + - Rest operator ### Functions to write: @@ -41,7 +39,7 @@ function sample(){ } ``` -Write a function called `invokeMax` which accepts a function and a maximum amount. `invokeMax` should return a function that when called increments a counter. If the counter is greater than the maximum amount, the inner function should return "Maxed Out" +Write a function called `invokeMax` which accepts a function and a maximum amount. `invokeMax` should return a function that when called increments a counter. If the counter is greater than the maximum amount, the inner function should return "Maxed Out!" ```javascript function add(a,b){ diff --git a/canvas_exercise/.DS_Store b/canvas_exercise/.DS_Store new file mode 100644 index 00000000..c4c55869 Binary files /dev/null and b/canvas_exercise/.DS_Store differ diff --git a/canvas_exercise/shapes_game/index.js b/canvas_exercise/shapes_game/index.js index 0de5f18a..639ca754 100644 --- a/canvas_exercise/shapes_game/index.js +++ b/canvas_exercise/shapes_game/index.js @@ -1,17 +1,140 @@ window.addEventListener("load", function() { function clear(ctx, width, heigt) { + ctx.clearRect(0,0,width,height); } function drawRandomShape(ctx, width, height) { + var shapeNum = Math.floor(Math.random() * 4); + + var x = Math.random() * (width - 100); + var y = Math.random() * (height - 100); + + effectOn = false; + clearTimeout(initParticlesTimer); + clearInterval(particlesInterval); + clearTimeout(stopParticleTimer); + + switch(shapeNum) { + case 0: // Red Triangle + ctx.fillStyle = "red"; + ctx.beginPath(); + ctx.moveTo(x,y); + ctx.lineTo(x+100, y+100); + ctx.lineTo(x, y+100); + ctx.lineTo(x,y); + ctx.fill(); + ctx.closePath(); + initParticlesTimer = setTimeout(function() { + blastShape(x,y); + }, 500); + return "red0"; + case 1: // White Square + ctx.fillStyle = "white"; + ctx.fillRect(x,y,100,100); + initParticlesTimer = setTimeout(function() { + blastShape(x,y); + }, 500); + return "white1" + case 2: // White Triangle + ctx.fillStyle = "white"; + ctx.beginPath(); + ctx.moveTo(x,y); + ctx.lineTo(x+100, y+100); + ctx.lineTo(x, y+100); + ctx.lineTo(x,y); + ctx.fill(); + ctx.closePath(); + initParticlesTimer = setTimeout(function() { + blastShape(x,y); + }, 500); + return "white0"; + default: // Red Square + ctx.fillStyle = "red"; + ctx.fillRect(x,y,100,100); + initParticlesTimer = setTimeout(function() { + blastShape(x,y); + }, 500); + return "red1"; + } } function drawGameStartText(ctx, width, height, score) { + ctx.font = "40px comic-sans"; + ctx.fillStyle = "white"; + ctx.fillText("Press any key to start!", width*0.3, height*0.45); + ctx.fillText("Last Score: " + score, width*0.38, height*0.55); + } + + function restartGame(ctx, width, height, scoreSpan, timerSpan) { + clear(ctx, width, height); + drawGameStartText(ctx,width,height, scoreSpan.innerText); + resetTimeNScore(scoreSpan, timerSpan); + } + + function resetTimeNScore(scoreSpan) { + scoreSpan.innerText = 0; + timerSpan.innerText = 30; + } + + function startTimer(timerSpan) { + intervalId = setInterval(function() { + timerSpan.innerText = Number(timerSpan.innerText) - 1; + + if (Number(timerSpan.innerText) === 0) { + clearInterval(intervalId); + endGame(); + } + }, 1000); + } + + function endGame() { + expectedKey = undefined; + gameOn = false; + effectOn = false; + restartGame(ctx, width, height, scoreSpan, timerSpan); } - function restartGame(ctx, width, height) { + // MAYBE implement this + function randomEffect(randomNum) { + + } + + function blastShape(x, y) { + + clear(ctx, height, width); + effectOn = true; + for (var i = 0; i < particles.length; i++) { + particles[i].x = Math.floor(Math.random() * 50) + x; + particles[i].y = Math.floor(Math.random() * 50) + y; + particles[i].velX = Math.floor(Math.random() * 10 - 5); + particles[i].velY = Math.floor(Math.random() * 10 - 5); + } + + particleTimer = setTimeout(function() { + if (effectOn) { + clearInterval(particlesInterval); + effectOn = false; + clear(ctx, canvas.width, canvas.height); + } + }, 2000); + + particlesInterval = setInterval(function() { + if (effectOn) { + clear(ctx, canvas.width, canvas.height); + for (var j = 0; j < particles.length; j++) { + ctx.fillRect(particles[j].x,particles[j].y,2,2); + particles[j].x += particles[j].velX; + particles[j].y += particles[j].velY; + } + } + }, 50); } +/* + Initialize game and variables +*/ + var canvas = document.getElementById("shapes-game"), height = canvas.scrollHeight, width = canvas.scrollWidth, @@ -24,13 +147,48 @@ window.addEventListener("load", function() { timerSpan = document.getElementById("time-remaining"), scoreSpan = document.getElementById("score-val"), seconds = 3, - intervalId; + intervalId, + particles = [], + initParticlesTimer, + particlesInterval, + stopParticleTimer, + effectOn = false;; canvas.width = width; canvas.height = height; - document.addEventListener("keyup", function() { - + // Set up the particles only once;. + for (var i = 0; i < 50; i++) { + var particle = { + x: 0, + y: 0, + velX: 10, + velY: 10 + } + particles.push(particle); + } + + document.addEventListener("keyup", function(event) { + if (gameOn === false) { + startTimer(timerSpan); + gameOn = true; + } + + clear(ctx, canvas.width, canvas.height); + + if (expectedKey !== undefined) { + if (event.keyCode === expectedKeysMap[expectedKey]) { + scoreSpan.innerText = Number(scoreSpan.innerText) + 1; + } else if (event.keyCode == 38 || event.keyCode=== 40 || event.keyCode=== 37 || event.keyCode=== 39){ + scoreSpan.innerText = Number(scoreSpan.innerText) - 1; + } + } + + expectedKey = drawRandomShape(ctx, canvas.width, canvas.height); }); + + + + restartGame(ctx, canvas.width, canvas.height, scoreSpan); }); diff --git a/intermediate_oop/tic_tac_toe/.DS_Store b/intermediate_oop/tic_tac_toe/.DS_Store new file mode 100644 index 00000000..11474080 Binary files /dev/null and b/intermediate_oop/tic_tac_toe/.DS_Store differ diff --git a/intermediate_oop/tic_tac_toe/index.html b/intermediate_oop/tic_tac_toe/index.html index 00c74eb9..a0fe6bbb 100644 --- a/intermediate_oop/tic_tac_toe/index.html +++ b/intermediate_oop/tic_tac_toe/index.html @@ -11,9 +11,12 @@ - - - + + + + + +

    Tic Tac Toe

    diff --git a/intermediate_oop/tic_tac_toe/js/board.js b/intermediate_oop/tic_tac_toe/js/board.js new file mode 100644 index 00000000..8f6dbc0e --- /dev/null +++ b/intermediate_oop/tic_tac_toe/js/board.js @@ -0,0 +1,41 @@ +function Board() { + this.row = [0, 0, 0]; + this.col = [0, 0, 0]; + this.diagUpperLeft = 0; + this.diagUpperRight = 0; + this.boardSize = 3; // I am aware this is extraneous! May support n-size boards one day :D +} + +Board.prototype.makeMark = function(x, y, adjuster){ + this.row[x] += adjuster; + this.col[y] += adjuster; + + // Middle spot, vs diagonals... + if (x === 1 && y === 1) { + this.diagUpperLeft += adjuster; + this.diagUpperRight += adjuster; + } else if (x === y) { // Wrote it out the long way and noticed the pattern :D + this.diagUpperLeft += adjuster; + } else if (Math.abs(x - y) === this.boardSize-1) { + this.diagUpperRight += adjuster; + } +} + +// Question: Do I need to bother with "else if" for formatting purposes, if each condition returns?? +Board.prototype.testWinCondition = function(x, y, adjuster) { + if (this.row[x] === adjuster * this.boardSize) return true; + if (this.col[y] === adjuster * this.boardSize) return true; + if (this.diagUpperLeft === adjuster * this.boardSize) return true; + if (this.diagUpperRight === adjuster * this.boardSize) return true; + + return false; +} + +Board.prototype.clearBoard = function() { + for (var i = 0; i < this.row.length; i++) { + this.row[i] = 0; + this.col[i] = 0; + } + this.diagUpperLeft = 0; + this.diagUpperRight = 0; +} \ No newline at end of file diff --git a/intermediate_oop/tic_tac_toe/js/game.js b/intermediate_oop/tic_tac_toe/js/game.js new file mode 100644 index 00000000..a4c5a14a --- /dev/null +++ b/intermediate_oop/tic_tac_toe/js/game.js @@ -0,0 +1,54 @@ +function Game() { + this.players = []; + this.players.push(new Player("Brian", "X", 1)); + this.players.push(new Player("Jenny", "O", -1)); + this.currentPlayer = 0; + this.message = document.getElementById("message"); + + this.board = new Board(); + this.containerDiv = document.getElementById("board"); + this.containerDiv.addEventListener('click', this.attemptMark.bind(this)); + this.newGameBtn = document.getElementById("new-game"); + this.newGameBtn.addEventListener('click', this.newGame.bind(this)); +} + +Game.prototype.attemptMark = function(event) { + // this is bound to GAME so we can call game's board's functions, and more! + // Also remember to use Player's mark + var targetIdStr = event.target.id; + + if (event.target.className !== "square") { + return; + } + + var x = Number.parseInt(targetIdStr[7]); + var y = Number.parseInt(targetIdStr[9]); + + if (event.target.innerText === "") { + console.log(`Player ${this.currentPlayer+1}: Marking Board with: ${this.players[this.currentPlayer].mark}`); + this.board.makeMark(x, y, this.players[this.currentPlayer].adjuster); + event.target.innerText = this.players[this.currentPlayer].mark; + if (this.testWin(x, y)) { + this.message.innerText = `Player ${this.currentPlayer+1} Wins!`; + } + this.currentPlayer = this.currentPlayer === 0? 1 : 0; + } else { + alert("That square is occupied!"); + } +} + +Game.prototype.testWin = function(x, y) { + return this.board.testWinCondition(x, y, this.players[this.currentPlayer].adjuster); +} + +Game.prototype.newGame = function() { + this.currentPlayer = 0; + this.board.clearBoard(); + this.message.innerText = ""; + var squares = document.getElementsByClassName("square"); + for (var i = 0; i < squares.length; i++) { + squares[i].innerText = ""; + } +} + + diff --git a/intermediate_oop/tic_tac_toe/js/main.js b/intermediate_oop/tic_tac_toe/js/main.js index 379d891d..3cc3f6ce 100644 --- a/intermediate_oop/tic_tac_toe/js/main.js +++ b/intermediate_oop/tic_tac_toe/js/main.js @@ -1,2 +1,4 @@ document.addEventListener("DOMContentLoaded", function() { + var game = new Game(); }); + diff --git a/intermediate_oop/tic_tac_toe/js/player.js b/intermediate_oop/tic_tac_toe/js/player.js new file mode 100644 index 00000000..36b8d68c --- /dev/null +++ b/intermediate_oop/tic_tac_toe/js/player.js @@ -0,0 +1,5 @@ +function Player(name="", mark="ERR", adjuster = 0) { + this.name = name; + this.mark = mark; + this.adjuster = adjuster; +} \ No newline at end of file diff --git a/jquery_exercise/.DS_Store b/jquery_exercise/.DS_Store new file mode 100644 index 00000000..ddc8cd39 Binary files /dev/null and b/jquery_exercise/.DS_Store differ diff --git a/jquery_exercise/Files/.DS_Store b/jquery_exercise/Files/.DS_Store new file mode 100644 index 00000000..a077ef92 Binary files /dev/null and b/jquery_exercise/Files/.DS_Store differ diff --git a/jquery_exercise/Files/logo b/jquery_exercise/Files/logo new file mode 100644 index 00000000..f287ec17 Binary files /dev/null and b/jquery_exercise/Files/logo differ diff --git a/jquery_exercise/Files/logo.png b/jquery_exercise/Files/logo.png new file mode 100644 index 00000000..f287ec17 Binary files /dev/null and b/jquery_exercise/Files/logo.png differ diff --git a/jquery_exercise/app.css b/jquery_exercise/app.css new file mode 100644 index 00000000..03f9bca2 --- /dev/null +++ b/jquery_exercise/app.css @@ -0,0 +1,107 @@ +.container { + display: flex; + justify-content: center; +} + +.content_column { + background-color: #f4deab; +} + +.header { + height: 48; + display: inline-flex; + align-items: center; + background-color: #f9571b; + width: 800px; +} + +.border_round { + border-radius: 4px; +} + +.border_round_sm { + border-radius: 2px; + border-width:1px; + border-style: solid; +} + +#logo { + width: 32px; + height: 32px; + margin-left: 12px; +} + +#title_page { + margin-left: 12px; + margin-right: 12px; +} + +#subFav_links { + margin-left: 24px; + padding-left: 12px; +} + +#submission { + margin-left: 128px; + padding-left: 12px; + margin-bottom: 32px; +} + +#form_submit { + margin-top: 12px; + margin-bottom: 12px; +} + +.input { + margin-top: 8px; + margin-bottom: 8px; +} + +#input_label1 { + position: relative; + text-align: right; + left: 0px; +} + +#input_label2 { + position: relative; + text-align: right; + left: 8px; +} + +#input_text1 { + position:relative; + left: 30px; + width: 128px; + height: 24px; +} + +#input_text2 { + position:relative; + left: 37px; + width: 128px; + height: 24px; +} + +#button_submit { + position:relative; + left: 60px; + width: 64px; + height: 32px; +} + +.visible { + display:list-item; +} + +.invisible { + display:none; +} + +.tiny_text { + font-size: 10px; +} + +.link_filter { + text-decoration: none; +} \ No newline at end of file diff --git a/jquery_exercise/app.js b/jquery_exercise/app.js new file mode 100644 index 00000000..d5536dfa --- /dev/null +++ b/jquery_exercise/app.js @@ -0,0 +1,86 @@ +$(document).ready(function() { + var $linkSubmit = $("#link_submit"); + var $formSubmit = $("#submission"); + var $btnSubmit = $("#form_submit"); + var $list = $("#list_links"); + var $btnFavorites = $("#link_fav"); + var favoritesOnly = false; + var urlFilterOn = false; + + $linkSubmit.on("click", function() { + $formSubmit.eq(0).slideToggle(250); + }); + + $btnSubmit.on("submit", function (e) { + e.preventDefault(); + + // GRAB THE TEXT BOXES HERE + var $title = $('#input_text1').val(); + var $url = ($('#input_text2').val()).replace("http://", ""); + + var $listItem = $("
  • ", { + }); + + $listItem.html(` + ${$title} + (${$url}) + `); + + $list.append($listItem); + + $formSubmit.eq(0).slideToggle(250); + }); + + $btnFavorites.on("click", function() { + favoritesOnly = !favoritesOnly; + + $("#list_links li").each(function() { + if (urlFilterOn === false) { + var $checkbox = $(this).children().eq(0); + + if ($checkbox.prop("checked") === false) { + $(this).toggleClass("visible invisible"); + } + } else { + $(this).addClass("visible"); + $(this).removeClass("invisible"); + } + }); + + if (urlFilterOn) { + urlFilterOn = false; + favoritesOnly = false; + $btnFavorites.text("favorites"); + } + + if (favoritesOnly === true) { + $list.css("list-style-type", "none"); + } else { + $list.css("list-style-type", "decimal"); + } + + // Just in case this was off! + $list.on('click', ".link_filter", listCallback); + }); + + $list.on('click', ".link_filter", listCallback); + + function listCallback(e) { + $btnFavorites.text("all"); + urlFilterOn = true; + favoritesOnly = false; + var $urlLink = $(e.target).text(); + + + $("#list_links li").each(function() { + var $linkText = $(this).children().eq(2).children().eq(0).text(); + if ($linkText !== $urlLink) { + $(this).toggleClass("visible invisible"); + }; + }); + + $list.css("list-style-type", "none"); + $list.off('click'); + + }; +}); \ No newline at end of file diff --git a/jquery_exercise/index.html b/jquery_exercise/index.html new file mode 100644 index 00000000..71abbd4f --- /dev/null +++ b/jquery_exercise/index.html @@ -0,0 +1,61 @@ + + + + + Document + + + + +
    +
    +
    + +
    +

    Hack or Snooze

    +
    + +
    + +
    + +
    +
    + + \ 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