Skip to content

Instantly share code, notes, and snippets.

@Kraballa
Created January 29, 2020 18:10
Show Gist options
  • Save Kraballa/31babe3e96f2ac704cb4f6368244c4e8 to your computer and use it in GitHub Desktop.
Save Kraballa/31babe3e96f2ac704cb4f6368244c4e8 to your computer and use it in GitHub Desktop.
Procedual text generator. See comment for example. Inspired by tracery (http://www.crystalcodepalace.com/traceryTut.html)
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace TextGeneration
{
public static class TextGenerator
{
private static Random rand = new Random();
// Regex for matching any curly braces syntax
private static Regex Brackets = new Regex(@"{[a-zA-Z0-9:]+}");
// Regex for detecting the {xyz:abx} syntax.
private static Regex Naming = new Regex(@"[a-zA-Z0-9]+:[a-zA-Z0-9]+");
// Main functionality. a dictionary is expected as an input. see example for explanation.
public static string Process(Dictionary<string, string[]> kernel)
{
//the key "start" is the entry point. if it doesn't exist, we can't proceed.
if (!kernel.ContainsKey("start"))
throw new KeyNotFoundException("kernel does not contain the entry point key 'start'");
Dictionary<string, string> nameMap = new Dictionary<string, string>();
string ret = Pick(kernel["start"]);
while (Brackets.IsMatch(ret))
{
Match m = Brackets.Matches(ret)[0];
string key = m.Value.Substring(1, m.Value.Length - 2);
if (Naming.IsMatch(key))
{
// key follows the memory syntax. if it was already selected, paste it. else, select one and put it in the dictionary
string[] split = Naming.Match(key).Value.Split(':');
string mappedName = split[0];
if (!nameMap.ContainsKey(mappedName))
{
nameMap.Add(mappedName, Pick(kernel[split[1]]));
}
ret = Brackets.Replace(ret, nameMap[mappedName], 1);
}
else if (kernel.ContainsKey(key))
{
// map the key onto a random element of its value array
ret = Brackets.Replace(ret, Pick(kernel[key]), 1);
}
else
{
throw new Exception("error, key '" + key + "' not in kernel");
}
}
return ret;
}
// helper method to pick a random element out of an array
private static string Pick(string[] array)
{
return array[rand.Next(array.Length)];
}
}
}
@Kraballa
Copy link
Author

Kraballa commented Jan 29, 2020

Example usage:

Dictionary<string, string[]> test = new Dictionary<string, string[]>();
test.Add("start", new string[] { "{FirstLine} {SecondLine}" });
test.Add("FirstLine", new string[] { "My name is {x:Name}." });
test.Add("SecondLine", new string[] { "{y:Name} {x:Name}." });
test.Add("Name", new string[] { "James","Bond","Peter","Hans","Michael" });
Console.WriteLine(KernelProcessor.Process(test));

So first we add the "start" key with a single value of "{FirstLine} {SecondLine}". Meaning when we evaluate it we'll have exactly that after one iteration. Now we match "{FirstLine}" and recognise that "FirstLine" is a key in our dictionary. So that will be resolved. Then we match "{x:Name}". "x" is the name I chose as a temporary variable. Since it hasn't been initialized yet we generate a new "Name". The next time we see "{x:Name}" that same name will be used. The remaining replacements follow the same rule.

One possible result would be "My name is Michael. Peter Michael.". the more options there are within the arrays, the more variety you're going to get. Now it's just up to your creativity. I've used this in some smaller projects and it's always great fun.

There probably are some issues when faced with faulty dictionaries, will fix those as I get to them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment