+ Added formattable benchmark for returns and adjusted to vaguely fuzzy-search

This commit is contained in:
Matthew Cech
2019-05-05 18:55:05 -07:00
parent 3a8306f1e6
commit 99667330df
10 changed files with 248 additions and 30 deletions
+5 -4
View File
@@ -2,6 +2,7 @@ package commands;
import core.Command; import core.Command;
import core.LocStrings; import core.LocStrings;
import core.benchmark.BenchmarkFormattable;
import core.benchmark.BenchmarkFramework; import core.benchmark.BenchmarkFramework;
import dataStructures.KittyChannel; import dataStructures.KittyChannel;
import dataStructures.KittyGuild; import dataStructures.KittyGuild;
@@ -36,18 +37,18 @@ public class CommandBenchmark extends Command
return; return;
} }
String result = null; BenchmarkFormattable commandOutput = null;
synchronized(framework) synchronized(framework)
{ {
result = framework.Run(input.args.trim()); commandOutput = framework.Run(input.args.trim());
} }
if(result == null) if(commandOutput == null)
{ {
res.Call(LocStrings.Stub("BenchmarkInvalid")); res.Call(LocStrings.Stub("BenchmarkInvalid"));
return; return;
} }
res.Call(result); commandOutput.Call(res);
} }
} }
@@ -4,19 +4,20 @@ import java.util.List;
import core.benchmark.BenchmarkCommand; import core.benchmark.BenchmarkCommand;
import core.benchmark.BenchmarkEntry; import core.benchmark.BenchmarkEntry;
import core.benchmark.BenchmarkFormattable;
import core.benchmark.BenchmarkInput; import core.benchmark.BenchmarkInput;
import core.benchmark.BenchmarkManager; import core.benchmark.BenchmarkManager;
public class BenchmarkCommandCompare extends BenchmarkCommand public class BenchmarkCommandCompare extends BenchmarkCommand
{ {
public String OnRun(BenchmarkManager manager, BenchmarkInput input) public BenchmarkFormattable OnRun(BenchmarkManager manager, BenchmarkInput input)
{ {
final String lineDelimiter = "\n"; final String lineDelimiter = "\n";
String output = ""; String output = "";
String[] inputSplit = input.value.trim().split("\\s+"); String[] inputSplit = input.value.trim().split("\\s+");
if(inputSplit.length < 2) if(inputSplit.length < 2)
return "You must to specify 2 models to compare!"; return new BenchmarkFormattable("You must to specify 2 models to compare!");
for(int i = 0; i < inputSplit.length; ++i) for(int i = 0; i < inputSplit.length; ++i)
{ {
@@ -38,6 +39,6 @@ public class BenchmarkCommandCompare extends BenchmarkCommand
output += "----------"; output += "----------";
} }
return output; return new BenchmarkFormattable(output);
} }
} }
@@ -6,10 +6,10 @@ import core.benchmark.*;
public class BenchmarkCommandFind extends BenchmarkCommand public class BenchmarkCommandFind extends BenchmarkCommand
{ {
private final char lineDelimiter = '\n'; private final String lineDelimiter = "\n";
private final int listMax = 30; private final int listMax = 30;
public String OnRun(BenchmarkManager manager, BenchmarkInput input) public BenchmarkFormattable OnRun(BenchmarkManager manager, BenchmarkInput input)
{ {
String output = ""; String output = "";
String searchString = input.value.trim(); String searchString = input.value.trim();
@@ -42,6 +42,6 @@ public class BenchmarkCommandFind extends BenchmarkCommand
output = "Couldn't find any models matching the search `" + searchString + "`!"; output = "Couldn't find any models matching the search `" + searchString + "`!";
} }
return output; return new BenchmarkFormattable(output);
} }
} }
@@ -4,8 +4,11 @@ import java.util.List;
import core.benchmark.BenchmarkCommand; import core.benchmark.BenchmarkCommand;
import core.benchmark.BenchmarkEntry; import core.benchmark.BenchmarkEntry;
import core.benchmark.BenchmarkFormattable;
import core.benchmark.BenchmarkInput; import core.benchmark.BenchmarkInput;
import core.benchmark.BenchmarkManager; import core.benchmark.BenchmarkManager;
import dataStructures.KittyEmbed;
import java.awt.Color;
public class BenchmarkCommandInfo extends BenchmarkCommand public class BenchmarkCommandInfo extends BenchmarkCommand
{ {
@@ -20,27 +23,76 @@ public class BenchmarkCommandInfo extends BenchmarkCommand
return output; return output;
} }
public static KittyEmbed FormatInfoEmbed(BenchmarkEntry entry)
{
// Populate general embed info
KittyEmbed embed = new KittyEmbed();
embed.title = "" + entry.brand + " " + entry.model + "" + lineDelimiter;
embed.authorLink = entry.url;
embed.authorImage = "https://kittybot.net/assets/generic/baseline_assessment_black_48dp.png";
embed.authorText = "[view online]";
embed.color = Color.LIGHT_GRAY;
embed.descriptionText = "Average Bench: " + entry.benchmark + "%";
// Brand coloring
String brandClean = entry.brand.toLowerCase().trim();
if(brandClean.contains("intel"))
embed.color = Color.getHSBColor((float) (205.0/360), 1.00f, 0.75f);
else if(brandClean.contains("amd"))
embed.color = Color.getHSBColor((float) (357.0/360), 0.88f, 0.92f);
else if (brandClean.contains("nvidia"))
embed.color = Color.getHSBColor((float) (081.0/360), 1.00f, 0.72f);
// Add relative performance numbers
switch(entry.type)
{
case CPU:
embed.footerText = "(100% ~ Intel i7-7700k)";
break;
case GPU:
embed.footerText = "(100% ~ Nvidia GTX 1070)";
break;
case SSD:
embed.footerText = "(100% ~ Samsung 850 Pro)";
break;
case HDD:
embed.footerText = "(100% ~ Seagate Baracuda 3TB (2016))";
break;
case RAM:
embed.footerText = "(100% ~ Corsair Vengence LPX 2666 C15 4x8GB w/ Intel Skylake)";
break;
case USB:
embed.footerText = "(100% ~ Sandisk Extreme 64GB)";
break;
default:
}
return embed;
}
@Override @Override
public String OnRun(BenchmarkManager manager, BenchmarkInput input) public BenchmarkFormattable OnRun(BenchmarkManager manager, BenchmarkInput input)
{ {
String searchString = input.value.trim(); String searchString = input.value.trim();
long start = System.currentTimeMillis();
String output = "";
output += lineDelimiter;
List<BenchmarkEntry> entries = manager.FindModel(searchString); List<BenchmarkEntry> entries = manager.FindModel(searchString);
if(entries.size() < 1) if(entries.size() < 1)
output += "Couldn't find any models containing `" + searchString + "`!"; return new BenchmarkFormattable("Couldn't find any models containing `" + searchString + "`!");
output += FormatInfo(entries.get(0)); List<BenchmarkEntry> sortedEntries = manager.EvaluateLevenshteinDistance(entries, searchString);
output += lineDelimiter; BenchmarkEntry entry = sortedEntries.get(0);
KittyEmbed embed = FormatInfoEmbed(entry);
if(entries.size() > 1) if(entries.size() > 1)
output += "_chose " + entries.get(0).model + " from " + entries.size() + " potential components. To see others, try the command `benchmark find " + searchString +"`_)"; embed.footerText += " (Chose the " + entry.model + " from " + entries.size() + " potential components. To see others, try the command 'benchmark find " + searchString +"')";
return output; embed.footerText += " (took " + (System.currentTimeMillis() - start) + "ms)";
return new BenchmarkFormattable(embed);
} }
} }
+5 -3
View File
@@ -1,8 +1,10 @@
package core.benchmark; package core.benchmark;
public abstract class BenchmarkCommand { public abstract class BenchmarkCommand
public BenchmarkCommand() { } {
public BenchmarkCommand()
{ }
// OVERRIDE ME // OVERRIDE ME
public abstract String OnRun(BenchmarkManager manager, BenchmarkInput input); public abstract BenchmarkFormattable OnRun(BenchmarkManager manager, BenchmarkInput input);
} }
@@ -0,0 +1,40 @@
package core.benchmark;
import dataStructures.KittyEmbed;
import dataStructures.Response;
// Only one isn't null!
public class BenchmarkFormattable
{
public final KittyEmbed resEmbed;
public final String resString;
// Default constructor is hidden and disabled.
// If somehow it is called, defaults to an empty string and no embed.
@SuppressWarnings("unused")
private BenchmarkFormattable()
{
this.resEmbed = null;
this.resString = "";
}
public BenchmarkFormattable(String res)
{
this.resEmbed = null;
this.resString = res;
}
public BenchmarkFormattable(KittyEmbed embed)
{
this.resEmbed = embed;
this.resString = null;
}
public void Call(Response res)
{
if(resEmbed == null)
res.Call(resString);
else
res.CallEmbed(resEmbed);
}
}
+2 -2
View File
@@ -22,7 +22,7 @@ public class BenchmarkFramework
} }
// Runs a command if possible. // Runs a command if possible.
public String Run(String args) public BenchmarkFormattable Run(String args)
{ {
BenchmarkInput input = new BenchmarkInput(args); BenchmarkInput input = new BenchmarkInput(args);
return ExecuteCommand(input.key, input); return ExecuteCommand(input.key, input);
@@ -39,7 +39,7 @@ public class BenchmarkFramework
} }
// Executes a command with the specified name, and provides it with some extra input data. // Executes a command with the specified name, and provides it with some extra input data.
private String ExecuteCommand(String name, BenchmarkInput input) private BenchmarkFormattable ExecuteCommand(String name, BenchmarkInput input)
{ {
BenchmarkCommand command = benchmarkCommand.get(name.toLowerCase()); BenchmarkCommand command = benchmarkCommand.get(name.toLowerCase());
+74
View File
@@ -2,8 +2,10 @@ package core.benchmark;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import dataStructures.Pair;
import utils.FileUtils; import utils.FileUtils;
import utils.directoryMonitor.DirectoryMonitor; import utils.directoryMonitor.DirectoryMonitor;
import utils.directoryMonitor.MonitoredFile; import utils.directoryMonitor.MonitoredFile;
@@ -67,6 +69,78 @@ public class BenchmarkManager
} }
} }
// Re-evaluates and re-orders the input list based on the Levenshtein distance of the
// original search and the contents of the provided list of benchmark entries.
public List<BenchmarkEntry> EvaluateLevenshteinDistance(List<BenchmarkEntry> entries, String search)
{
// Populate cost list with heuristic results
List<Pair<BenchmarkEntry, Integer>> cost = new ArrayList<Pair<BenchmarkEntry, Integer>>();
int bestSoFar = Integer.MAX_VALUE;
for(int i = 0; i < entries.size(); ++i)
{
BenchmarkEntry entry = entries.get(i);
String entryString = entry.brand + " " + entry.model;
int heuristic = LevenshteinHeuristic(entryString, search, bestSoFar);
if(heuristic < bestSoFar)
bestSoFar = heuristic;
cost.add(new Pair<BenchmarkEntry, Integer>(entry, heuristic));
}
// Sort list
Collections.sort(cost, (i1, i2) -> i1.Second.compareTo(i2.Second));
// Build new output list
List<BenchmarkEntry> sortedEntries = new ArrayList<BenchmarkEntry>();
for(int i = 0; i < cost.size(); ++i)
sortedEntries.add(cost.get(i).First);
return sortedEntries;
}
// Finds the minimum integer in an array of ints and returns it.
private int min(int[] arr)
{
int min = Integer.MAX_VALUE;
for(int i = 0; i < arr.length; ++i)
{
if(arr[i] < min)
min = arr[i];
}
return min;
}
// Returns cost based on distance of characters from string. This is a kinda sloppy way
// to do it, but because I keep tabs on the best result so far, it could be much worse.
// Still chucks a lot - a non-recursive result w/ memoization would be best but this will do.
private int LevenshteinHeuristic(String str1, String str2, int bestSoFar)
{
int cost;
if(str1.length() <= 0)
return str2.length();
if(str2.length() <= 0)
return str1.length();
if(str1.charAt(0) == str2.charAt(0))
cost = 0;
else
cost = 1;
int distance = Math.abs(str1.length() - str2.length());
if(distance > bestSoFar)
return distance;
int s1 = LevenshteinHeuristic(str1.substring(1), str2, bestSoFar) + 1;
int s2 = LevenshteinHeuristic(str1, str2.substring(1), bestSoFar) + 1;
int s3 = LevenshteinHeuristic(str1.substring(1), str2.substring(1), bestSoFar) + cost;
return min(new int[] {s1, s2, s3 });
}
// Search for a substring in the model name // Search for a substring in the model name
public List<BenchmarkEntry> FindModel(String modelSubstr) public List<BenchmarkEntry> FindModel(String modelSubstr)
{ {
+19
View File
@@ -0,0 +1,19 @@
package dataStructures;
import java.awt.Color;
public class KittyEmbed
{
public String title = null;
public Color color = null;
public String footerText = null;
public String authorText = null;
public String authorLink = null;
public String authorImage = null;
public String descriptionText = null;
public KittyEmbed()
{
this.title = "Untitled";
}
}
+34 -5
View File
@@ -5,26 +5,52 @@ import java.io.InputStream;
import net.dv8tion.jda.core.JDA; import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.entities.TextChannel; import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.events.message.guild.*; import net.dv8tion.jda.core.events.message.guild.*;
import net.dv8tion.jda.core.EmbedBuilder;
import utils.GlobalLog; import utils.GlobalLog;
import utils.LogFilter; import utils.LogFilter;
// NOTE(wisp): This isn't constructed with the factory at this time, mostly // This isn't constructed with the factory at this time, mostly because we need it
// because we need it to handle all the response queue behavior and everything // to handle all the response queue behavior and everything internally. So long as
// internally. So long as commands aren't exposed to the GuildMessageReceivedEvent // commands aren't exposed to the GuildMessageReceivedEvent then we should be fine.
// then we should be fine.
public class Response public class Response
{ {
// Variables
private GuildMessageReceivedEvent event; private GuildMessageReceivedEvent event;
private JDA kitty; private JDA kitty;
private final int discordMessageMax = 2000; private final int discordMessageMax = 2000;
private final int kittyMessageMax = 1950; private final int kittyMessageMax = 1950;
// Constructor
public Response(GuildMessageReceivedEvent event, JDA kitty) public Response(GuildMessageReceivedEvent event, JDA kitty)
{ {
this.event = event; this.event = event;
this.kitty = kitty; this.kitty = kitty;
} }
// Builds a nicely formatted embedded message based on information provided
public void CallEmbed(KittyEmbed embedInfo)
{
EmbedBuilder embed = new EmbedBuilder();
if(embedInfo.title != null)
embed.setTitle(embedInfo.title);
if(embedInfo.color != null)
embed.setColor(embedInfo.color);
if(embedInfo.descriptionText != null)
embed.setDescription(embedInfo.descriptionText);
if(embedInfo.footerText != null)
embed.setFooter(embedInfo.footerText, null);
if(embedInfo.authorImage != null || embedInfo.authorLink != null || embedInfo.authorText != null)
embed.setAuthor(embedInfo.authorText, embedInfo.authorLink, embedInfo.authorImage);
event.getChannel().sendMessage(embed.build()).queue();
}
// Queues a standard text-based message response to the channel that issued the command.
public void Call(String toRespondWith) public void Call(String toRespondWith)
{ {
GlobalLog.Log(LogFilter.Response, "Sending response: " + toRespondWith); GlobalLog.Log(LogFilter.Response, "Sending response: " + toRespondWith);
@@ -38,6 +64,7 @@ public class Response
} }
} }
// Queues a standard text-based message response to the specified channel.
public void CallToChannel(String toRespondWith, String channelID) public void CallToChannel(String toRespondWith, String channelID)
{ {
TextChannel channel; TextChannel channel;
@@ -52,6 +79,7 @@ public class Response
} }
} }
// Immediately dispatches the message to the channel that issued the command.
public void CallImmediate(String toRespondWith) public void CallImmediate(String toRespondWith)
{ {
GlobalLog.Log(LogFilter.Response, "Sending immediate response: " + toRespondWith); GlobalLog.Log(LogFilter.Response, "Sending immediate response: " + toRespondWith);
@@ -65,13 +93,14 @@ public class Response
} }
} }
// This is for responding with a file rather than a String // Queues a file response to the channel that issued the command.
public void CallFile(File toRespondWith, String extension) public void CallFile(File toRespondWith, String extension)
{ {
GlobalLog.Log(LogFilter.Response, "Sending file response"); GlobalLog.Log(LogFilter.Response, "Sending file response");
event.getChannel().sendFile(toRespondWith, "return." + extension).queue(); event.getChannel().sendFile(toRespondWith, "return." + extension).queue();
} }
// Queues an input stream response to the channel that issued the command.
public void CallInput(InputStream in, String extension) public void CallInput(InputStream in, String extension)
{ {
GlobalLog.Log(LogFilter.Response, "Sending input stream response"); GlobalLog.Log(LogFilter.Response, "Sending input stream response");