diff --git a/README.md b/README.md index 3945c152..fcc25b48 100755 --- a/README.md +++ b/README.md @@ -1,93 +1,29 @@ Assignment 2 - Short Stack: Basic Two-tier Web Application using HTML/CSS/JS and Node.js === -Due: September 16th, by 11:59 PM. +Abhijay Thammana 9/16/20 +https://athammana-a2-athammana-1.glitch.me/ -This assignment aims to introduce you to the concepts and practice involved in creating a prototype (i.e. not deployment ready) two-tiered web application. The baseline aims of this assignment involve creating an application that demonstrates the use of several specific pieces of HTML, CSS, JavaScript, and Node.js functionality. +-------------------------------------------------------------------------------------------------------------------------- -Baseline Requirements ---- +## Unscrmbl +So I kind of went overboard with this project, because I was bored this week, so sorry to whoever has to go through all the code. Basically the game is to just unscramble the word in large font and type the correct one in the input box below. With each wrong answer, nothing happens. Which each correct answer the score (stored in the server) increments and the word to scramble is pulled from the server. If the player is stuck they can use the Cheats button to get each letter starting from the first one. Or the 'answerkey()' function in the console. Once the player is satisfied with their score, they can type their name for the leaderboard in the top right (or not) and then click end game. The score will then be stored in the server, and then the game updates the top 5 scores from the leaderboard and places them sorted. There is also a delete everything button to delete all entries. For the basic CSS positioning, there is a left block and a center block that both have the display flex property. Essentially almost everything inside of them are centered with justify-content and align-content. The flex wrap and flex direction column are also applied to each of the blocks. -Note that there is a very large range of application areas and possibilities that meet these baseline requirements. Make your application do something useful! A todo list, storing / retrieving high scores for a very simple game, have a little fun with it. - -Your application is required to implement the following functionalities: - -- a `Server` which not only serves files, but also maintains a tabular dataset with 3 or more fields related to your application -- a `Results` functionality which shows the entire dataset residing in the server's memory -- a `Form/Entry` functionality which allows a user to add, modify, or delete data items residing in the server's memory -- a `Server Logic` which, upon receiving new or modified "incoming" data, includes and uses a function that adds at least one additional derived field to this incoming data before integrating it with the existing dataset -- the `Derived field` for a new row of data must be computed based on fields already existing in the row. For example, a `todo` dataset with `task`, `priority`, and `creation_date` may generate a new field `deadline` by looking at `creation_date` and `priority` - -Your application is required to demonstrate the use of the following concepts: - -HTML: -- One or more [HTML Forms](https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms), with any combination of form tags appropriate for the user input portion of the application -- A results page displaying all data currently available on the server. You will most likely use a `` tag for this, but `
+ + +
+ + + +
+ +
+
+

Unscramble!

+
+
+
+
+ +
+
+
- diff --git a/public/js/scripts.js b/public/js/scripts.js index de052eae..4025fceb 100755 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -1,3 +1,163 @@ -// Add some Javascript code here, to run on the front end. +'use strict'; -console.log("Welcome to assignment 2!") \ No newline at end of file +function start() { // runs a lot of the functions necessary for the first time (scrambled word, enter in the input) + newScrambled(); + updateScores(); + updateCurrentScore(); + let inp = document.querySelector('#answer'); + + inp.addEventListener('keyup', (e) => { + event.preventDefault(); + + if (e.code === 'Enter') { + guessWord(inp.value); + } + }); +} + + +function deleteEverything() { + endGame(); + fetch('/deleteEverything', { + method: 'GET' + }).then(() => { + document.querySelector('#currScore').innerHTML = 0; + updateScores(); + newScrambled(); + }); +} + + +function hint() { + let inp = document.querySelector('#answer'); + + fetch('/hint', { + method: 'POST', + body: inp.dataset.hint.length + }).then((res) => { + res.text().then(char => { + inp.dataset.hint = inp.dataset.hint ? inp.dataset.hint + char : char; + inp.value = inp.dataset.hint; + }); + }); +} + + +function updateCurrentScore() { + fetch('/getCurrScore', { + method: 'GET' + }).then((res) => { + res.text().then(score => { + document.querySelector('#currScore').innerHTML = score; + }); + }); +} + + +function updateScores() { + fetch('/getScores', { + method: 'GET' + }).then((res) => { + res.text().then((scores) => { + let insertDiv = document.querySelector('#serverscores'); + + scores = JSON.parse(scores); + scores = scores.sort((a, b) => parseInt(b.score) - parseInt(a.score)); + insertDiv.innerHTML = ''; + + scores.forEach((score, i) => { + if (i < 5){ + let ele = document.createElement('tr'); + + ele.innerHTML = `${score.name}: ${score.score}`; + insertDiv.appendChild(ele); + } + }); + }); + }); +} + + +function endGame() { // ends the game and sends score to the server + let name = document.querySelector('#boardName').value ? document.querySelector('#boardName').value : '???'; + let date = new Date(); + let scorejson = JSON.stringify({"date": date, "name": name.toUpperCase(), "score": 0}); + + fetch('/newScore', { + method: 'POST', + body: scorejson + }).then(() => { + updateScores(); + newScrambled(); + updateCurrentScore(); + }); +} + + +function changeInputUnderlines() { // Change all the css styling needed for the dynamic character underlines + let length = document.querySelector('#scrambledWord').innerText.length, + input = document.querySelector('#answer'), + charWidth = 1, + gap = 0.5 * charWidth, + totalWidth = length * (charWidth + gap); + + input.value = ''; + input.dataset.hint = ''; + input.maxLength = length; + input.style.width = `${totalWidth}ch`; + input.style.letterSpacing = `${gap}ch`; + input.style.background = `repeating-linear-gradient(to right, black 0, black ${charWidth}ch, transparent 0, transparent ${charWidth + gap}ch) 0 100% / ${totalWidth - gap}ch 2px no-repeat`; +} + + +function newScrambled() { // Changes the scrambled word to a new one from the server + document.querySelector('#scrambledWord').innerHTML = 'Loading...'; + fetch('/currentWord', { + method: 'GET' + }).then((res) => { + res.text().then((newWord) => { + document.querySelector('#scrambledWord').innerHTML = newWord; + changeInputUnderlines(); + }); + }); +} + + +function guessWord(guess) { // takes in a value and sends it to the server to see if its correct + let data = JSON.stringify({'guess': guess}); + fetch('/guess', { + method: 'POST', + body: data + }).then((res) => { + res.text().then((correct) => { // is either true or false to see if that was the correct word + let scrambled = document.querySelector('#scrambledWord'); + + if (correct === 'false') { + + scrambled.classList.add('incorrect'); + setTimeout(() => scrambled.classList.remove('incorrect'), 2500); + } else { + scrambled.classList.remove('incorrect'); + updateCurrentScore(); + newScrambled(); + } + }); + }); +} + + +function answerkey() { // prints the answer key + let randomwords = ['size', 'pipe', 'show', 'toy', 'zipper', 'throne', 'baby', 'seat', 'river', 'ocean', 'spade', + 'pump', 'cakes', 'skate', 'cat', 'vegetable', 'nut', 'furniture', 'tendency', 'car', 'sleet', 'truck', 'basket', 'writer', + 'fish', 'rock', 'ants', 'border', 'experience', 'kitty', 'flesh', 'servant', 'hydrant', 'planes', 'week', 'office', + 'dog', 'art', 'yak', 'distance', 'stocking', 'snails', 'playground', 'knot', 'curtain', 'wrench', 'daughter', 'seashore', + 'side', 'channel', 'cow', 'surprise', 'team', 'partner', 'paper', 'leg', 'arch', 'produce', 'bell', 'language', 'hope', + 'women', 'angle', 'cream', 'jar', 'respect', 'pigs', 'loaf', 'fly', 'time', 'uncle', 'move', 'earth', 'pies', 'flame', + 'door', 'place', 'wing', 'fact', 'cherries', 'need', 'knee', 'ground', 'key', 'farm', 'direction', 'crayon', 'authority', + 'idea', 'cake', 'winter', 'copper', 'son', 'cactus', 'caption', 'road', 'slope', 'trouble', 'finger', 'comparison']; + console.log(randomwords); +} + + window.onload = () => { // just runs start when page opens + start(); + }; \ No newline at end of file diff --git a/server.improved.js b/server.improved.js index 26673fc0..6a66da77 100644 --- a/server.improved.js +++ b/server.improved.js @@ -1,72 +1,159 @@ +'use strict'; + const http = require( 'http' ), - fs = require( 'fs' ), - // IMPORTANT: you must run `npm install` in the directory for this assignment - // to install the mime library used in the following line of code - mime = require( 'mime' ), - dir = 'public/', - port = 3000 + fs = require( 'fs' ), + mime = require( 'mime' ), + dir = 'public/', + port = 3000; + +/* + * Game Logic Code Here + */ + +const randomwords = ['size','pipe','show','toy','zipper','throne','baby','seat','river','ocean','spade', + 'pump','cakes','skate','cat','vegetable','nut','furniture','tendency','car','sleet','truck','basket','writer', + 'fish','rock','ants','border','experience','kitty','flesh','servant','hydrant','planes','week','office', + 'dog','art','yak','distance','stocking','snails','playground','knot','curtain','wrench','daughter','seashore', + 'side','channel','cow','surprise','team','partner','paper','leg','arch','produce','bell','language','hope', + 'women','angle','cream','jar','respect','pigs','loaf','fly','time','uncle','move','earth','pies','flame', + 'door','place','wing','fact','cherries','need','knee','ground','key','farm','direction','crayon','authority', + 'idea','cake','winter','copper','son','cactus','caption','road','slope','trouble','finger','comparison']; + +let scores = [{date: "2020-09-14T19:09:02.410Z", name: "MOM", score: 50, rating: 500}, // filler scores + {date: "2020-09-14T19:09:45.849Z", name: "DAD", score: 12, rating: 120}, + {date: "2020-09-14T19:09:52.299Z", name: "MAD", score: 9, rating: 90}, + {date: "2020-09-14T19:09:55.471Z", name: "DOG", score: 8, rating: 80}], + currentScore = 0, + currentWord = ''; + + +const scramble = (word) => { + let scrambled = word.split(''); + + for (let i = scrambled.length - 1; i > 0; i--) { + let j = Math.floor(Math.random() * i); + let temp = scrambled[i]; + + scrambled[i] = scrambled[j]; + scrambled[j] = temp; + } + return scrambled.join(''); +}; -const appdata = [ - { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, - { 'model': 'honda', 'year': 2004, 'mpg': 30 }, - { 'model': 'ford', 'year': 1987, 'mpg': 14} -] -const server = http.createServer( function( request,response ) { - if( request.method === 'GET' ) { - handleGet( request, response ) - }else if( request.method === 'POST' ){ - handlePost( request, response ) - } -}) -const handleGet = function( request, response ) { - const filename = dir + request.url.slice( 1 ) +/* + * Server Code Below Here + */ + +const server = http.createServer(function( request,response ) { + if (request.method === 'GET') { + handleGet(request, response); + + } else if (request.method === 'POST'){ + handlePost(request, response); + + } +}); + +const handleGet = function(request, response) { + const filename = dir + request.url.slice(1); + + if (request.url === '/') { + sendFile(response, 'public/index.html'); + + } else if (request.url === '/currentWord') { + currentWord = randomwords[Math.floor(Math.random() * randomwords.length)]; + + response.writeHead(200, {'Content-Type': 'text/plain'}); + response.end(scramble(currentWord)); + + } else if (request.url === '/getCurrScore') { + response.writeHead(200, {'Content-Type': 'text/plain'}); + response.end(currentScore.toString()); + + } else if (request.url === '/getScores') { + response.writeHead(200, {'Content-Type': 'text/plain'}); + response.end(JSON.stringify(scores)); + + } else if (request.url === '/deleteEverything') { + scores = []; + currentScore = 0; + currentWord = ''; + + response.writeHead(200, "OK"); + response.end(); + + } else { + sendFile(response, filename); + } +}; + +const handlePost = function(request, response) { + let dataString = ''; + + request.on('data', function(data) { + dataString += data; + }); + + if (request.url === '/guess'){ + request.on( 'end', function() { + let guess = JSON.parse(dataString).guess; + currentScore = guess === currentWord ? currentScore + 1 : currentScore; + + response.writeHead( 200, "OK", {'Content-Type': 'text/plain'}); + response.end((guess === currentWord).toString()); // sends the boolean in string format + }); + + } else if (request.url === '/newScore'){ + request.on('end', () => { + let scoreJSON = JSON.parse(dataString); - if( request.url === '/' ) { - sendFile( response, 'public/index.html' ) - }else{ - sendFile( response, filename ) - } -} + scoreJSON.score = currentScore; + scoreJSON.rating = scoreJSON.score * 10; -const handlePost = function( request, response ) { - let dataString = '' + scores.push(scoreJSON); + currentScore = 0; - request.on( 'data', function( data ) { - dataString += data - }) + response.writeHead(200, "OK"); + response.end(); + }); - request.on( 'end', function() { - console.log( JSON.parse( dataString ) ) + } else if (request.url === '/hint'){ + request.on('end', () => { + let hint = parseInt(dataString); + let ret = hint < currentWord.length ? currentWord[hint] : ''; - // ... do something with the data here!!! + response.writeHead(200, "OK", {'Content-Type': 'text/plain'}); + response.end(ret.toString()); - response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) - response.end() - }) -} + }); + } else { + response.writeHead(200); + response.end(); -const sendFile = function( response, filename ) { - const type = mime.getType( filename ) + } +}; - fs.readFile( filename, function( err, content ) { +const sendFile = function(response, filename) { + const type = mime.getType(filename); - // if the error = null, then we've loaded the file successfully - if( err === null ) { + fs.readFile(filename, function(err, content) { - // status code: https://httpstatuses.com - response.writeHeader( 200, { 'Content-Type': type }) - response.end( content ) + // if the error = null, then we've loaded the file successfully + if (err === null) { - }else{ + // status code: https://httpstatuses.com + response.writeHeader(200, {'Content-Type': type}); + response.end(content); - // file not found, error code 404 - response.writeHeader( 404 ) - response.end( '404 Error: File Not Found' ) + } else { + // file not found, error code 404 + response.writeHeader(404); + response.end('404 Error: File Not Found'); - } - }) -} + } + }); +}; -server.listen( process.env.PORT || port ) +server.listen(process.env.PORT || port, () => console.log('Listening on Port:', port));