Jump to content

[SOLVED] To infinity: And beyond! (A question regarding loops)


Szandor

Recommended Posts

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?

Link to comment
Share on other sites

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}

Link to comment
Share on other sites

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).

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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;
}

?>

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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;
}

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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?

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.