Better way to create text based game [closed] - javascript

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
Right, i was uncertain if I should post this as it is a little vague but I really would like some help with this so I will try explain as best as possible.
The Idea:
To create a text based game using Javascript/jQuery, the game will be that of one where a story is being told and you get to pick the options.
My idea was to use a textarea to allow input from the user (select a option) and output text (from the story).
How far have I got?
Well this is what I have created so far.
JavaScript/jQuery:
var current, text, location, option1, option2;
location = ''; // house, car, moon
current = 0;
gameOver = false;
pick = false;
jQuery("textarea").keypress(function (e) {
if (e.which == 13) {
var content = this.value;
var lastLine = content.substr(content.lastIndexOf("\n") + 1);
// Story
if (current == 0 && pick == false) {
option1 = 'Look around';
option2 = 'Check you have arms (Check arms)';
text = 'You open your eyes \n\nOptions: \n' + option1 + '\n' + option2;
pick = true;
} else if (current == 0 && lastLine == 'Check arms' && pick == true) {
text = 'You check your arms, they seem fine';
pick = false;
} else if (current == 0 && lastLine == 'Look around' && pick == true || current == 2 && lastLine == 'Get Out') {
option1 = 'Walk to a nearby house';
option2 = 'Get in a rocket that is next to you (Get in rocket)';
text = 'You do a 360 spin, you see you have limited options \n\nOptions: \n' + option1 + '\n' + option2;
pick = false;
if (current == 2 && lastLine == 'Get Out') {
current = 1;
} else {
current++;
}
}
//House Story
else if (current == 1 && lastLine == 'Walk to house' && pick == false) {
option1 = 'Knock on the front door';
option2 = 'Jump through the front window';
text = 'You walk to the house and see there are no lights on, the building is old and appears to be burnt out\n\nOptions: \n ' + option1 + '\n ' + option2;
pick = false;
current++;
}
// Rocket story
else if (current == 1 && lastLine == 'Get in rocket' && pick == false) {
option1 = 'Get out of the rocket(Get out)';
option2 = 'Hit the biggest button you can find(Hit Button)';
text = 'You hop into the rocket, there are a lot of buttons infront of you\n\nOptions: \n ' + option1 + '\n ' + option2;
pick = false;
current++;
}
$('textarea ').val($('textarea ').val() + '\n\n ' + text + '\n ');
}
});
It works (kinda) but it is getting complicated to code like this. To me its very messy and I have tried to re-write it but I cannot find a way to make this neat/ easier to code.
Have a look at the demo:
Please do take a look at the demo if you wish to try and help me as you will get a good idea what I am trying to achieve.
Demo walk-through:
DEMO HERE
In the textarea click enter to start
Type one of the options to progress in the game (Options: Check arms or Look around)
Type one of the options to progress in the game (Options: Walk to a nearby house or Get in rocket)
End of demo
Note: After typing a option click enter to continue. At the moment all options must be typed exactly as seen (If option has brackets you type that instead)
This is a short demo but you should get the point. I have searched around and cant find/think of a suitable method to code this game.
Get to the point, what's the question?
My questions is: What is a suitable method to code this game?
Suitable: Easy to maintain/read + add new story "parts" etc.

I would go with some kind of strategy pattern. Create i.e. a Game constructor
var Game = new function(strategy) {
this.strategy = strategy;
}
Game.prototyp.playScene = function() {
return this.strategy();
}
Then you can create scenes where you would place your logic
var sceneOne = function() {
console.log('First scene logic here');
}
var sceneTwo = function() {
console.log('Second scene logic here');
}
and finally you can call these logics as follows:
var game;
if(e.which == 13) {
if(condition1) {
game = new Game(sceneOne);
} else {
game = new Game(sceneTwo);
}
game.playScene();
}
fiddle

The biggest issue I see here is that your code is going to grow and grow as your game/story expands. You're actually writing the logic of your game in the code itself.
As an alternative I would suggest splitting out your logic into steps and logic. For example:
var allTiles =
[
{location: 'Forest', description: 'Deep, dark and scary'},
{location: 'Castle', description: 'High, made from stone and very dramatic'},
];
var currentState =
{
equipment: ['Sword', 'Bow', '3 gold coins'];
currentLocationIndex: 0
};
These may of course be in different files so you can add locations to your world.
Next you need your core logic class, this will look a lot like the one you've already got:
jQuery("textarea").keypress(function (e) {
var currentLocation = allTiles[currentState.currentLocationIndex];
printDescription(currentLocation.Description);
// process commands... into pseudo code territory
if(userDoesAction1){
currentLocation.doAction1();
}
}
I've not gone into massive detail here - it will depend very much on the structure of your game. Personally I like the idea of creating an array functions in your location which are things you can do at your location... actually, JS is a very nice language to do this sort of game!

You can use my jQuery terminal. Then you can write core of the game that act on data in JSON object. If you write something like this you will be able to change the data (update or replace) without need to change the code, it's called data driven programming (Eric Raymond write nice chapter in his book about it)

When making games i think that an Object Oriented approach is the best to keep things separated and easy to code and find:
For example you could separate the different rooms in your game in different function structures.
var parentFunction = function(){
var protectedValue = ‘variable’;
var childFunction = function(){
alert(protectedValue);
}
childFunction();
}
var object = new parentFunction();
You initialize the function every time you move to the related ambient, and put the possible actions that the player can take in child-functions.
But i guess the answer would be too big here: you should spend some time before starting to make a scheme of the logic that your game will follow and how you want to separate things (and how they relate to each other).

Related

Can't get values past array[0] to translate properly

Okay, to start with I should mention this is a very small personal project, and I've only have a handful of coding classes several years ago now. I can figure out a lot of the (very) basics, but have a hard time troubleshooting. I'm in a little bit over my head here, and need a dumbed down solution.
I'm trying to put together a VERY simple translator that takes in a word or sentence from the user via a text input box, puts each word of the string into an array, translates each word in order, then spits out each translated word in the order it was input. For example, typing "I like cats" would output "Ich mag Katze" in German.
I've got most of it, but I CAN'T get anything but the first array element to translate. It comes out like "Ich like cats".
I've used a loop, probably because I'm an amateur and don't know another way of doing this, and I'd rather not use any libraries or anything. This is a very small project I want to have a couple of friends utilize locally; and I know there has to be some very simple code that will just take a string, put it into an array, swap one word for another word, and then output the results, but I'm damned if I can make it work.
What I currently have is the closest I've gotten, but like I said, it doesn't work. I've jerry-rigged the loop and clearly that's the totally wrong approach, but I can't see the forest for the trees. If you can help me, please make it "Javascript for Babies" picture book levels of simple, I cannot stress enough how inexperienced I am. This is just supposed to be a fun little extra thing for my D&D group.
function checkForTranslation(input, outputDiv) {
var input = document.getElementById("inputTextField").value;
var outputDiv = document.getElementById("translationOutputDiv");
input = input.toLowerCase();
//puts user input into an array and then outputs it word by word
const myArray = input.split(" "); //added .split, thank you James, still otherwise broken
let output = "";
let translation = "";
for (let i = 0; i < myArray.length; i++) {
output += myArray[i]; //up to here, this works perfectly to put each word in the string into an array
//prints all words but doesnt translate the second onwards
translation += myArray[i];
if (output == "") {
//document.getElementById("print2").innerHTML = "Translation Here";
}
else if (output == "apple") {
translation = "x-ray";
}
else if (output == "banana") {
translation = "yak";
}
else {
translation = "???";
}
output += " "; //adds a space when displaying original user input
} // END FOR LOOP
document.getElementById("print").innerHTML = output; //this outputs the original user input to the screen
document.getElementById("print3").innerHTML = translation; //this should output the translated output to the screen
} // END FUNCTION CHECKFORTRANSLATION
What it looks like
P.S. I'm not worried about Best Practices here, this is supposed to be a quickie project that I can send to a couple friends and they can open the HTML doc, saved locally, in their browser when they want to mess around with it if they want their half-orc character to say "die by my hammer!" or something. If you have suggestions for making it neater great, but I'm not worried about a mess, no one is going to be reading this but me, and hopefully once it's fixed I'll never have to read it again either!
Since it is a manual simple translation, you should just create a "dictionary" and use it to get the translations.
var dictionary = {
"apple": "x-ray",
"banana": "yak"
}
function checkForTranslation() {
var input = document.getElementById("inputTextField").value.toLowerCase();
var words = input
.split(' ') // split string to words
.filter(function(word) { // remove empty words
return word.length > 0
});
var translatedWords = words.map(function(word) {
var wordTranslation = dictionary[word]; // get from dictionary
if (wordTranslation) {
return wordTranslation;
} else { // if word was not found in dictionary
return "???";
}
});
var translatedText = translatedWords.join(' ');
document.getElementById("translationOutputDiv").innerHTML = translatedText;
}
document.getElementById('translate').addEventListener('click', function() {
checkForTranslation();
});
<input type="text" id="inputTextField" />
<button id="translate">translate</button>
<br/>
<hr />
<div id="translationOutputDiv"></div>
Or if you want it a little more organized, you could use
const dictionary = {
"apple": "x-ray",
"banana": "yak"
}
function getTranslation(string) {
return string
.toLowerCase()
.split(' ')
.filter(word => word)
.map(word => dictionary[word] || '???')
.join(' ');
}
function translate(inputEl, outputEl) {
outputEl.innerHTML = getTranslation(inputEl.value);
}
document.querySelector('#translate').addEventListener('click', function() {
const input = document.querySelector('#inputTextField');
const output = document.querySelector('#translationOutputDiv');
translate(input, output);
});
<input type="text" id="inputTextField" />
<button id="translate">translate</button>
<br/>
<hr />
<div id="translationOutputDiv"></div>

Trying to make a language translation app

I am trying to make a translation app that can translate a number between 1-30 from english to its german/french counterpart. I think I am somewhat on the right track, ive made the arrays with all the translations, but the problems I am having is I don't know how to correlate the number the user puts in via a prompt, to one of the values in the array, example:
User is prompted for number between 1-30, user is prompted for language French/German = Translation
This is what I am trying to do. Bellow is what I have so far, feel free to nit pick, but bear in mind I am new to Javascript so there is probably a lot wrong.
function translate() {
if (lang = "French") {
console.log(frenchTranslation);
} else {
console.log(germanTranslation);
}
};
var x=translate
translate(x)
var number=(Number(prompt ("What is your number? Must be between 1-30")));
var lang=(prompt ("What is your language? Must be 'French' or 'German'. Case Sensitive."));
var frenchTranslation = ["Please enter a number between 1-30", "un","deux","trois","quatre","cinq","six","sept","huit","neuf","dix","onze","douze","treize","quatorze","quinze","seize","dix-sept","dix-huit","dix-neuf",
"vingt","vingt et un","vingt-deux","vingt-trois","vingt-quatre","vingt-cinq","vingt-six","vingt-sept","vingt huit","vingt-neuf","trente"];
var germanTranslation = ["Please enter a number between 1-30","Eins","Zwei","Drei","Vier","Fünf","Sechs","Sieben","Acht","Neun","Zehn","Elf","Zwölf","Dreizehn","Vierzehn","Fünfzehn","Sechzehn","Siebzehn","Achtzehn","Neunzehn",
"Zwanzig","Einundzwanzig","Zweiundzwanzig","Dreiundzwanzig","Vierundzwanzig","Fünfundzwanzig","Sechsundzwanzig","Siebenundzwanzig","Achtundzwanzig","Neunundzwanzig","Dreiβig"];
Right, so first of all, you need to add some input validation to know what the user has selected. I recommend storing it somewhere, then you should make sure that it's in the correct range. Just use an if statement to check if the number is >= 0 && <= 30. After that when you're trying to use console.log you need to use array index of the correct number.
Here's my solution, you can improve on it a lot.
var frenchTranslation = ["Please enter a number between 1-30", "un","deux","trois","quatre","cinq","six","sept","huit","neuf","dix","onze","douze","treize","quatorze","quinze","seize","dix-sept","dix-huit","dix-neuf",
"vingt","vingt et un","vingt-deux","vingt-trois","vingt-quatre","vingt-cinq","vingt-six","vingt-sept","vingt huit","vingt-neuf","trente"];
var germanTranslation = ["Please enter a number between 1-30","Eins","Zwei","Drei","Vier","Fünf","Sechs","Sieben","Acht","Neun","Zehn","Elf","Zwölf","Dreizehn","Vierzehn","Fünfzehn","Sechzehn","Siebzehn","Achtzehn","Neunzehn",
"Zwanzig","Einundzwanzig","Zweiundzwanzig","Dreiundzwanzig","Vierundzwanzig","Fünfundzwanzig","Sechsundzwanzig","Siebenundzwanzig","Achtundzwanzig","Neunundzwanzig","Dreiβig"];
function translate()
{
const yournumber = Number(prompt("Enter your number (1-30)"));
console.log(yournumber);
const language = prompt("Choose a language - German or French");
if(yournumber < 1 || yournumber > 30) {
alert("Too hard");
}
else {
if(language === "French") {
console.log(frenchTranslation[yournumber]);
}
if(language === "German") {
console.log(germanTranslation[yournumber]);
}
}
}
translate();

Javascript - How to know how much string matched in another string?

I have been implementing a simple quiz for English. In that, we need to validate answers, which are entered by users in input field. In the current implementation, I am comparing the correct answer with user's answer exactly. Like,
HTML
<input type="text" id="answer" />
<button onclick="validate()">Validate</button>
Javascript
var question = "Do you like movies?",
answer = "No, I don't like movies.";
function validate() {
var userInput = document.getElementById('answer').value;
if(answer == userInput) {
console.log("correct");
} else {
console.log("wrong");
}
}
But I don't want validate exactly. Like, ignore case sensitive, commas, apostrophe, etc. For example if user enters,
i dont like movies
The answer can be correct. I don't know how start and where to start. Anyone please help.
One option would be to strip out all non-word characters and spaces, and compare the lower-case version of each replaced string:
var question = "Do you like movies?",
answer = "No, I don't like movies.";
const normalize = str => str
.replace(/[^\w ]/g, '')
.toLowerCase();
function validate(userInput) {
const noramlizedInput = normalize(userInput)
const noramlizedAnswer = normalize(answer);
if (noramlizedInput == noramlizedAnswer) {
console.log("correct");
} else {
console.log("wrong");
}
}
validate('No i dont like movies');
validate("NO!!!!! I DON''t like movies.");
Another option would be to loop through all possible substrings of the userInput and figure out which has the most overlap with the desired answer, but that's a whole lot more complicated.
An easier option would be to check to see how many overlapping words there are:
var question = "Do you like movies?",
answer = "No, I don't like movies.";
const normalize = str => str
.replace(/[^\w ]/g, '')
.toLowerCase()
.split(/\s+/)
function validate(userInput) {
const noramlizedInputArr = normalize(userInput);
const noramlizedAnswerArr = normalize(answer);
const overlapCount = noramlizedInputArr.reduce((a, word) => (
a + Number(noramlizedAnswerArr.includes(word))
), 0);
console.log(overlapCount);
if (overlapCount >= 4) {
console.log("correct");
} else {
console.log("wrong");
}
}
validate('No i dont like movies');
validate("NO!!!!! I DON''t like movies.");
validate("i dont like movies.");
validate("Yes I like movies.");
If you are interested in simply catching spelling errors and small variations, a standard metric is called edit distance or Levenshtein distance. This is a count of the minimum number of deletions, insertions, or substitutions you need to change one text into another. Strings like "No I don't like the movies" and "No I don't like the moveys" will have small edit distances.
Here's a quick and dirty recursive edit distance function that will give you an idea:
function validate(text, pattern) {
// some simple preprocessing
let p = pattern.toLowerCase().replace(/[^a-z]+/ig, '')
let t= text.toLowerCase().replace(/[^a-z]+/ig, '')
// memoize recursive algorithm
let matrix = Array.from({length: t.length + 1}, () => [])
function editDistance(text, pattern, i = 0, j = 0){
if(i == text.length && j == pattern.length) return 0
if(i == text.length) return pattern.length - j
if(j == pattern.length) return text.length - i
let choices = [
(matrix[i+1][j+1] || (matrix[i+1][j+1] = editDistance(text, pattern, i+1, j+1))) + (text[i].toLowerCase() === pattern[j].toLowerCase() ? 0 : 1),
(matrix[i+1][j] || (matrix[i+1][j] = editDistance(text, pattern, i+1, j))) + 1,
(matrix[i][j+1] || (matrix[i][j+1] = editDistance(text, pattern, i, j+1))) + 1
]
return Math.min(...choices)
}
return editDistance(t, p)
}
// similar strings have smaller edit distances
console.log(validate("No I dont lik moves","No i dont like movies"))
// a little less similar
console.log(validate("Yes I like movies","No i dont like movies"))
// totally different
console.log(validate("Where is the bathroom","No i dont like movies"))
// careful -- small edit distance !== close meaning
console.log(validate("I do like tacos","I don't like tacos"))
Picking a minimum acceptable distance works pretty well for matching strings with small typos. Of course, if you are trying to gauge user intent, none of these simple hues tics will work. Strings like "I love tacos" and "I loath tacos" have a small edit distance and you can't tell that they mean the opposite without knowledge of the language. If you need to do this level of checking you can try using a service like Watson Conversation that will return user intents to input.

requiring input before conditional statements in javascript

In this exercise I have a prompt where I want to require input. So if the user doesn't enter anything, it will ask again until a response is given. When a response is finally given it will go through the list of conditional statements. If a response is given on the first prompt it will simply go through that list of conditional statements. I can't seem to get this to work. What am I doing wrong here?
var question = prompt("What programming language are you currently learning?");
var questionUpper = question.toUpperCase();
var firstCap = question.charAt(0).toUpperCase() + question.slice(1).toLowerCase();
while (true) {
if (question === '') {
var test = prompt("Please enter a programming language.");
} else {
break;
}
if (questionUpper === 'HTML') {
alert(questionUpper + ' is a good language to start with.');
} else if (questionUpper === 'CSS') {
alert(questionUpper + ' helps HTML look good.');
} else if (firstCap === 'Javascript') {
alert(firstCap + ' is a fun language to learn after HTML and CSS.');
} else if (firstCap === 'Python') {
alert(firstCap + ' is a good language, too.');
} else {
alert("At least you're learning something.");
}
}
I believe the error is in the while cycle. You are assigning the answer to the variable "test", but the condition is checking the value of "question".
var question = prompt("What programming language are you currently learning?");
while (true) {
if (question === '') {
question = prompt("Please enter a programming language.");
} else {
break;
}
}
var questionUpper = question.toUpperCase();
var firstCap = question.charAt(0).toUpperCase() + question.slice(1).toLowerCase();
if (questionUpper === 'HTML') {
alert(questionUpper + ' is a good language to start with.');
} else if (questionUpper === 'CSS') {
alert(questionUpper + ' helps HTML look good.');
} else if (firstCap === 'Javascript') {
alert(firstCap + ' is a fun language to learn after HTML and CSS.');
} else if (firstCap === 'Python') {
alert(firstCap + ' is a good language, too.');
} else {
alert("At least you're learning something.");
}
I also moved the upper-case after the loop in order to make it effective.
This looks like it makes much more sense to solve using a language object representation (also making it much easier to understand and edit). For example:
var langs = {
html: 'HTML is a good language to start with.',
css: 'CSS helps HTML look good.',
javascript: 'Javascript is a fun language to learn after HTML and CSS',
python: 'Python is a good language, too.'
}
// ask initial question
var answer = prompt('What programming language are you currently learning?')
// ask follow up until answer is given
while(answer.length <= 0){
answer = prompt('Please enter a programming language.')
}
// deal with answer
var lower = answer.toLowerCase()
if(langs[lower]){
alert(langs[lower])
}else{
alert('At least you\'re learning something.')
}
This way you can modify your langs object to contain any properties you like for each language. Maybe they have text related to other questions, or other variables you would like to associate with each language. This makes it easier to add or remove languages, or separate the data from the view, allowing you to get language data easily from a database without changing any logic.
To achieve expected result, you can use switch case option
var question = prompt("What programming language are you currently learning?");
var questionUpper = question.toUpperCase();
var firstCap = question.charAt(0).toUpperCase() + question.slice(1).toLowerCase();
switch(questionUpper){
case 'HTML':
alert(questionUpper + ' is a good language to start with.');
break;
case 'CSS':
alert(questionUpper + ' is a good language to start with.');
break;
case 'JAVASCRIPT':
alert(questionUpper + ' is a good language to start with.');
break;
case 'PYTHON':
alert(questionUpper + ' is a good language to start with.');
break;
default:
alert("At least you're learning something.");
}
The sample in CodePen

"parsing" a text document to specific javascript format?

Edit - I think parsing it the wrong word to use, I'm actually looking to format that text file into the format I provided.
I'm working on a project and I've ran into a slight problem. I need help with mass editing a text file into a certain format. I don't really know how to do this. For example, here is the text file: http://frankerfacez.com/users.txt
That has the list of users and emotes (users without periods, emotes with). I would need that to be changed into the format of
//user
"emote":["cdn.frankerfacez.com/channel/ user / emote.png"],
For reference, this is what I need the format to be: https://dl.dropboxusercontent.com/u/23313911/CustomEmotes.js
I really don't know how easy this will be or if it is even possible, but any help would be greatly appreciated!
lines = txtFile.responseText.split("\n");
var user = null;
var emote = null;
var o = {};
for (i in lines) {
var line = lines[i];
if (line.charAt(0) != ".")
user = line;
else {
try{
emote = line.substring(1, line.legth);
var a = new Array();
eval("var cnd = new String('cdn.frankerfacez.com/channel/'+user+'/'+emote+'.png');");
a.push(cnd.toString());
eval("o."+emote+" = a;");
}catch(e){
console.error(emote + " - " + user);
console.error(e);
}
}
}
console.log(o);

Categories

Resources