diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..be0bce0 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}\\server.improved.js" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index f229958..b02b2ef 100644 --- a/README.md +++ b/README.md @@ -1,98 +1,43 @@ -Assignment 2 - Short Stack: Basic Two-tier Web Application using HTML/CSS/JS and Node.js -=== - -Due: September 8th, by 11:59 AM. - -This assignment aims to introduce you to creating a prototype two-tiered web application. -Your application will include the use of HTML, CSS, JavaScript, and Node.js functionality, with active communication between the client and the server over the life of a user session. - -Baseline Requirements +# Assignment 1 for CS4241 +Link To the Project --- +https://a2-hao-oliverchen.glitch.me/ -There is a large range of application areas and possibilities that meet these baseline requirements. -Try to 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 (complete at least two) 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 `
+ + + + + + + + +
UsernameWinLossWinrate
+

Action --- add new user or delete existing user

+
+ + + + + + +
+ +

+ Current Player *if not specified, will pick the first user in the table* +

+ + + + + + + + + +
UsernameWinLossWinrate
+

Action --- edit current username or switch to an existing user

+
+ + + + + +
+
+ +
+ +
Rock Paper Scissors
+ + +
+
+

Player

+

0

+ +
+
+

Computer

+

0

+ +
+
+ +
Decide Your Move
+
+ + +
Moves Left: 5
+ + +
+ + + +
+ + +
+ + + +
+
+ + + + \ No newline at end of file diff --git a/public/js/scripts.js b/public/js/scripts.js index de052ea..3e79386 100644 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -1,3 +1,376 @@ // Add some Javascript code here, to run on the front end. -console.log("Welcome to assignment 2!") \ No newline at end of file +console.log("Welcome to assignment 2!") + +// Complete logic of game +const game = () => { + let playerScore = 0; + let computerScore = 0; + let moves = 0; + + + // Function to + const playGame = () => { + const rockBtn = document.querySelector('.rock'); + const paperBtn = document.querySelector('.paper'); + const scissorBtn = document.querySelector('.scissor'); + const playerOptions = [rockBtn, paperBtn, scissorBtn]; + const computerOptions = ['rock', 'paper', 'scissors'] + + // Function to start playing game + playerOptions.forEach(option => { + option.addEventListener('click', function () { + + const movesLeft = document.querySelector('.movesleft'); + moves++; + movesLeft.innerText = `Moves Left: ${5 - moves}`; + + + const choiceNumber = Math.floor(Math.random() * 3); + const computerChoice = computerOptions[choiceNumber]; + + // Function to check who wins + winner(this.innerText, computerChoice) + + // Calling gameOver function after 5 moves + if (moves == 5) { + gameOver(playerOptions, movesLeft); + } + }) + }) + + } + + // Function to decide winner + const winner = (player, computer) => { + const result = document.querySelector('.result'); + const playerScoreBoard = document.querySelector('.p-count'); + const computerScoreBoard = document.querySelector('.c-count'); + player = player.toLowerCase(); + computer = computer.toLowerCase(); + if (player === computer) { + result.textContent = 'Tie' + // increment both scores when a tie occurs + computerScore++; + computerScoreBoard.textContent = computerScore; + playerScore++; + playerScoreBoard.textContent = playerScore; + } + else if (player == 'rock') { + if (computer == 'paper') { + result.textContent = 'Computer Won'; + computerScore++; + computerScoreBoard.textContent = computerScore; + + } else { + result.textContent = 'Player Won' + playerScore++; + playerScoreBoard.textContent = playerScore; + } + } + else if (player == 'scissors') { + if (computer == 'rock') { + result.textContent = 'Computer Won'; + computerScore++; + computerScoreBoard.textContent = computerScore; + } else { + result.textContent = 'Player Won'; + playerScore++; + playerScoreBoard.textContent = playerScore; + } + } + else if (player == 'paper') { + if (computer == 'scissors') { + result.textContent = 'Computer Won'; + computerScore++; + computerScoreBoard.textContent = computerScore; + } else { + result.textContent = 'Player Won'; + playerScore++; + playerScoreBoard.textContent = playerScore; + } + } + } + + // Function to run when game is over + const gameOver = (playerOptions, movesLeft) => { + + const chooseMove = document.querySelector('.move'); + const result = document.querySelector('.result'); + const reloadBtn = document.querySelector('.reload'); + var gameStatus = 2; + + playerOptions.forEach(option => { + option.style.display = 'none'; + }) + + + chooseMove.innerText = 'Game Over!!' + movesLeft.style.display = 'none'; + + if (playerScore > computerScore) { + result.style.fontSize = '2rem'; + result.innerText = 'You Won The Game' + result.style.color = '#308D46'; + gameStatus = 1; + + } + else if (playerScore < computerScore) { + result.style.fontSize = '2rem'; + result.innerText = 'You Lost The Game'; + result.style.color = 'red'; + gameStatus = 0; + + } + else { + result.style.fontSize = '2rem'; + result.innerText = 'Tie'; + result.style.color = 'grey' + } + reloadBtn.innerText = 'Restart'; + reloadBtn.style.display = 'flex'; + if (gameStatus === 0) { + reloadBtn.onclick = handleGameLose + } + if (gameStatus === 1) { + reloadBtn.onclick = handleGameWin + } + reloadBtn.addEventListener('click', () => { + window.location.reload(); + }) + } + + + // Calling playGame function inside game + playGame(); + +} + +// Calling the game function +game(); + +const handleGameWin = function(e) { + // prevent default form action from being carried out + e.preventDefault() + + const newUser = document.querySelector('#currentUsername'), + json = { currentuser: "_WinCondition_", newusername: newUser.value }, + body = JSON.stringify(json) + + fetch('/submit', { + method: 'POST', + body + }) + .then(async function (response) { + let data = await response.json() + const currentUser = document.querySelector('#currentUsername') + if (currentUser.value !== null) { + const pos = findDataIndex(data, newUser.value) + makeCurrentTable(data,pos) + makeTable(data); + } + else{alert("Please choose a user!")} + }) + return false +} +const handleGameLose = function (e) { + // prevent default form action from being carried out + e.preventDefault() + + const newUser = document.querySelector('#currentUsername'), + json = { currentuser: "_LossCondition_", newusername: newUser.value }, + body = JSON.stringify(json) + + fetch('/submit', { + method: 'POST', + body + }) + .then(async function (response) { + let data = await response.json() + if (currentUser.value !== null) { + const pos = findDataIndex(data, newUser.value) + makeCurrentTable(data,pos) + makeTable(data); + } + else{alert("Please choose a user!")} + }) + return false + } + +const updateName = function (e) { + // prevent default form action from being carried out + e.preventDefault() + + const input = document.querySelector('#newUsername'), + currentUser = document.querySelector('#currentUsername'), + json = { currentuser: "__" + currentUser.value, newusername: input.value }, + body = JSON.stringify(json) + + fetch('/submit', { + method: 'POST', + body + }) + .then(async function (response) { + let data = await response.json() + const currentUser = document.querySelector('#username'); + const win = document.querySelector('#win'); + const loss = document.querySelector('#loss'); + const winrate = document.querySelector('#winrate'); + const currentUser_1 = document.querySelector('#currentUsername'); + makeTable(data) + // find the data + const pos = findDataIndex(data, input.value) + if (input.value !== '') { + currentUser.textContent = data[pos].playerName + win.textContent = data[pos].win + loss.textContent = data[pos].loss + if (data[pos].win !== 0 || data[pos].loss !== 0) { + winrate.textContent = ((data[pos].win / (data[pos].win + data[pos].loss)) * 100).toFixed(2) + } + winrate.textContent = data[pos].winrate + currentUser_1.value = data[pos].playerName + input.value = '' + } + else { alert("Field must be valid and not empty!") } + + }) + return false +} + +const displayData = function (e) { + // prevent default form action from being carried out + e.preventDefault() + + + const json = '', + body = JSON.stringify(json) + + fetch('/submit', { + method: 'POST', + body + }) + .then(async function (response) { + let data = await response.json() + const currentUser_1 = document.querySelector('#currentUsername'); + makeTable(data); + if (data.length !== 0) { + makeCurrentTable(data, 0) + currentUser_1.value = data[0].playerName + } + }) + return false +} + +const addUser = function (e) { + // prevent default form action from being carried out + e.preventDefault() + + const newUser = document.querySelector('#newUsername_2'), + json = { currentuser: "_yessir_", newusername: newUser.value }, + body = JSON.stringify(json) + + fetch('/submit', { + method: 'POST', + body + }) + .then(async function (response) { + let data = await response.json() + if (newUser.value === '') { + alert("Field must be valid and not empty!") + } + makeTable(data); + newUser.value = '' + }) + return false +} + +const deleteUser = function (e) { + // prevent default form action from being carried out + e.preventDefault() + + const newUser = document.querySelector('#unwanted'), + json = { currentuser: "_Delete_", newusername: newUser.value }, + body = JSON.stringify(json) + + fetch('/submit', { + method: 'POST', + body + }) + .then(async function (response) { + let data = await response.json() + if (newUser.value === '') { + alert("Field must be valid and not empty!") + } + if (data[0].playerName === newUser.value) { + alert("This game requires at least one player!") + } + //TODO:handle current player status + const currentUser = document.querySelector('#currentUsername') + const pos = findDataIndex(data, currentUser.value) + if (pos !== null) { + const table = document.getElementById('currentTable') + table.innerHTML = ""; + currentUser.value = null; + } + makeTable(data); + newUser.value = '' + }) + return false +} + +function makeTable(data) { + const table = document.getElementById('myTable') + table.innerHTML = ""; + for (let index = 0; index < data.length; index++) { + const row = ` + ${data[index].playerName} + ${data[index].win} + ${data[index].loss} + ${data[index].winrate} + ` + table.innerHTML += row + + } +} + +function makeCurrentTable(data, pos) { + const table = document.getElementById('currentTable') + table.innerHTML = ""; + + const row = ` + ${data[pos].playerName} + ${data[pos].win} + ${data[pos].loss} + ${data[pos].winrate} + ` + table.innerHTML = row +} + +function findDataIndex(data, input) { + for (let i = 0; i < data.length; i++) { + if (data[i].playerName === input) { + return i; + } + } +} + +function checkValidity(data, newName) { + let x = true; + for (let index = 0; index < data.length; index++) { + if (data[index].playerName === newName) { + x = false + } + } + return x; +} + +window.onload = function () { + const update_btn = document.querySelector('#updateUsername') + update_btn.onclick = updateName + const display_btn = document.querySelector('#display') + display_btn.onclick = displayData + const add_btn = document.querySelector('#add') + add_btn.onclick = addUser + const delete_btn = document.querySelector('#delete') + delete_btn.onclick = deleteUser +} \ No newline at end of file diff --git a/server.improved.js b/server.improved.js index 26673fc..f6a9c33 100644 --- a/server.improved.js +++ b/server.improved.js @@ -1,72 +1,132 @@ -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 +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 const appdata = [ - { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, - { 'model': 'honda', 'year': 2004, 'mpg': 30 }, - { 'model': 'ford', 'year': 1987, 'mpg': 14} + { playerName: "oliver", win: 0, loss: 0, winrate: 0 } ] -const server = http.createServer( function( request,response ) { - if( request.method === 'GET' ) { - handleGet( request, response ) - }else if( request.method === 'POST' ){ - handlePost( request, response ) +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 ) +const handleGet = function (request, response) { + const filename = dir + request.url.slice(1) - if( request.url === '/' ) { - sendFile( response, 'public/index.html' ) - }else{ - sendFile( response, filename ) + if (request.url === '/') { + sendFile(response, 'public/index.html') + } else { + sendFile(response, filename) } } -const handlePost = function( request, response ) { +const handlePost = function (request, response) { let dataString = '' - request.on( 'data', function( data ) { - dataString += data + request.on('data', function (data) { + dataString += data }) - request.on( 'end', function() { - console.log( JSON.parse( dataString ) ) - + request.on('end', function () { + console.log(JSON.parse(dataString)) // ... do something with the data here!!! - - response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) - response.end() + oldName = JSON.parse(dataString).currentuser + newName = JSON.parse(dataString).newusername + + if (oldName === '_WinCondition_') { + const newData = appdata.map(item => { + if (item.playerName === newName) { + return { ...item, win: item.win+1, winrate: (((item.win + 1) / (item.loss + item.win + 1)) * 100).toFixed(2) } + } + return item + }) + updateUserArray(appdata, newData) + } + if (oldName === '_LossCondition_') { + const newData = appdata.map(item => { + if (item.playerName === newName) { + return { ...item, loss: item.loss+1, winrate: (((item.win) / (item.loss + item.win + 1)) * 100).toFixed(2) } + } + return item + }) + updateUserArray(appdata, newData) + } + + if (newName !== '') { + if (checkValidity(appdata, newName)) { + const newData = appdata.map(item => { + if ("__" + item.playerName === oldName) { + return { ...item, playerName: newName } + } + return item + }) + updateUserArray(appdata, newData) + } + if (oldName === '_yessir_' && checkValidity(appdata, newName)) { + appdata[appdata.length] = { playerName: newName, win: 0, loss: 0, winrate: 0 } + } + if (oldName === '_Delete_' && !checkValidity(appdata, newName) && appdata.length > 1) { + removeData(appdata, newName) + } + } + console.log(JSON.stringify(appdata)) + response.writeHead(200, "OK", { 'Content-Type': 'text/plain' }) + response.end(JSON.stringify(appdata)) }) } -const sendFile = function( response, filename ) { - const type = mime.getType( filename ) +const sendFile = function (response, filename) { + const type = mime.getType(filename) - fs.readFile( filename, function( err, content ) { + fs.readFile(filename, function (err, content) { - // if the error = null, then we've loaded the file successfully - if( err === null ) { + // if the error = null, then we've loaded the file successfully + if (err === null) { - // status code: https://httpstatuses.com - response.writeHeader( 200, { 'Content-Type': type }) - response.end( content ) + // status code: https://httpstatuses.com + response.writeHeader(200, { 'Content-Type': type }) + response.end(content) - }else{ + } else { - // file not found, error code 404 - response.writeHeader( 404 ) - response.end( '404 Error: File Not Found' ) + // 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) + +function updateUserArray(oldData, newData) { + for (let index = 0; index < newData.length; index++) { + oldData[index] = newData[index]; + } +} + +function checkValidity(data, newName) { + let x = true; + for (let index = 0; index < data.length; index++) { + if (data[index].playerName !== null && data[index].playerName === newName) { + x = false + } + } + return x; +} + +function removeData(data, target) { + for (let i = 0; i < data.length; i++) { + if (data[i].playerName === target) { + data.splice(i, 1) + } + } +} \ No newline at end of file