Skip to content

Commit

Permalink
Merge pull request #85 from Capstone-Projects-2024-Spring/CS2STT-57-I…
Browse files Browse the repository at this point in the history
…ntegrate-dynamic-generation-endpoint-for-user

Dynamic Mode
  • Loading branch information
Dem0nMaxwell authored May 2, 2024
2 parents ebd6e03 + e6a621a commit 1b3a5f0
Show file tree
Hide file tree
Showing 6 changed files with 421 additions and 52 deletions.
6 changes: 3 additions & 3 deletions static/js/content/Menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ function Menu({}) {
Robot
</button>
</Link>
<Link id="importTextMode" to="/importText" className="menu-item" style={linkStyle}>
<Link id="dynamicMode" to="/dynamicMode" className="menu-item" style={linkStyle}>
<button style={buttonStyle}>
<img src="/static/pics/import_text_mode.png" alt="Import Text Image" style={{ width: '150px', height: '150px', marginBottom: '20px' }} />
Import Text Mode
<img src="/static/pics/import_text_mode.png" alt="Import Text Image" style={{ width: '150px', height: 'auto', marginBottom: '20px' }} />
Dynamic Mode
</button>
</Link>
<Link id="multiplayerMode" to="/multiplayer" className="menu-item" style={linkStyle}>
Expand Down
319 changes: 319 additions & 0 deletions static/js/reusable/DynamicMode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
function DynamicMode() {
const date = new Date();
let text = "Click start button to start!";
let words = text.split(" ");
const timeToType = 90000;
const charsPerLine = 120;
let avgTxtLen;
getAvgTxtLen("","words").then(avgLen=>{avgTxtLen=avgLen});
let currBlurbIndex = 0;
let currBlurb;
let blurbStartTime;

let currentCharIndex = 0; //only increment when user has typed correct letter
let currentWordIndex = 0;
let startTime;
//let timerInterval;
let userInputCorrectText = "";
let correctCharsTyped = 0; // Track correct characters typed
let correctLettersTyped = 0;
let totalCharsTyped = 0; // Track total characters typed

const intervalRef = React.useRef(null);


async function getAvgTxtLen(difficulty,form){
let avgTxtLen;
try {
const response = await fetch('/get_avg_txt_len/?difficulty='+difficulty+'&form='+form);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const avgTxtLenResponseStr = await response.text();
avgTxtLen = parseInt(avgTxtLenResponseStr);
} catch (error) {
console.error('There was a problem with the fetch operation:', error);
}
return avgTxtLen;
}

function startTimerInterval(){
intervalRef.current = setInterval(updateTimer, 1000);
}

function stopTimerInterval(){
clearInterval(intervalRef.current);
}

React.useEffect(() => {
return () => {
clearInterval(intervalRef.current);
};
}, []);

async function fetchTxt(wpm) {
let newText = "";
try {
const response = await fetch('/generate_text/?wpm='+wpm+'&form=words&amount='+charsPerLine/avgTxtLen);

if (!response.ok) {
throw new Error('Network response was not ok');
}

newText = await response.text() + " ";
} catch (error) {
console.error('There was a problem with the fetch operation:', error);
}
return newText;
}

async function postUserMetrics(wpm, accuracy, elapsedTime){
try{
const postData = {"wpm":wpm,"accuracy":accuracy,"mode":"Dynamic Mode","elapsedTime":elapsedTime/60,"date":date.toISOString()}
const response = await fetch('/update_db',{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postData)});
if(!response.ok){
throw new Error('Network response was not ok');
}
}
catch(error){
console.error('There was a problem with the fetch operation:', error);
}
}

//update text color as user types text
//green text if user typed correctly
//red background text if user typed incorrectly
/*
The whole function logic is based on following assumption, we divide text into 3 sections
correct text | incorrect text | untyped text
*/
//if you don't get what is going on here, open a type racer game and type some wrong text
function updateText() {
var str = text;
var userInputFullText = userInputCorrectText + document.getElementById("input-box").value;

var greenText = ""; //correct text
var redText = ""; //incorrect text
var uncoloredText = ""; //untyped text

//green text
//start index is fixed to 0
//end index is number of matched letters, until the first incorrect letter
var greenStartIndex = 0;
var greenEndIndex = 0;
var numMatchLetters = 0;
for (var i = 0; i < userInputFullText.length; i++) {
if (userInputFullText[i] == text[i]) { //what if userInputFullText is longer than text? could not happend because submission
numMatchLetters++;
} else {
break;
}
}

greenEndIndex = numMatchLetters;
greenText = text.substring(greenStartIndex, greenEndIndex);

//red text
//start index is the first unmatched letter, if it exists. It equals to greenEndIndex
//end index is the last index of user input text
var redStartIndex = greenEndIndex;
var redEndIndex = greenEndIndex;
if (numMatchLetters < userInputFullText.length) { //if number of matched letters less than input letters means there are wrong input letters
redEndIndex = userInputFullText.length > text.length ? text.length : userInputFullText.length; //in case user input text is longer than text
}

redText = text.substring(redStartIndex, redEndIndex);

//uncoloredText is the rest of the text starting from redEndIndex
uncoloredText = str.substring(redEndIndex);

/* debug
console.log("updateText debugging");
console.log("userInputFullText: " + userInputFullText);
console.log("greenText: " + greenText);
console.log("red text: " + redText);
console.log("uncoloredText: " + uncoloredText + "\n");
*/

var updatedText = `<span style="color: #9CCE52">${greenText}</span>` +
`<span style="background: #F0A3A3">${redText}</span>` + uncoloredText;

document.getElementById("text-display").innerHTML = updatedText;
}


async function startTimer() {
currentWordIndex = 0; //initializes value for play again
currentCharIndex = 0;
correctCharsTyped = 0; //Need to reset to prevent other games from using previous numbers
totalCharsTyped = 0;
currBlurbIndex = 0;
userInputCorrectText = "";
document.getElementById("input-box").value = "";
document.getElementById("result").innerHTML = "";

startTime = new Date().getTime();
blurbStartTime = startTime;
text = await fetchTxt(0);
currBlurb = text;
words = text.split(" ");

displayText();
enableInput();

//clearInterval(timerInterval);
//timerInterval = setInterval(updateTimer, 10);

stopTimerInterval();
startTimerInterval();
}

function updateTimer() {
const currentTime = new Date().getTime();
const timeLeft = (timeToType-(currentTime - startTime)) / 1000;
document.getElementById("result").innerHTML = `Timer: ${timeLeft.toFixed(0)} seconds`;
if(timeLeft<=0){
submitInput();
}
}

function displayText() {
document.getElementById("text-display").innerHTML = text;
}


function enableInput() {
document.getElementById("input-box").disabled = false;
document.getElementById("input-box").focus();
}

async function checkInput() {
var userInputText = document.getElementById("input-box").value;
var userInputLastChar = userInputText[userInputText.length - 1];

//updates text color
updateText();

//idk what this is
//if typed word matches with text word and last letter is space, clear input box and add word to userInputCorrectText
if (userInputText.substring(0, userInputText.length - 1) == words[currentWordIndex] && userInputLastChar == ' ') {
currentWordIndex++;
userInputCorrectText += userInputText; //saves correct text
document.getElementById("input-box").value = "";
}

if (userInputLastChar == text[currentCharIndex]) { //works but logic is bad
currentCharIndex++;
currBlurbIndex++;
correctCharsTyped++;
if(text[currentCharIndex]!=' '){
correctLettersTyped++;
}
}
totalCharsTyped++;
//Add more input once last letter of current blurb is typed
if (currBlurbIndex >= currBlurb.length-1) {
const endTime = new Date().getTime();
const elapsedTime = (endTime - blurbStartTime) / 1000;
const wpm = Math.round(((currBlurbIndex/5.0)/(elapsedTime/60)));
currBlurb = await fetchTxt(wpm);
text+=currBlurb;
currBlurbIndex=0
}
}

function submitInput() {
//clearInterval(timerInterval);
stopTimerInterval();
const endTime = new Date().getTime();
const elapsedTime = (endTime - startTime) / 1000;
const wordsPerMinute = Math.round((correctLettersTyped / 5.0) / (elapsedTime / 60.0));
console.log(correctCharsTyped);
let accuracy = 0;
if (totalCharsTyped!=0){
accuracy = (correctCharsTyped / totalCharsTyped) * 100 ;
}
document.getElementById("result").innerHTML = `Congratulations! You completed the game in ${elapsedTime.toFixed(2)} seconds. Your speed: ${wordsPerMinute} WPM. Your accuracy: ${accuracy.toFixed(2)}%`;
document.getElementById("input-box").value = "";
document.getElementById("input-box").disabled = true;
postUserMetrics(wordsPerMinute,accuracy,elapsedTime);
}

function stopTimer(){
stopTimerInterval();
document.getElementById("input-box").disabled = false;
document.getElementById("input-box").value = "";
userInputCorrectText = "";
currentCharIndex = 0;
document.getElementById("result").innerHTML = "";
currentWordIndex = 0; //initializes value for play again
updateText();
document.getElementById("text-display").innerHTML = "Click start button to start!";

}


function fillText(){
userInputCorrectText = text.substring(0, text.length-1);
currentCharIndex = text.length-1;
updateText();
//console.log("text: " + text);
//console.log("userInputCorrectText: " + userInputCorrectText);
}



function changeBackground(season) {
const body = document.body;
switch(season) {
case 'spring':
body.style.backgroundImage = "url('/static/pics/spring.jpg')";
break;

case 'winter':
body.style.backgroundImage = "url('/static/pics/winter.jpg')";
break;
case 'summer':
body.style.backgroundImage = "url('/static/pics/summer.jpg')";
break;
case 'autumn':
body.style.backgroundImage = "url('/static/pics/autumn.jpg')";
break;
default:
body.style.backgroundImage = "none";
}
}

return (
<div id="game-container">
<h1>Dynamic Mode</h1>
<div id="text-display">{text}</div>

<input type="text" id="input-box" onInput={checkInput} disabled />
<div id="result"></div>
<div id="holder"></div>
<div className="button-container">
<button onClick={startTimer}>Start</button>
<button onClick={stopTimer}>Reset</button>
<button onClick={fillText}>Fill Text</button>

{/* Dropdown menu */}
<div className="dropdown">
<button className="dropbtn">Cosmetic</button>
<div className="dropdown-content">
<button onClick={() => changeBackground('spring')}>Spring</button>
<button onClick={() => changeBackground('summer')}>Summer</button>
<button onClick={() => changeBackground('autumn')}>Autumn</button>
<button onClick={() => changeBackground('winter')}>Winter</button>
</div>
</div>
</div>
</div>
);

}
2 changes: 1 addition & 1 deletion static/js/reusable/Multiplayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function Multiplayer({userSession}) {
const elapsedTime = (currentTime - startTime) / 1000;

const typed = currentCharIndex + "/" + text.length;
const wpm = Math.round((currentCharIndex/5 / elapsedTime) * 60);
const wpm = Math.round((currentCharIndex / 5.0) / (elapsedTime / 60.0));
const accuracy = (text.length - wrongCharCount)/text.length*100;

document.querySelector(".progress-display").innerHTML = "Typed: " + typed;
Expand Down
8 changes: 7 additions & 1 deletion static/js/reusable/RobotOpponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function RobotOpponent() {


let correctCharsTyped = 0; // Track correct characters typed
let correctLettersTyped = 0;
let totalCharsTyped = 0; // Track total characters typed

async function getAvgTxtLen(difficulty,form){
Expand Down Expand Up @@ -177,6 +178,8 @@ function RobotOpponent() {
function startTimer() {
currentWordIndex = 0; //initializes value for play again
currentCharIndex = 0;
correctCharsTyped = 0; //Need to reset to prevent other games from using previous numbers
totalCharsTyped = 0;
userInputCorrectText = "";
document.getElementById("input-box").value = "";
document.getElementById("result").innerHTML = "";
Expand Down Expand Up @@ -259,6 +262,9 @@ function RobotOpponent() {
if (userInputLastChar == text[currentCharIndex]) { //works but logic is bad
currentCharIndex++;
correctCharsTyped++; // Increment correct characters typed
if(text[currentCharIndex]!=' '){
correctLettersTyped++;
}
}
totalCharsTyped++; // Increment total characters

Expand Down Expand Up @@ -297,7 +303,7 @@ function RobotOpponent() {
clearInterval(timerInterval);
const endTime = new Date().getTime();
const elapsedTime = (endTime - startTime) / 1000;
const wordsPerMinute = Math.round((currentCharIndex/5 / elapsedTime) * 60);
const wordsPerMinute = Math.round((correctLettersTyped/5 / elapsedTime) * 60);
const accuracy = (correctCharsTyped / totalCharsTyped) * 100; // Calculate accuracy
document.getElementById("result").innerHTML = `Congratulations! You completed the game in ${elapsedTime.toFixed(2)} seconds. Your speed: ${wordsPerMinute} WPM. Accuracy: ${accuracy.toFixed(2)}%`;
document.getElementById("input-box").value = "";
Expand Down
Loading

0 comments on commit 1b3a5f0

Please sign in to comment.