Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - papste

#1
I have disabled the control btnWalk "btnWalk.Enabled = false;" which is connected to the SetCursorMode (mode 0). However the button gets enabled automatically every time the character changes room. Is there a way to avoid this? I want the button to stay permanently disabled until i decide to re-enable it.

Having to program the command  "if (condition == true) btnWalk.Enabled = false" at room load (Before fade in) would not be a desired solution, meaning i have to add it to the script for every single room individually.

(edit): I am using version AGS 3.1.2 SP1
#2
The reason why i chose the bubble sort wasn't the simplicity of the code but mainly because its requires the least memory of all the sorting algorithms and also it preserves the relative order of records with equal keys.
But mainly it was a memory vs time issue. I will try more memory intensive algorithms tho, just to see how the game handles...
#3
Long explanation of the problem at hand included so please bare with me until the end:)

Due to the text parser limitation of allowing only 1500 entries i have created my own module including a text parser that reads vocabulary entries from a text file and fills an array. The reading is done at game start at a special room that i call "loading screen". The whole process used to take not more than 1 second to read around 2500 entries from the file and fill my array which worked fine for me. I would like to keep this module even if the parser limitation was raised, because its much easier for me to edit the text file and insert/remove words from it, rather than have to enter them in the parser one by one.

However during the game when the user inputs some text, my module splits the sentence into words and compares each word individually with the 2500 entries in the array deciding whether the word exists in the vocabulary whether it is a noun, adjective, verb, etc, and whether it is an ignored word or not. This process slows down the game as the user has to wait 2-5 seconds (depending on the machine's speed) after the text is entered for the calculations to take place. Also the background music which is playing tends to "tatter" during the calculation time.

So i came up with an idea to sort the array alphabetically in order to make it easier to find words in the array and not have to go all over the 2500 vocabulary entries multiple times for every word that the user enters. I have thus created a function that uses the bubble sort method and i have assigned the function to run at the loading screen, just after the array is read from the text file.

My problem is that the sorting procedure takes around 2-3 minutes and the user is forced to wait at the loading screen, which i find too much for a text adventure and is very annoying. The computer that i use is a fast one and i suspect the problem will be far too obvious on slower machines.

Following is the code that i used for the sorting. I had to use the noloopcheck command in order to achieve this. If anyone can help achieve faster sorting times i would be grateful for the help. The custom parser module that i created has solved a lot of problems for me. I would hate to have to throw it away just because the sorting takes too much time. My other option was to pre-sort the vocabulary file in Excel, but i would like to avoid this.

Code: ags

textParser.ash
------------------

#define MAX_WORDS_IN_VOCAB 5000

struct vocab {
  int groupId;
  String word;
  String attributes;
};

import vocab vocabEntry[MAX_WORDS_IN_VOCAB];

textParser.asc
-------------------

vocab vocabEntry[MAX_WORDS_IN_VOCAB];
export vocabEntry;

static function noloopcheck text_parser::sort_vocab() {
  bool swapped = true;
  vocab tempEntry;
  int loopCount = 0;
  while (swapped) {
    int i;
    swapped = false;
    while ((i <= MAX_WORDS_IN_VOCAB - 2) && (!String.IsNullOrEmpty(vocabEntry[i + 1].word))) {
      if (vocabEntry[i].word.CompareTo(vocabEntry[i + 1].word) > 0) {
        tempEntry.groupId = vocabEntry[i].groupId;
        tempEntry.word = vocabEntry[i].word;
        tempEntry.attributes = vocabEntry[i].attributes;
        vocabEntry[i].groupId = vocabEntry[i + 1].groupId;
        vocabEntry[i].word = vocabEntry[i + 1].word;
        vocabEntry[i].attributes = vocabEntry[i + 1].attributes;
        vocabEntry[i + 1].groupId = tempEntry.groupId;
        vocabEntry[i + 1].word = tempEntry.word;
        vocabEntry[i + 1].attributes = tempEntry.attributes;
        swapped = true;
      }
      i++;
    }
  }
// End of function.
}


I also figured i should include a part describing the text file structure in order to make it easier to understand how the array is set. So this is a small sample of the file containing only a few lines. The line is read and split into 3 parts (groupId, word, and attributes) and is passed into the vocab struct array.

vocab.txt
------------

1 the article
2 a article
3 an article
4 el article
5 la article
6 los article
50 and conjuction
51 or conjuction
52 nor conjuction
54 if conjuction
55 but conjuction
100 it pronoun
101 this pronoun
102 these pronoun
103 that pronoun
104 those pronoun
105 them pronoun
...
#4
Ok i temporarily found a solution to my problem. The correct code is the following:

Code: ags

String synonym[15];
export synonym;

String replace_text(String userInput) {
  int i = 1;
  while ((i <= 15) && (!String.IsNullOrEmpty(synonym[i]))) {
    int result = userInput.IndexOf(synonym[i]);
    while ((result != -1) && ((String.Format("%c", userInput.Chars[result - 1]) == " ") || (result == 0)) &&
    ((String.Format("%c", userInput.Chars[result + synonym[i].Length]) == " ") || (result + synonym[i].Length == userInput.Length))) {
      String tempInput1 = userInput.Substring(0, result + synonym[i].Length);
      String tempInput2 = userInput.Substring(result + synonym[i].Length, userInput.Length - 1);
      tempInput1 = tempInput1.Replace(synonym[i], synonym[0]);
      userInput = tempInput1.Append(tempInput2);
      result = userInput.IndexOf(synonym[i]);
    }
    synonym[i] = null;
    i++;
  }
  return userInput;
}

String check_for_synonym(String userInput) {
  if (String.IsNullOrEmpty(userInput)) return null;

  synonym[0] = "dog";
  synonym[1] = "hound";
  synonym[2] = "mongrel";
  synonym[3] = "mutt";
  synonym[4] = "man's best friend";
  synonym[5] = "pooch";
  synonym[6] = "puppy";
  synonym[7] = "deadly piranha poodle";
  userInput = replace_text(userInput);

  synonym[0] = "abdulla";
  synonym[1] = "abdullah";
  synonym[2] = "doo";
  userInput = replace_text(userInput); 

  return userInput;
}

// ...after getting the user's input from the textbox...
  String userInput = txtParserInput.Text;
  userInput = check_for_synonym(userInput);
  Parser.ParseText(userInput);
  String badWord = Parser.SaidUnknownWord();
  if (badWord != null) { // ran out of viable replacement words...
    Display("You will not need to use the word \"%s\" in this game.", badWord);
  }
#5
A game that relies primarily on the text parser can grow quite big as far as the number of vocabulary words used is concerned. Just to give an idea, if you open the Quest for glory 2 (made more than a decade ago by sierra) with the resource viewer you can see that the vocabulary goes up to 4095 words not including synonyms. Now i suppose you can live without the use of adjectives like "BLACK" bird, or "BRONZE' lamp, etc where you can instead type "examine lamp" and remove the adjective completely. However it would make the game alot more real and immersive if AGS didn't have the 1500 word limitation. If it's not too much trouble for you Chris i would be grateful if you increased the limit to 5000 (or more if that is possible) in a future version.
#6
I came up with the following code seems to work well so far. It works for multi-word synonyms too. I figured i should post it in case you guys could come up with a modification to handle the array better:

Code: ags

String synonym[10];
export synonym;

String replace_text(String userInput) {
  int i = 1;
  while ((i <= 10) && (!String.IsNullOrEmpty(synonym[i]))) {
    int result = userInput.IndexOf(synonym[i]);
    if (result != -1) {
      userInput = userInput.Replace(synonym[i], synonym[0]);
    }
    synonym[i] = null;
    i++;
  }
  return userInput;
}

String check_for_synonym(String userInput) {
  if (String.IsNullOrEmpty(userInput)) return null;

  synonym[0] = "dog";
  synonym[1] = "hound";
  synonym[2] = "mongrel";
  synonym[3] = "mutt";
  synonym[4] = "man's best friend";
  synonym[5] = "pooch";
  synonym[6] = "puppy";
  synonym[7] = "deadly piranha poodle";
  userInput = replace_text(userInput);

  synonym[0] = "abdulla";
  synonym[1] = "abdullah";
  synonym[2] = "doo";
  userInput = replace_text(userInput); 

  return userInput;
}

// ...after getting the user's input from the textbox...
  String userInput = txtParserInput.Text;
  userInput = check_for_synonym(userInput);
  Parser.ParseText(userInput);
  String badWord = Parser.SaidUnknownWord();
  if (badWord != null) { // ran out of viable replacement words...
    Display("You will not need to use the word \"%s\" in this game.", badWord);
  }


edit: the above code replaces every instance of the synonym found in the user input. In the case of the user input being "open the door", it finds "doo" and replaces with "abdulla" resulting to the text "open abdullar", which isn't what we seek. Thus i somehow have to add an additional check to see if the character following the synonym is a space (meaning its the end of the word) and replace the text only if it satisfies the check. I might need a little help with that since the sentence might end with that word and thus not contain any spaces.
#7
If it turns out too slow then i guess i can always have the values loading from the file into an array at game start, so i don't have to access the file every time the parser input is used. The problem with that is i need to find a way to distinguish the different word groups within the array. I was thinking it could work if i programmed AGS to add a code ending with a special character at the start of the each word (eg 1.1@dog 1.2@hound 2.1@greet 2.1@hello etc.) while they are being read from the file at game start, and then remove the code just before i replace the words at userInput. As for the type of file i don't really mind about the format to be used as long as its easy to edit the words from outside the AGS editor. A simple text file could work without problems (if i find a way to distinguish the different word groups).

Anyway the original code that you provided has already helped overcome the parser limitation by removing the 1-word synonyms from the text parser, which i think will be enough to make the game work without sacrificing anything, since my original problem was that the parser had too many words.
#8
Thank you both for the swift responce  :)
monkey_05_06 your idea works like a charm. I'm currently looking for a way to use your script but having AGS read the synonyms from a file instead of coding it directly into the script.  I guess i can achieve that by having AGS read groups of words from a file and then returning the first word in  the group.

edit: your script seems to work just fine with single words. It doesn't seem to work in the case of dog = deadly piranha poodle, which is a 3-word substitute. I guess its because String badWord = Parser.SaidUnknownWord() returns only 1 word, and in the case of the word being "deadly piranha poodle" your script will try to replace each word individually returning "dog dog dog". I suppose that's what the problem is but then again I'm not an expert with AGS programming yet.
#9
After having carefully checked the manual and forums, i couldn't find anywhere the maximum number of text parser words. However the specific game that i was trying to develop gives me the error message that i have used too many words. It would help to know the correct number so that i can manage my game better and remove the unnecessary words. Also is there any chance of increasing the limit or any other way to overcome this obstacle? 
SMF spam blocked by CleanTalk