| @ -1,84 +1,29 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <!-- Meta --> | |||
| <meta charset="UTF-8" /> | |||
| <title>Trend Spotter | Game</title> | |||
| <!-- / --> | |||
| <!-- Styles --> | |||
| <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> | |||
| <link href="https://fonts.googleapis.com/css?family=Josefin+Sans:100,300,400,600,700" rel="stylesheet"> | |||
| <link href="https://fonts.googleapis.com/css?family=Poppins:100,300,400,500,700,800,900" rel="stylesheet"> | |||
| <link rel="stylesheet" href="app.css"> | |||
| <!-- / --> | |||
| </head> | |||
| <body> | |||
| <div class=""> | |||
| <div class="row w-100"> | |||
| <div class="col-12"> | |||
| <div class="logo w-100">trend spot</div> | |||
| </div> | |||
| </div> | |||
| <div class="container-fluid game"> | |||
| <div class="row"> | |||
| <!-- Main --> | |||
| <div class="col-8 game__main"> | |||
| <div class="game__round">Round 4/7</div> | |||
| <div class="row w-100"> | |||
| <div class="col-8"> | |||
| <input class="w-100 game__phrase--entry" type="text"> | |||
| </div> | |||
| <div class="col-4"> | |||
| <div class="game__phrase--clue">car</div> | |||
| </div> | |||
| <div class="col-xs-12 col-sm-7"> | |||
| <a class="logo"> | |||
| <div>WHAT</div> | |||
| <div>THE </div> | |||
| <div>TREND?!</div> | |||
| </a> | |||
| <div class="game__main"> | |||
| <form onSubmit={this.sendNickname}> | |||
| <input class="login__input" maxLength="30" placeholder="Nickname" /> | |||
| <span> | |||
| <button class="game__submit">Let's Play!</button> | |||
| </span> | |||
| </form> | |||
| </div> | |||
| <button class="game__submit">Submit my answer!</button> | |||
| <!--<div class="game__user-score">jesseg89: 20,005 points</div>--> | |||
| <div class="game__room">You're in <em>dave61's room</em> | 12/20 players</div> | |||
| <a href="lobby.html" class="game__exit">Leave Game</a> | |||
| </div> | |||
| <!-- / --> | |||
| <div class="col-xs-12 col-sm-5 game__sidebar"> | |||
| <canvas class="scene scene--full" id="scene" ></canvas> | |||
| <div class="col-4 col-sm-3 game__sidebar"> | |||
| <div class="game__timer">60 seconds</div> | |||
| <ol class="game__players"> | |||
| <li class="game__player"> | |||
| <span class="game__player-name">John Daniels</span> | |||
| <span class="game__player-score">10,000 points</span> | |||
| <span class="game__player-ready">X</span> | |||
| </li> | |||
| <li class="game__player"> | |||
| <span class="game__player-name">John Daniels</span> | |||
| <span class="game__player-score">10,000 points</span> | |||
| <span class="game__player-ready">X</span> | |||
| </li> | |||
| <li class="game__player"> | |||
| <span class="game__player-name">John Daniels</span> | |||
| <span class="game__player-score">10,000 points</span> | |||
| <span class="game__player-ready">X</span> | |||
| </li> | |||
| </ol> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- Scripts --> | |||
| <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> | |||
| <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script> | |||
| <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> | |||
| <script src="scripts/index.js"></script> | |||
| <!-- / --> | |||
| </body> | |||
| </html> | |||
| </div> | |||
| @ -0,0 +1,14 @@ | |||
| <div class="container"> | |||
| <div class="row"> | |||
| <ul> | |||
| {for room in rooms} | |||
| <li class="lobby__room"> | |||
| <span class="lobby__room-name">{room.name}</span> | |||
| <span class="lobby__room-occupancy">{room.occupancy}</span> | |||
| <span class="lobby__room-status">{room.status}</span> | |||
| </li> | |||
| {/for} | |||
| </ul> | |||
| </div> | |||
| <button>Create Room<button> | |||
| </div> | |||
| @ -0,0 +1,73 @@ | |||
| //gets the trending data | |||
| const trendingAPI = require("./trendsAPI.js"); | |||
| class Player | |||
| { | |||
| constructor(s) | |||
| { | |||
| //name of the user | |||
| this.name = null; | |||
| //players socket | |||
| this.socket = s; | |||
| //score of the player | |||
| this.score = 0; | |||
| //reference to the room -- might not need this | |||
| this.room = null; | |||
| //the word the user selected for current round | |||
| this.submission = ''; | |||
| this.roundScore = 0; | |||
| //logs the user data so we can record it to data base at end of round | |||
| this.log = []; | |||
| } | |||
| /** | |||
| * generate the json object used in 'roomUpdate' socket io event | |||
| * | |||
| * return {name: score: word:} | |||
| */ | |||
| genJASON() | |||
| { | |||
| var result = new Object(); | |||
| result.name = this.name; | |||
| result.score = this.score; | |||
| result.word = this.submission; | |||
| return result; | |||
| } | |||
| /** | |||
| * data -- literally a string | |||
| * @param data | |||
| */ | |||
| selectWord(data) | |||
| { | |||
| var w = data + " " + this.room.currentWord; | |||
| this.submission = data; | |||
| //console.log(w); | |||
| this.room.update(); | |||
| return new Promise(function(resolve, reject) | |||
| { | |||
| trendingAPI.getPopularity(w).then(function(result) | |||
| { | |||
| console.log("api result for " + result + w); | |||
| resolve(result); | |||
| }).catch(function(err){ | |||
| console.log(err); | |||
| }) | |||
| }); | |||
| } | |||
| } | |||
| module.exports = Player; | |||
| @ -0,0 +1,281 @@ | |||
| //used for the getting the word array | |||
| const utils = require("./utils.js"); | |||
| /** | |||
| * Object used for storing rooms | |||
| * @param capacityP -- the number of people that can be in room | |||
| * @param pass -- the room password -- null if none | |||
| * @param owner -- the person who is creating the room | |||
| */ | |||
| class Room | |||
| { | |||
| constructor(capacityP, pass, owner) | |||
| { | |||
| //max capacity of room -- default is 4 for now | |||
| this.capacity = capacityP; | |||
| //name of the room | |||
| this.roomName = owner.name; | |||
| //list of words used in the game | |||
| //7 for now will change later to be room specific | |||
| this.words = utils.getRandomWords(7); | |||
| this.currentWord = this.words.pop(); | |||
| //list players -- so we can push requests to them | |||
| this.users = []; | |||
| //increments when rounds pass | |||
| this.currentRound = 0; | |||
| // the password of the room -- null if no password | |||
| this.password = pass; | |||
| /** | |||
| 1 = Waiting for users | |||
| 2 = Word shown, Waiting for response from users | |||
| 3 = Showing Result | |||
| 4 = Game Over, Display Final Results | |||
| */ | |||
| this.state = 1; | |||
| this.addUser(owner); | |||
| } | |||
| /** | |||
| * generate the json object used in 'roomUpdate' socket io event | |||
| * | |||
| * return {name: score: word:} | |||
| */ | |||
| genJASON() | |||
| { | |||
| var result = new Object(); | |||
| result.name = this.roomName; | |||
| result.users = this.users; | |||
| return result; | |||
| } | |||
| /** | |||
| * creates json to send in the 'roomUpdate' socket event | |||
| * | |||
| * {users: gameState: roundWinner: currentWord: } | |||
| */ | |||
| generateRoomUpdate() | |||
| { | |||
| var result = new Object(); | |||
| result.users = []; | |||
| this.users.forEach(function(u) | |||
| { | |||
| result.users.push(u.genJASON()); | |||
| }); | |||
| //sort the users based on score | |||
| var countOuter = 0; | |||
| var countInner = 0; | |||
| var countSwap = 0; | |||
| // var swapped; | |||
| // do | |||
| // { | |||
| // countOuter++; | |||
| // swapped = false; | |||
| // for(var i = 0; i < result.users.length; i++) | |||
| // { | |||
| // countInner++; | |||
| // if(result.users[i].score && result.users[i + 1].score && | |||
| // result.users[i].score > result.users[i + 1].score) | |||
| // { | |||
| // countSwap++; | |||
| // var temp = result.users[i]; | |||
| // result.users[i] = result.users[j]; | |||
| // result.users[j] = temp; | |||
| // swapped = true; | |||
| // } | |||
| // } | |||
| // } while(swapped); | |||
| result.gameState = this.state; | |||
| //sets round winner | |||
| var rWinner = -1; | |||
| for(var i = 0; i < this.users.length; i++) | |||
| { | |||
| if(rWinner < this.users[i].roundScore) | |||
| { | |||
| result.roundWinner = this.users[i].name; | |||
| rWinner = this.users[i].roundScore; | |||
| } | |||
| } | |||
| result.currentWord = this.currentWord; | |||
| return result; | |||
| } | |||
| /** | |||
| * adds a user to a room | |||
| * @param p | |||
| * return 0 if they could join | |||
| */ | |||
| addUser(player) | |||
| { | |||
| //console.log("user added"); | |||
| //check if room is not full | |||
| this.users.push(player); | |||
| player.room = this; | |||
| if(this.users.length == this.capacity) | |||
| { | |||
| this.state = 2; | |||
| } | |||
| console.log("user added to room " + player.name); | |||
| //console.log(this.users); | |||
| this.update(); | |||
| } | |||
| /** | |||
| * Removes a specific user from the room and adjusts the size of the array | |||
| * if the array is empty, the room closes | |||
| * @param p | |||
| */ | |||
| removeUser(p) | |||
| { | |||
| console.log("remove users fnc called"); | |||
| var temp = new Array(); | |||
| for(var i = 0; i < temp.length; i++) | |||
| { | |||
| if(p.name === this.users[i].name) | |||
| { | |||
| } | |||
| else | |||
| { | |||
| temp.push(this.users[i]); | |||
| } | |||
| } | |||
| this.users = temp; | |||
| //if room is empty remove the room from rooms list | |||
| if(this.users.length == 0) | |||
| { | |||
| console.log("room scrubbed"); | |||
| delete rooms[this.roomName]; | |||
| } | |||
| this.update(); | |||
| } | |||
| /** | |||
| * Whether or not a user can join this room -- checks for number of people are | |||
| * already in the room and the password | |||
| * @param p | |||
| * @returns {boolean} | |||
| */ | |||
| canJoin(p) | |||
| { | |||
| if(this.password == null) | |||
| { | |||
| return (this.users.length < this.capacity); | |||
| } | |||
| else | |||
| { | |||
| return (this.users.length < this.capacity) && (p === this.password); | |||
| } | |||
| } | |||
| /** | |||
| * starts new round for the room -- called once all the players have submitted | |||
| */ | |||
| newRound() | |||
| { | |||
| console.log("new round started"); | |||
| if(this.words.length == 0) | |||
| { | |||
| this.state == 4; | |||
| } | |||
| else | |||
| { | |||
| this.currentRound++; | |||
| this.users.forEach(function(u) | |||
| { | |||
| u.submission = ''; | |||
| }); | |||
| this.currentWord = this.words.pop(); | |||
| this.state = 2; | |||
| } | |||
| this.sendRoomUpdate(); | |||
| } | |||
| //updates room variables | |||
| update() | |||
| { | |||
| switch(this.state) | |||
| { | |||
| case 1: //waiting for users to join | |||
| { | |||
| if(this.users.length == this.capacity) | |||
| { | |||
| this.newRound(); | |||
| } | |||
| break; | |||
| } | |||
| case 2: // waiting for responses | |||
| { | |||
| var flag = true; | |||
| var test = ""; | |||
| this.users.forEach(function(u) | |||
| { | |||
| test+=u.submission; | |||
| if(u.submission === '') | |||
| { | |||
| flag = false; | |||
| } | |||
| }); | |||
| console.log("big stuff " + test); | |||
| if(flag) | |||
| { | |||
| this.state = 3; | |||
| this.newRound(); | |||
| // setTimeout(function() { | |||
| // | |||
| // }, 4000); | |||
| } | |||
| break; | |||
| } | |||
| case 3: // showing results -- time out fnc | |||
| { | |||
| console.log("error &&&&&&&&&&&&&&&&&&"); | |||
| break; | |||
| } | |||
| case 4: //game over display final result | |||
| { | |||
| //sqlStuff.dumpRoom(this); | |||
| break; | |||
| } | |||
| default: | |||
| { | |||
| console.log("You don goof up") | |||
| } | |||
| } | |||
| console.log(this.state + " state"); | |||
| this.sendRoomUpdate(); | |||
| } | |||
| } | |||
| module.exports = Room; | |||
| @ -0,0 +1,89 @@ | |||
| const googt = require('google-trends-api'); | |||
| const DAY = 1000 * 60 * 60 * 24; // 1 day in milliseconds | |||
| const YEAR = DAY * 365; // 1 year in milliseconds | |||
| const GEO = 'US'; //the scope of the trends | |||
| /** | |||
| desc: helper function for getYearStats, gets list of popularity for days in | |||
| the month. | |||
| */ | |||
| function getMonthStats(word, month){ | |||
| return new Promise(function(resolve, reject){ | |||
| //set up query for 1 month length | |||
| googt.interestOverTime({ | |||
| keyword:word, | |||
| startTime:new Date(Date.now() - (YEAR * month/12)), | |||
| endTime:new Date(Date.now() - (YEAR * (month-1)/12)) | |||
| }).then(function(results){ | |||
| //parse the json, fill return array w tuples of date + value | |||
| var times = JSON.parse(results).default.timelineData; | |||
| var ret = []; | |||
| for(var i = 0; i < times.length; ++i){ | |||
| var tup = new Object(); | |||
| tup.time = times[i].formattedTime; | |||
| tup.value = times[i].value[0]; | |||
| ret[i] = tup; | |||
| } | |||
| resolve(ret); | |||
| }); | |||
| }); | |||
| } | |||
| module.exports= | |||
| { | |||
| /* | |||
| desc: returns an integer score for the word over the day | |||
| */ | |||
| getPopularity: function(word) | |||
| { | |||
| //must be a promise since call to trends API is async | |||
| return new Promise(function(resolve, reject){ | |||
| //specifies the keyword, time interval, granularity, and region | |||
| googt.interestOverTime({keyword:word, | |||
| startTime:new Date(Date.now() - DAY),granularTimeResolution:true, | |||
| geo:GEO | |||
| }) | |||
| .then(function(results){ | |||
| //turn into json object | |||
| data = JSON.parse(results).default.timelineData; | |||
| //add up values | |||
| var total = 0; | |||
| data.forEach(function(element){ | |||
| total += element.value[0]; | |||
| }) | |||
| //tell function to return | |||
| console.log("********************" + total); | |||
| //pl.selectWord2(total); | |||
| resolve(total); | |||
| }).catch(function(err){ | |||
| reject("Google Trends Query Failed"); | |||
| }); | |||
| }); | |||
| }, | |||
| /** | |||
| desc: returns a list of tuples (date, value) representing word's | |||
| popularity for the past year | |||
| */ | |||
| /* | |||
| getYearStats: async function(word){ | |||
| var ret = []; | |||
| for (var i = 9; i > 0; --i) { | |||
| await getMonthStats(word,i).then(function(data){ | |||
| ret = ret.concat(data); | |||
| }); | |||
| console.log(i); | |||
| } | |||
| return ret; | |||
| } | |||
| */ | |||
| }; | |||
| @ -0,0 +1,37 @@ | |||
| var fs = require('fs'); | |||
| const WORD_FILE_PATH = '../../words/words.txt'; | |||
| //loads words from word file | |||
| var words = []; | |||
| var data = fs.readFileSync(WORD_FILE_PATH, 'utf8'); | |||
| var lines = data.split('\n'); | |||
| lines.forEach(function(element){ | |||
| words.push(element); | |||
| }); | |||
| module.exports= | |||
| { | |||
| /** | |||
| * returns a specific amount of words -- unique | |||
| * @param num the number of words | |||
| * @returns {Array} the random, unique words | |||
| */ | |||
| getRandomWords : function(num) | |||
| { | |||
| var rwords = []; | |||
| for(var i = 0; i < num; ++i){ | |||
| var randindex = Math.round((Math.random() * (words.length - 1))); | |||
| var newword = words[randindex]; | |||
| var uniq = true; | |||
| rwords.forEach(function(element){ | |||
| if(newword === element){ | |||
| --i; | |||
| uniq = false; | |||
| } | |||
| }); | |||
| if(uniq)rwords.push(newword); | |||
| } | |||
| return rwords; | |||
| } | |||
| }; | |||