Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 92 additions & 5 deletions DiscordBot/Data/FuzzTable.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,114 @@
// FuzzTable.cs
//
using System;
using System.IO;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace DiscordBot.Data;

// A simple random string picking engine.
//
// Load it up like a list with string choices that can be picked from.
// Remember recently-picked choices so they don't repeat too soon.
// Individual string choices can also be evaluated with a simple syntax
// to further allow for alternative wordings, such as humanized messages.
//
// Evaluating "(He|She|They) (picked|chose) a (green|red|blue) (ball|block)."
// might return "She picked a red ball."
//
public class FuzzTable
{
private static Random random = new();
private static Regex parenContents = null;
private static TimeSpan timeout = new(10*10000/*x10nanoseconds*/);

//TODO: an instance keeps an array of alternates and an MRU list
private List<string> choices = new();
private Queue<string> recent = new();

public void Clear()
{
choices.Clear();
recent.Clear();
}

public int Count => choices.Count + recent.Count;

// Add a string as a valid choice from which to pick.
// Note that empty strings or whitespace can be added manually as valid choices.
// Duplicate choices are also allowed for weighting.
//
public void Add(string choice)
{
choices.Add(choice);
}

// Add a collection of choice strings all at once.
//
public void Add(IEnumerable<string> stream)
{
if (stream == null)
return;
foreach (var choice in stream)
Add(choice);
}

// Load a file of string choices.
// Lines starting with a '#' character are ignored, as are blank lines.
// Each remaining line of the file is trimmed of leading and trailing whitespace.
// Each line is added as a new choice, and duplicates are allowed for weighting.
//
public void Load(string filename)
{
foreach (string line in File.ReadLines(filename))
{
string choice = line.Trim();
if (choice.StartsWith('#'))
continue;
Add(choice);
}
}

// Pick one of the active choices.
// This choice is transferred to the MRU so it's not picked again too soon.
// If the evaluate flag is given, further Evaluate() it as a fuzz string.
// Returns the chosen results, or the empty string if no choices available.
//
public string Pick(bool evaluate=false)
{
Recycle();
if (choices.Count == 0)
return "";
int pick = random.Next(0, choices.Count);
string chosen = choices[pick];
choices.RemoveAt(pick);
recent.Enqueue(chosen);
if (evaluate)
return Evaluate(chosen);
return chosen;
}

// When the MRU gets too long, return the oldest MRU choice(s) back
// to the active list of choices.
//
private void Recycle()
{
// Caps the MRU at half of total choices.
while (recent.Count > choices.Count)
{
string choice = recent.Dequeue();
choices.Add(choice);
}
}

// Evaluate a single fuzz string.
// "(He|She|They) (picked|selected) a (green|red|blue) (ball|block)."
// "She picked a red ball."
// Replace any parenthetical phrase with one of its choices at random.
// Allows for nesting of choices. There's currently no way to escape
// parentheses or vertical bars so strings must not include strays.
// Returns one permutation from all choice alternatives given.
// Does not remember what choices were given.
// There is no MRU of individual permutations given.
//
public static string Evaluate(string fuzz)
public static string Evaluate(string fuzz)
{
if (string.IsNullOrEmpty(fuzz))
return "";
Expand Down Expand Up @@ -61,3 +147,4 @@ private static string PickAlternate(string fuzz)
}

}

27 changes: 21 additions & 6 deletions DiscordBot/Modules/UserModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using DiscordBot.Utils;
using HtmlAgilityPack;
using DiscordBot.Attributes;
using DiscordBot.Data;

namespace DiscordBot.Modules;

Expand All @@ -31,6 +32,8 @@ public class UserModule : ModuleBase
#endregion

private readonly Random _random = new();
private FuzzTable _slapObjects = new();
private FuzzTable _slapFails = new();

[Command("Help"), Priority(100)]
[Summary("Does what you see now.")]
Expand Down Expand Up @@ -624,10 +627,20 @@ public async Task SlapUser(params IUser[] users)

var uname = Context.User.GetUserPreferredName();

if (Settings.UserModuleSlapChoices == null || Settings.UserModuleSlapChoices.Count == 0)
Settings.UserModuleSlapChoices = new List<string>() { "fish", "mallet" };
if (Settings.UserModuleSlapFails == null || Settings.UserModuleSlapFails.Count == 0)
Settings.UserModuleSlapFails = new List<string>() { "hurting themselves" };
if (_slapObjects.Count == 0)
_slapObjects.Add(Settings.UserModuleSlapChoices);
if (_slapObjects.Count == 0)
_slapObjects.Add("fish|mallet");

if (_slapFails.Count == 0)
_slapFails.Add(Settings.UserModuleSlapFails);
if (_slapFails.Count == 0)
_slapFails.Add("hurting themselves");

// if (Settings.UserModuleSlapChoices == null || Settings.UserModuleSlapChoices.Count == 0)
// Settings.UserModuleSlapChoices = new List<string>() { "fish", "mallet" };
// if (Settings.UserModuleSlapFails == null || Settings.UserModuleSlapFails.Count == 0)
// Settings.UserModuleSlapFails = new List<string>() { "hurting themselves" };

bool fail = (_random.Next(1, 100) < 5);

Expand All @@ -642,9 +655,11 @@ public async Task SlapUser(params IUser[] users)
if (fail)
{
sb.Append(" around a bit with a large ");
sb.Append(Settings.UserModuleSlapChoices[_random.Next() % Settings.UserModuleSlapChoices.Count]);
sb.Append(_slapObjects.Pick(true));
//sb.Append(Settings.UserModuleSlapChoices[_random.Next() % Settings.UserModuleSlapChoices.Count]);
sb.Append(", but misses and ends up ");
sb.Append(Settings.UserModuleSlapFails[_random.Next() % Settings.UserModuleSlapFails.Count]);
sb.Append(_slapFails.Pick(true));
//sb.Append(Settings.UserModuleSlapFails[_random.Next() % Settings.UserModuleSlapFails.Count]);
sb.Append(".");
}
else
Expand Down
Loading