Szandor Posted November 13, 2009 Share Posted November 13, 2009 Hi! I'm very new PHP and have a question about loops. I have written a program that takes a string, finds special tags in it, replaces those tags with random strings from a list and returns the result. Right now, if that list also contains tags, the tags are returned as they are without being replaced. I want to replace those with random strings as well, but without risking any infinite loops. Here's a more thorogh example: Let's say I have a string that is "I like {NICE} but not {BAD}." and three lists that look like this: [NICE] dessert lego {ANIMALS} cartoons [bAD] tummyache evil clowns smelly socks dogs [ANIMALS] cats cows ducks badgers The {} tags are replaced by a random entry from the corresponding list. Right now the {NICE} tag would return the actual string {ANIMALS} if that is choosen, but I want the program to recognize this as another tag, replace that with a random entry from the [ANIMALS] list and return that. Help, anyone? Quote Link to comment Share on other sites More sharing options...
The Little Guy Posted November 13, 2009 Share Posted November 13, 2009 Can you post an example of an input, and an expected outcome? Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 13, 2009 Author Share Posted November 13, 2009 Can you post an example of an input, and an expected outcome? That's it above, but I can post another one: Example input: .a {TAVERN} of the {DESC} {THING} [TAVERN] Tavern Inn Alehouse [DESC] Red Drunken Mute [THING] {ANIMAL} {PERSON} [ANIMAL] Dragon Mule Cat [PERSON] Wizard King Harlot Example output (wanted) Inn of the Mute Harlot Example output (actual) Inn of the Mute {PERSON} Quote Link to comment Share on other sites More sharing options...
The Little Guy Posted November 13, 2009 Share Posted November 13, 2009 Is there a way you store this list currently? external file, array, some other method? Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 13, 2009 Author Share Posted November 13, 2009 Is there a way you store this list currently? external file, array, some other method? The input and the list are both in the same external file. The file defines one or more patterns (the input) that are read into an array. Each time the program is run, a random pattern is chosen from that array and processed. Each tag in the pattern is replaced by a random entry from a list. The lists in the file are read into a multidimensional array from which the progam randomly chooses an entry from the correct list (defined by the tag). Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 13, 2009 Author Share Posted November 13, 2009 Here's the program in action: http://minaphpprojekt.szandor.com/rannames/ You can choose a template and how many names to generate. You can preview the entire template file at the bottom of the page. The template that best shows what I want to do is the default one, "template.name". The other templates do not have any tags in their lists yet. As you can see in the example, tags occasionally show up in the generated names. Quote Link to comment Share on other sites More sharing options...
Psycho Posted November 13, 2009 Share Posted November 13, 2009 OK, I'm going to lay out some assumptions here. First, I *assume* that ther may be more lists with "sub tags" as in your example of the list above with {ANIMALS} appearing in the [NICE] list. I also assume that if {ANIMALS} is the randomly selected item under [NICE] that you would want it replaced with a legitimate value under [ANIMALS] - i.e. not another subtag if there are any. I'll put something together and post momentarily. Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 13, 2009 Author Share Posted November 13, 2009 OK, I'm going to lay out some assumptions here. First, I *assume* that ther may be more lists with "sub tags" as in your example of the list above with {ANIMALS} appearing in the [NICE] list. I also assume that if {ANIMALS} is the randomly selected item under [NICE] that you would want it replaced with a legitimate value under [ANIMALS] - i.e. not another subtag if there are any. I'll put something together and post momentarily. Actually I would like to continue replacing subtags, otherwise I might as well have used intermediate rules. I know this might lead to infinite loops, but surely there must be a way to count the number of times a function has been run and prevent subtituting more than, say, fifty times and return an error (or nothing) if it exceeds that number. Quote Link to comment Share on other sites More sharing options...
Psycho Posted November 13, 2009 Share Posted November 13, 2009 Actually I would like to continue replacing subtags, otherwise I might as well have used intermediate rules. I know this might lead to infinite loops, but surely there must be a way to count the number of times a function has been run and prevent subtituting more than, say, fifty times and return an error (or nothing) if it exceeds that number. Then why didn't you STATE that you want to limit it to some arbitrary number? You need to be very precise in your needs to prevent us from wasting our time. Oh, another question that needs to be answered is what should happen if the same tag appears twice in the input. Should it have the same replacement or can it have different replacements. The solution will be different. Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 13, 2009 Author Share Posted November 13, 2009 I'm sorry, I thought I was clear enough on this. I'll try to be more precise. A tag that is featured twice should have different output. What I'm trying to do is a PHP version of a random name generator that is included in a piece of map software called AutoREALM. It uses complex rules to randomly generate compound strings for use as names. It seems like a good starter project for learning PHP. You can read more about their syntax here: http://autorealm.sourceforge.net/chm/html/auto7zcj.htm This is the PHP I've written so far: index.php <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <? include 'settings.php'; // Loads the settings. include_once 'lang' . DIRECTORY_SEPARATOR . $settings[lang] . '.php'; // Loads the language of the GUI. ?> <html xmlns="http://www.w3.org/1999/xhtml" lang="<? echo $settings[lang]; ?>" xml:lang="<? echo $settings['lang']; ?>"> <head> <title><? echo $lang['title']; ?></title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link href="rannames.css" rel="stylesheet" type="text/css" /> </head> <body> <h1><? echo $lang['title']; ?></h1> <p><? echo $lang['intro']; ?></p> <form method="post" action="<? $_SERVER['PHP_SELF'] ?>"> <p> <select name="template"> <? if (array_key_exists('_submit_check', $_POST)) { // If a template has been set by the user, $namefile = "templates/" . $_POST['template']; // that template is active. } else { // Otherwise, $namefile = "templates/" . $settings['def_temp']; // the default template is active. } // This will find all documents with the ".name" file ending in the folder "templates" and list them in an array. if ($handle = opendir('templates')) { // We open the folder. $counter = 0; // The initial position of the array to write to. while (false !== ($file = readdir($handle))) { // Loops as long as there are files to list in the folder. if (preg_match('/^.+\.name$/',$file)) { // Makes sure only ".name"-files are processed. $templatedir[$counter] = $file; // The actual writing of the array. $counter = $counter + 1; // The next position of the array to write to. } } } closedir($handle); // Then we close the directory. sort($templatedir); // Let's sort the list alphabetically. foreach ($templatedir as $key => $val) { // For every value in the array, if ($val != $_POST['template']) { // if the template is not the active one, echo "\t\t\t\t\t<option value=\"$val\">$val</option>\n"; // we add it to the list of options. } else { // If it´s the active template, echo "\t\t\t\t\t<option value=\"$val\" selected=\"selected \">$val</option>\n"; // we make it pre-selected. } } ?> </select> </p> <p><input style="width:3em;" name="quantity" maxlength="3" value="<? if ($_POST['quantity'] != "") { // If the user has set a quantity of names to list, echo $_POST['quantity']; // we write out that quantity. } else { // Otherwise, echo "10"; // we use a default. } ?>" /><input type="hidden" name="_submit_check" value="1" /></p> <p><button type="submit"><? echo $lang['button']; ?></button></p> </form> <h2><? echo $lang['metadata']; ?></h2> <? require 'engine.php'; // We'll need the actual randomizer engine now. if (isset($metainfo)) { echo "<dl>\n"; foreach($metainfo as $metatitle => $metatext) { echo "<dt>" . $metatitle . "</dt>\n"; echo "<dd>" . $metatext . "</dd>\n"; } echo "</dl>\n"; } else { echo "<p style=\"font-style:italic;\">No meta info.</p>\n"; } /*if (isset($metainfo)) { echo "<pre>"; print_r ($metainfo); echo "</pre>"; } else { echo "<p>No meta info.</p>\n"; }*/ ?> <h2><? echo $lang['list']; ?></h2> <ul id="namelist"> <? // If the user has set a quantity of names to list, we randomize a name that number of times. Otherwise, we do it ten times. if ($_POST['quantity'] != "") { $b = $_POST['quantity']; } else { $b = 10; } for($a = 0;$a != $b;$a++) { // This loops the number of times specified above. $thepatterntitle = array_rand($patterns); // We choose a random pattern, $thepattern = $patterns[$thepatterntitle]; // and write it to a variable. $name = preg_replace_callback('/\{[^\{\}\r\n]*\}/','randel', $thepattern); // This replaces the pattern with a random name, based on that pattern. echo "\t\t\t<li>" . $name . "</li>\n"; // Then we just print it on the page. } ?> </ul> <? // This lists all availible patterns in the template file. /* echo "<h2>" . $lang['patterns'] . "</h2>\n<ul>"; foreach($patterns as $patnum => $patpat) { echo "<li>" . $patpat . "</li>\n"; } echo "</ul>"; */ echo "<h2>Input file</h2>\n"; echo "<pre class=\"box\">\n"; $file = fopen($namefile, "r") or exit("Unable to open file!"); while(!feof($file)) { // $nextline = fgets($file); echo fgets($file); } echo "</pre>"; ?> </body> </html> engine.php <? // First we open the template file for reading. $file = fopen($namefile, "r") or exit("Unable to open file!"); // As long as we don't reach the end of the file, the following is run on each line. while(!feof($file)) { // We go to the next line (or the first line, if run for the first time). $nextline = fgets($file); // Two consecutive slashes and anything after is removed, as are empty lines and beginning and trailing whitespaces. $nextline = preg_replace("/\/\/.+|(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "", $nextline); $nextline = preg_replace("/^[ \t]+|[ \t]+$|\n$/", "", $nextline); // If, after this, the line is not empty... if ($nextline != "") { // If the line begins with "m:" it is metadata and placed in the $metainfo array. if (preg_match('/^\m\:/',$nextline)) { $metatitle = preg_replace('/^\m\:/','',$nextline); $metatitle = preg_replace('/(?= ).*/','',$metatitle); $nextline = preg_replace('/^[^\n|^ ]*(?= )/','',$nextline); $nextline = preg_replace('/^ /','',$nextline); $metainfo[$metatitle] = $nextline; // If the line begins with a dot it is a pattern and is placed in the $patterns array } elseif (preg_match('/^\./',$nextline)) { $pattitle = preg_replace('/^\./','',$nextline); $pattitle = preg_replace('/(?= ).*/','',$pattitle); $nextline = preg_replace('/^[^\n|^ ]*(?= )/','',$nextline); $nextline = preg_replace('/^ /','',$nextline); $patterns[$pattitle] = $nextline; // If the line begins with a square bracket it is a list header and is saved in a variable for use soon. } elseif (preg_match('/^\[.*/',$nextline)) { $nextline = preg_replace('/^\[/','',$nextline); $nextline = preg_replace('/\]$/','',$nextline); $sectline = $nextline; // If the line is neither of the above it's considered a part of a list and is placed in a multidimensional array under the previously saved header. } else { $elements[$sectline][] = $nextline; } } } // When it's all over, we close the file. We won't be needing it anymore. fclose($file); // This function does the substituting. function randel($anelement) { global $elements; // We need this to be global. $anelement = preg_replace("/\{|\}/","",$anelement); // We strip away the curly brackets. $anelement = implode($anelement); // It needs to be a string, not an array. $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; // Since array_rand() can't be used on multi-dimensional arrays, we count the number of values in the selected sub-array, ... $anelement2 = rand(0,$anelement2); // ... randomly choose a number ... $theelement = $elements[$anelement][$anelement2]; // ... and then call the sub array. return $theelement; } ?> Quote Link to comment Share on other sites More sharing options...
Psycho Posted November 13, 2009 Share Posted November 13, 2009 This should work for you (tested). Just change $maxItterations in the replaceTag() function to whatever value you think is appropriate. <?php function processInput($text) { return preg_replace_callback('/{([^}]*)}/', replaceTag, $text); } function replaceTag($match, $itterations=1, $maxReached=false) { $maxItterations = 3; global $tags; $match = (is_array($match)) ? $match[1] : $match; $newTag = $match; //Replace tag if( isset($tags[$match]) && count($tags[$match])>0 ) { $replacements = $tags[$match]; //Remove subtags from replacements if max itterations reached if($maxReached) { foreach($replacements as $key => $value) { if (preg_match('/^{.*}$/', $value)!==0) { unset($replacements[$key]); } } } //Set replacement tag $newTag = $replacements[array_rand($replacements)]; } //== Thi line for debugging/illustrative purposes only echo "Itteration: $itterations, Match: $match, New Tag: $newTag<br />\n"; //Replace again if subtag if (preg_match('/^{.*}$/', $newTag)!==0) { //Get new replacement $match = substr($newTag, 1, strlen($newTag)-2); $newTag = replaceTag($match, ++$itterations, ($itterations==$maxItterations)); } return $newTag; } //Read the tags file and process into an ordered array $tagList = file('tags.txt'); $tags = array(); $tagLabel = false; foreach($tagList as $tag) { $tag = trim($tag); if (preg_match('/^\[.*\]$/', $tag)) { $tagLabel = substr($tag, 1, strlen($tag)-2); } elseif ($tagLabel!==false && !empty($tag)) { $tags[$tagLabel][] = $tag; } } $input = "I like {NICE} but not {BAD}."; $output = processInput($input); echo "<br/><br />"; echo "Input: $input<br />\n"; echo "Output: $output"; ?> I used an exagerrated replacements file for testing purposes so I could ensure the code would stop allowing subtags after a certain itteration: tags.txt [NICE] dessert lego {ANIMALS} {ANIMALS} {ANIMALS} {ANIMALS} {ANIMALS} {ANIMALS} {ANIMALS} {ANIMALS} {ANIMALS} cartoons [bAD] tummyache evil clowns smelly socks dogs [ANIMALS] {NICE} {NICE} {NICE} {NICE} {NICE} {NICE} {NICE} {NICE} {NICE} cats cows ducks badgers Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 13, 2009 Author Share Posted November 13, 2009 Thanks a lot! I'll read through the code, try it out and make sure I understand it. I'll post back here once I get it working. Again, thanks for the support! Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 15, 2009 Author Share Posted November 15, 2009 I've been trying out the code provided for me above, but I can't get it to work on substrings with more than one tag. I thought it was obvious I needed that to work, but I guess I was unclear here as well. So, I'll try to be as clear as possible with what I want this time. This is a template file I want to use. The PHP program should be able to work with other template files, following the same syntax: .a A {DENSITY} {COLOR} {FORM}, smelling like {SMELL} and tasting like {TASTE}. .b A {DENSITY} {COLOR} {FORM}, smelling and tasting like {TASTE}. .c A tasteless, {DENSITY} {COLOR} {FORM}, smelling like {SMELL}. .d An odorless, {DENSITY} {COLOR} {FORM}, tasting like {TASTE}. .e A {DENSITY} {COLOR} {FORM} without odor or taste. [sMELL] {THING} {STATE} {THING} [TASTE] {THING} {STATE} {THING} {THING} and {THING} nothing [FORM] draught elixir fluid liquid mixture potion substance tonic [DENSITY] bright bubbling chunky clear dark fizzing glowing milky muddy oily slimy smoking sparkling swirling thick thin viscious watery weak [color] amber black blue brown cobolt copper ebony emerald gold green ivory jade obsidian ochre orange pink purple red rose ruby silver teal translucent transparent violet white yellow [sTATE] ancient boiled bottled cultivated faded fermented forbidden fresh hot old ripe salty smoky sour stored strange strong sweetened watered down well tended [THING] acid acorns alchohol ale apples blood blueberries boots brandy bread cabbage candy caramel carrots cherries chocolate cinnamon cloth coffee dust eggs fish garlic grapes grass herbs honey iron lamp oil leather lemon licorice lime liquor mead meat metal milk mint mud oil onions paper peaches peanuts pears peppar pepper pus rasberries rhubarbs roses rot rum salt spice strawberries sugar sweat tea tomatoes urine vanilla vomit walnuts water wax wine wool In the above template, tags are used that begin and end with curly brackets. Example: {DENSITY} These tags should be replaced with entries from the corresponding list. For instance, if the tag is {DENSITY} it should be replaced with one item from the list named DENSITY. A list begins with the name in square brackets and ends when a new name in square brackets is found. A single list item is begun on a new line and ended when the line ends. Currently, my code can replace any tags within the patterns (lines begun with a dot followed by the name of the pattern, a space and then the pattern itself) with items from the correct lists, but I also want the option of using tags in the lists. By this I mean that any tags encountered are replaced by a corresponding list item. Should that item also be a tag, that too is replaced. This continues until a non-tag is encountered or a specified number of sub-replacements have been reached. The code written above by "mjdamato" almost does this, BUT cannot replace multiple tags in a list item. Example: If the item is "wine", it returns "wine", which is the expected behaviour. If the item is "{THING}", it returns a random value from the list named "THING", which is the expected behaviour. If the item is "{THING} and {THING}" it returns "THING and THING", which is an unwanted behaviour. The expected behaviour is for the code to return a separate random list entry for each occurence of the tag. I can change the code to use "preg_replace_callback()" on the string and through that use regexp to target each tag and run it through the function I'm currently already running, but that way I risk ending up with an infinite loop if the template file is badly written since I can't increment the count of iterations if I use that method. All patterns (lines in the template beginning with a dot) are saved into an array named "$patterns" and all lists are saved into a multidimensional array called "$elements". The following code is used to initiate the generation: for($a = 0;$a != $b;$a++) { // This loops a specified number of times. $thepatterntitle = array_rand($patterns); // We choose a random pattern, $thepattern = $patterns[$thepatterntitle]; // and write it to a variable. $name = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randel,$thepattern); // This replaces the pattern with a random name, based on that pattern. echo "\t\t\t<li>" . $name . "</li>\n"; // Then we just print it on the page. } And this is the function I wrote to do the actual randomization: function randel($anelement) { global $elements; // We need this to be global. $anelement = preg_replace("/\{|\}/","",$anelement); // We strip away the curly brackets. $anelement = implode($anelement); // It needs to be a string, not an array. $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; // Since array_rand() can't be used on multi-dimensional arrays, we count the number of values in the selected sub-array, ... $anelement2 = rand(0,$anelement2); // ... randomly choose a number ... $theelement = $elements[$anelement][$anelement2]; // ... and then call the sub array. return $theelement; } If I want to replace tags in list items as well I can simply add the following lines just before the "return" in the above function (This runs the risk of producing infinite loops.): if (preg_match('/^.*{.*?}.*$/', $theelement)!==0) { $theelement = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randel,$theelement); } Now, how can I make sure that my code does not run the risk of producing infinite loops? Alternatively, how can I modify the code provided for me by "mjdamato" so that all tags in a list item are replaced and not just when the list item is a single tag and nothing else? PLEASE! If anything is unclear, just tell me so that I can clarify. Quote Link to comment Share on other sites More sharing options...
Zane Posted November 15, 2009 Share Posted November 15, 2009 I'd set randel to be recursive if the end result was.. a bracketed one. a {THING} or {PERSON} or {BRACKETBLOCK}. function randel($anelement) { global $elements; // We need this to be global. $anelement = preg_replace("/\{|\}/","",$anelement); // We strip away the curly brackets. $anelement = implode($anelement); // It needs to be a string, not an array. $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; // Since array_rand() can't be used on multi-dimensional arrays, we count the number of values in the selected sub-array, ... $anelement2 = rand(0,$anelement2); // ... randomly choose a number ... $theelement = $elements[$anelement][$anelement2]; // ... and then call the sub array. /* This is untested and completely freewritten, but you pretty much do a preg match to test for whatever format your looking for (or not looking for essentially) and set it equal to a recursive reload of the same function. if(preg_match("#\{*\}#", $theelement)) $theelement = randel($theelement); return $theelement; } Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 15, 2009 Author Share Posted November 15, 2009 I'd set randel to be recursive if the end result was.. a bracketed one. a {THING} or {PERSON} or {BRACKETBLOCK}. function randel($anelement) { global $elements; // We need this to be global. $anelement = preg_replace("/\{|\}/","",$anelement); // We strip away the curly brackets. $anelement = implode($anelement); // It needs to be a string, not an array. $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; // Since array_rand() can't be used on multi-dimensional arrays, we count the number of values in the selected sub-array, ... $anelement2 = rand(0,$anelement2); // ... randomly choose a number ... $theelement = $elements[$anelement][$anelement2]; // ... and then call the sub array. /* This is untested and completely freewritten, but you pretty much do a preg match to test for whatever format your looking for (or not looking for essentially) and set it equal to a recursive reload of the same function. if(preg_match("#\{*\}#", $theelement)) $theelement = randel($theelement); return $theelement; } That's what I did in the last part of my obviously still unclear post above, although I used preg_replace_callback() so that all tags in the returned string are processed separately. The problem here is the infinite loop thingie. What if list A points to list B and list B points to list A? Without a way of counting the number of iterations an infinite loop may form. Quote Link to comment Share on other sites More sharing options...
Psycho Posted November 15, 2009 Share Posted November 15, 2009 The code I provided DOES process every tag independantly and it DOES account for an infinite loop. If you run the code exactly as I posted it, it works fine. You did not provide an example of your template file so I had to improvise. The code would just need to be modified to work with your specific import file. Using the code I provided I ran the following input text with the same tag repeated several times and the duplicate tags are replaced indipendantly: Input: I like {NICE} but not {BAD}. I like {NICE} too. Output #1: I like badgers but not smelly socks. I like cartoons too. Output #2: I like dessert but not evil clowns. I like dessert too. Output #3: I like lego but not dogs. I like cartoons too. However, one thing that is not accounted for is when a single tag is replaced with multiple tags. That was not in the original details. That will be much more difficult to handle. I is easy enough to count the itterations on a single replacement, but if you replace a tag with two tags do the previous itterations count for both, do they start over, or what? It will be much more difficult to track. Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 16, 2009 Author Share Posted November 16, 2009 Hmm, I wonder if it would work to replace a tag, then search for tags in the replacement string, place any found tags in an array and then run the function on the array somehow. I'll try to see what I can do, otherwise I'll go look for help at some other place, if no one here can help. Thanks for the help so far anyway, even if the script isn't working fully yet. I've learned a few things along the way and that's what counts, right? Quote Link to comment Share on other sites More sharing options...
Zane Posted November 16, 2009 Share Posted November 16, 2009 Something tells me you are just storing all of these "tags" in text format. Maybe such a discovery has already been exposed, nevertheless, this is not the way to go. Store dimensional data in something that can instantly be read as "dimensional"... like an array or XML.. or even (worst case scenario) a database. Looping through text that could very well be formatted text is completely redundant and a waste of time for you and your server's CPU. It's like having a database and not running searches on it. Just picking the desired rows out manually for every page. --Looking above I noticed the tags.txt file--- This is the file I'm talking about. You could easily make this a PHP file that does nothing but create an array of all of these tags. tags.php $tags = array(); $tags['nice'] = "dessert"; $tags['nice'] = "lego"; $tags['thing'] = "{ANIMAL}"; $tags['thing'] = "{PERSON}"; $tags['animal'] = "dog"; $tags['animal'] = "cat"; $tags['person'] = "wizard"; ?> Then you can just include or require this page on your engine page. require('tags.php'); echo "",print_r($tags),""; //show the tags ?> At this point (I'm too lazy to right all the code for it). You would use preg_replace_callback like you were doing to find all of the "tags".. (or elements in {BRACES}..) You can then take each match.. one by one, (using foreach) and send it through another function ... randal. where you'd use strtolower to get the text where you find the corresponding array key. .. e.g $tag[$match]. ($match might be "animal") or it may be "thing" in which case you'd have to run randal again on that particular match. Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 16, 2009 Author Share Posted November 16, 2009 Something tells me you are just storing all of these "tags" in text format. Maybe such a discovery has already been exposed, nevertheless, this is not the way to go. Store dimensional data in something that can instantly be read as "dimensional"... like an array or XML.. or even (worst case scenario) a database. Looping through text that could very well be formatted text is completely redundant and a waste of time for you and your server's CPU. It's like having a database and not running searches on it. Just picking the desired rows out manually for every page. --Looking above I noticed the tags.txt file--- This is the file I'm talking about. You could easily make this a PHP file that does nothing but create an array of all of these tags. tags.php <?php $tags = array(); $tags['nice'] = "dessert"; $tags['nice'] = "lego"; $tags['thing'] = "{ANIMAL}"; $tags['thing'] = "{PERSON}"; $tags['animal'] = "dog"; $tags['animal'] = "cat"; $tags['person'] = "wizard"; ?> Then you can just include or require this page on your engine page. <?php require('tags.php'); echo "<pre>",print_r($tags),"</pre>"; //show the tags ?> At this point (I'm too lazy to right all the code for it). You would use preg_replace_callback like you were doing to find all of the "tags".. (or elements in {BRACES}..) You can then take each match.. one by one, (using foreach) and send it through another function ... randal. where you'd use strtolower to get the text where you find the corresponding array key. .. e.g $tag[$match]. ($match might be "animal") or it may be "thing" in which case you'd have to run randal again on that particular match. But this will still not eliminate infinite loops, will it? Also, I do read in everything from a text file, but everything is read into arrays and then the file is closed. I need a syntax of my own to make it simple for anyone to write new templates for the randomizer. Here is a sample template file that randomizes potions: m:Description This template generates potions. Only the physical attributes of the potion are given, but in time it will define the effects of the potion as well. .a A {DENSITY} {COLOR} {FORM}, smelling like {SMELL} and tasting like {TASTE}. .b A {DENSITY} {COLOR} {FORM}, smelling and tasting like {TASTE}. .c A tasteless, {DENSITY} {COLOR} {FORM}, smelling like {SMELL}. .d An odorless, {DENSITY} {COLOR} {FORM}, tasting like {TASTE}. .e A {DENSITY} {COLOR} {FORM} without odor or taste. [sMELL] {THING} {STATE} {THING} [TASTE] {THING} {STATE} {THING} {THING} and {THING} [FORM] draught elixir fluid liquid mixture potion substance tonic [DENSITY] bright bubbling chunky clear dark fizzing glowing milky muddy oily slimy smoking sparkling swirling thick thin viscious watery weak [color] amber black blue brown cobolt copper ebony emerald gold green ivory jade obsidian ochre orange pink purple red rose ruby silver teal translucent transparent violet white yellow [sTATE] ancient boiled bottled cultivated faded fermented fresh hot old ripe salty smoky sour stored strange strong sweetened watered down well tended [THING] acid acorns alchohol ale apples blood blueberries boots brandy bread cabbage candy caramel carrots cherries chocolate cinnamon cloth coffee dust eggs fish garlic grapes grass herbs honey iron lamp oil leather lemon licorice lime liquor mead meat metal milk mint mud oil onions paper peaches peanuts pears peppar pepper pus rasberries rhubarbs roses rot rum salt spice strawberries sugar sweat tea tomatoes urine vanilla vomit walnuts water wax wine wool As you can see, the syntax is very simple and it is not required to know any PHP to write it. Making this a PHP file from the beginning will take away the simplicity of the syntax, and that is user un-friendly. Quote Link to comment Share on other sites More sharing options...
Psycho Posted November 16, 2009 Share Posted November 16, 2009 I started to modify my original script last night to work with your example input file, but the issue I raised at the end of my last post makes this problematic. Specifically, your data shows that you may replace a single tag with two tags. So, imagine the follwing situation: 1. You are replacing a tag and it is continually replaced with other tags (which is the reason for this post, right?). You have it set to only allow up to 50 replacements, but on iterration #49 the tag is then replaced with TWO tags. What should happen at that point? Does the first new tag count as the 50th iterration and the second new tag starts over again? If so, then there is still a potential for an infinite loop. To add to that, let's say the iterrations reach 50 and you want to ensure a new replacement is not a new tag. What if the last iteration is a tag that only contains sub-tags. What should happen in that situation? I was actually pretty close to completing the code to work against your example data file, but didn't want to spend time coding for a solution that was not intended. It would have been extremely helpful if you had provided the full example data file in your first post instead of just a modified sample. If you want to answer the questions above I may be able to put something together tonight. I don't have the code with me at the moment. Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 16, 2009 Author Share Posted November 16, 2009 Specifically, your data shows that you may replace a single tag with two tags. To clarify: A single tag may be replaced with any number of tags and/or any combination of tags and normal text. 1. You are replacing a tag and it is continually replaced with other tags (which is the reason for this post, right?). You have it set to only allow up to 50 replacements, but on iterration #49 the tag is then replaced with TWO tags. What should happen at that point? Does the first new tag count as the 50th iterration and the second new tag starts over again? If so, then there is still a potential for an infinite loop. To add to that, let's say the iterrations reach 50 and you want to ensure a new replacement is not a new tag. What if the last iteration is a tag that only contains sub-tags. What should happen in that situation? I'd say that in both these situations, the script should return an error. When I tested your unmodified code, I seldom got more iterations of the code than 10 and that had many references back and forward. Sure, a more complex template might raise this number, but the server should have no problem handling a large number of iterations, as long as they are not infinite. It would have been extremely helpful if you had provided the full example data file in your first post instead of just a modified sample. I'm sorry, I'm new to this and didn't know exactly how much was needed. I'll be more precise from now on. Quote Link to comment Share on other sites More sharing options...
Psycho Posted November 16, 2009 Share Posted November 16, 2009 I'd say that in both these situations, the script should return an error. When I tested your unmodified code, I seldom got more iterations of the code than 10 and that had many references back and forward. Sure, a more complex template might raise this number, but the server should have no problem handling a large number of iterations, as long as they are not infinite. OK, that really doesn't answer my question. I guess I shouldn't have defined such an edge condition. For the 2nd scenario (where there are no replacements that can be made when the max iterration is reached) an error can work. But, how the first scenario should be handled is still unclear. Let's say that on the 30th iterration a tag is replaced with two tags. Do both tags then start looking for a replacement starting at 31? As I stated previously, once you start splitting the possibilities it becomes much harder to control the count of iterrations. But, I'll give it a shot. Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 17, 2009 Author Share Posted November 17, 2009 I'd say that in both these situations, the script should return an error. When I tested your unmodified code, I seldom got more iterations of the code than 10 and that had many references back and forward. Sure, a more complex template might raise this number, but the server should have no problem handling a large number of iterations, as long as they are not infinite. OK, that really doesn't answer my question. I guess I shouldn't have defined such an edge condition. For the 2nd scenario (where there are no replacements that can be made when the max iterration is reached) an error can work. But, how the first scenario should be handled is still unclear. Let's say that on the 30th iterration a tag is replaced with two tags. Do both tags then start looking for a replacement starting at 31? As I stated previously, once you start splitting the possibilities it becomes much harder to control the count of iterrations. But, I'll give it a shot. Yes, they should start at 31. Otherwise we risk the infinite loops again. Envision it as a branch. Each new branch continues from where it sprung, but cannot reach further than the max. So, if a new branch is made at iteration 30 and the max is 50, that branch can only iterate 20 more times. I've now actually written code that does what I want, but I can't really use it and you'll know why when you see it. This is how it looks: <? // First we open the template file for reading. $file = fopen($namefile, "r") or exit("Unable to open file!"); // As long as we don't reach the end of the file, the following is run on each line. while(!feof($file)) { // We go to the next line (or the first line, if run for the first time). $nextline = fgets($file); // Two consecutive slashes and anything after is removed, as are empty lines and beginning and trailing whitespaces. $nextline = preg_replace("/\/\/.+|(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "", $nextline); $nextline = preg_replace("/^[ \t]+|[ \t]+$|\n$/", "", $nextline); // If, after this, the line is not empty... if ($nextline != "") { // If the line begins with "m:" it is metadata and placed in the $metainfo array. if (preg_match('/^\m\:/',$nextline)) { $metatitle = preg_replace('/^\m\:/','',$nextline); $nextline = $metatitle; $metatitle = preg_replace('/(?=\s).*/','',$metatitle); $nextline = preg_replace('/^\S*/','',$nextline); $nextline = preg_replace('/^\s*/','',$nextline); $metainfo[$metatitle] = $nextline; // If the line begins with a dot it is a pattern and is placed in the $patterns array } elseif (preg_match('/^\./',$nextline)) { $pattitle = preg_replace('/^\./','',$nextline); $pattitle = preg_replace('/(?= ).*/','',$pattitle); $nextline = preg_replace('/^[^\n|^ ]*(?= )/','',$nextline); $nextline = preg_replace('/^ /','',$nextline); $patterns[$pattitle] = $nextline; // If the line begins with a square bracket it is a list header and is saved in a variable for use soon. } elseif (preg_match('/^\[.*/',$nextline)) { $nextline = preg_replace('/^\[/','',$nextline); $nextline = preg_replace('/\]$/','',$nextline); $sectline = $nextline; // If the line is neither of the above it's considered a part of a list and is placed in a multidimensional array under the previously saved header. } else { $elements[$sectline][] = $nextline; } } } $tags = $elements; // When it's all over, we close the file. We won't be needing it anymore. fclose($file); function randel($anelement) { global $elements; $anelement = preg_replace("/\{|\}/","",$anelement); $anelement = implode($anelement); $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; $anelement2 = rand(0,$anelement2); $theelement = $elements[$anelement][$anelement2]; if (preg_match('/^.*{.*?}.*$/', $theelement)!==0) { $theelement = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randel2,$theelement); } return $theelement; } function randel2($anelement) { global $elements; $anelement = preg_replace("/\{|\}/","",$anelement); $anelement = implode($anelement); $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; $anelement2 = rand(0,$anelement2); $theelement = $elements[$anelement][$anelement2]; if (preg_match('/^.*{.*?}.*$/', $theelement)!==0) { $theelement = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randel3,$theelement); } return $theelement; } function randel3($anelement) { global $elements; $anelement = preg_replace("/\{|\}/","",$anelement); $anelement = implode($anelement); $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; $anelement2 = rand(0,$anelement2); $theelement = $elements[$anelement][$anelement2]; if (preg_match('/^.*{.*?}.*$/', $theelement)!==0) { $theelement = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randel4,$theelement); } return $theelement; } function randel4($anelement) { global $elements; $anelement = preg_replace("/\{|\}/","",$anelement); $anelement = implode($anelement); $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; $anelement2 = rand(0,$anelement2); $theelement = $elements[$anelement][$anelement2]; if (preg_match('/^.*{.*?}.*$/', $theelement)!==0) { $theelement = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randel5,$theelement); } return $theelement; } function randel5($anelement) { global $elements; $anelement = preg_replace("/\{|\}/","",$anelement); $anelement = implode($anelement); $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; $anelement2 = rand(0,$anelement2); $theelement = $elements[$anelement][$anelement2]; if (preg_match('/^.*{.*?}.*$/', $theelement)!==0) { $theelement = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randel6,$theelement); } return $theelement; } function randel6($anelement) { global $elements; $anelement = preg_replace("/\{|\}/","",$anelement); $anelement = implode($anelement); $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; $anelement2 = rand(0,$anelement2); $theelement = $elements[$anelement][$anelement2]; if (preg_match('/^.*{.*?}.*$/', $theelement)!==0) { $theelement = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randel7,$theelement); } return $theelement; } function randel7($anelement) { global $elements; $anelement = preg_replace("/\{|\}/","",$anelement); $anelement = implode($anelement); $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; $anelement2 = rand(0,$anelement2); $theelement = $elements[$anelement][$anelement2]; if (preg_match('/^.*{.*?}.*$/', $theelement)!==0) { $theelement = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randel8,$theelement); } return $theelement; } function randel8($anelement) { global $elements; $anelement = preg_replace("/\{|\}/","",$anelement); $anelement = implode($anelement); $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; $anelement2 = rand(0,$anelement2); $theelement = $elements[$anelement][$anelement2]; if (preg_match('/^.*{.*?}.*$/', $theelement)!==0) { $theelement = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randel9,$theelement); } return $theelement; } function randel9($anelement) { global $elements; $anelement = preg_replace("/\{|\}/","",$anelement); $anelement = implode($anelement); $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; $anelement2 = rand(0,$anelement2); $theelement = $elements[$anelement][$anelement2]; if (preg_match('/^.*{.*?}.*$/', $theelement)!==0) { $theelement = preg_replace_callback('/\{[^\{\}\r\n]*\}/',randellast,$theelement); } return $theelement; } function randellast($anelement) { global $elements; $anelement = preg_replace("/\{|\}/","",$anelement); $anelement = implode($anelement); $anelement2 = count($elements[$anelement], COUNT_RECURSIVE) - 1; $anelement2 = rand(0,$anelement2); $theelement = $elements[$anelement][$anelement2]; if (preg_match('/^.*{.*?}.*$/', $theelement)!==0) { $theelement = "[overflow error]"; } return $theelement; } ?> And this code has a limit of only ten iterations. If I want to raise that number to, say, 100 I'll have to duplicate the function 90 more times and edit all the function calls so that no loops are created - all by hand. What if I want to lower the number of iterations? What if I want to test different limits? That would mean a whole lot of editing, just to try out something. But apart from that it works exactly like I want it. It will replace any and all tags until it finds a non-tag match or has run a maximum of ten times. Quote Link to comment Share on other sites More sharing options...
Szandor Posted November 17, 2009 Author Share Posted November 17, 2009 Ok, by editing the script by mjdamato I've managed to solve this. Instead of passing along the iterations and maxiterations variables with the function I use global variables to do the same thing. It isn't perfect, but I guess it's close enough. If you want to see the code, just say so and I'll post it here. Thanks for all the help, I wouldn't have succeded without it. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.