Skip to content

Instantly share code, notes, and snippets.

@Xiaoy312
Last active November 29, 2016 19:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Xiaoy312/dba9d652dd9cfd22791d856526d1afcf to your computer and use it in GitHub Desktop.
Save Xiaoy312/dba9d652dd9cfd22791d856526d1afcf to your computer and use it in GitHub Desktop.
Category Category2 Category3 English EnglishAlias Chinese ChineseAlias RegexPattern RegexOptions Comment
Modifier S. S. \b(S[ \.]|S[nr]\.?|Senior) * i add space for `\b` to works
Modifier T${level} T${level} T(?<level>[1-3]) * i add space for `\b` to works
Modifier ${quantity}x ${quantity}x \b(?<quantity>\d)(?=[^x\:\d\-]) i add space for `\b` to works
Modifier x${quantity} x${quantity} x(?<quantity>\d) i add space for `\b` to works
Formatting ", " ", " ?\+ ?
Formatting ${floor}: ${floor}: ^(?<floor>\d+(\-\d+)?) ?[:f=\-\.)] * im
CommonTypo Aladdin Aladdin alladin i
CommonTypo Wyvern Wyvern Wyrven i
Glossary Wyvern Strategy 飛龍偷塔 Wyvern Strategy i
Unit 5* Orc Naga 蛇神 Naga i
Unit 5* Orc Wyvern Rider Wyvern 飛龍騎士 飛龍 Wyvern Rider|Wyvern i
Unit 5* Orc Raptor Rider Raptor 猛禽騎士 Raptor Rider|Raptor i
Unit 5* Orc Battle Drummer Drummer 戰鼓祭祀 Battle Drummer i
Unit 5* Orc Sorcerer 獸族咒巫 Sorcerer|Sorc i
Unit 5* Orc Wolf Rider 狼騎 Wolf Rider i
Unit 5* Orc Ice Wizard 冰法師 Ice ?Wizard|\bIW\b i
Unit 5* Orc Big Foot BF 雪魔 Big ?Foot|BF i
Unit 5* Undead Medusa 梅杜莎 Medusa|M[ou]m(my)? i
Unit 5* Undead Lich Lich 巫妖 Lich i
Unit 5* Undead Dark Admiral Daddy 黑暗提督 Dark Admiral|Admiral|Daddy|\bDad\b i
Unit 5* Undead Succubus 魅魔 Succubus|Succ i
Unit 5* Undead The Ninja of Darkness Ninja 暗忍 NoD|Ninja i
Unit 5* Undead Dark Archer 沈默箭魔 Dark Archer|\bDA\b i
Unit 5* Undead Death Knight 死亡騎士 Death Knight|DK i
Unit 5* Undead Bomb Unit 炸彈魔 Bomb Unit|Bomb i
Unit 5* Undead Hands of Death HoD 法老王 Hands of Death|Hand of Death|HoD i
Unit 5* Elf Sylphid 射擊遊戲 Sylphid|Sylph i
Unit 5* Elf Alchemist Alch 煉金術師 煉金 Alchemist|Alch i
Unit 5* Elf Hoyden Goku Goku 蠻橫女悟空 悟空 Hoyden Goku|Goku i
Unit 5* Elf Forest Guardian 大地守護神 Forest Guardian|FG i
Unit 5* Elf Druid 德魯伊 Druid i
Unit 5* Elf Fairy 妖精 Fairy i
Unit 5* Elf Unicorn Knight UK 精靈半人馬 半人馬 Unicorn Knight|UK i
Unit 5* Elf Wolf Warrior 狼戰士 Wolf Warrior|WW i
Unit 5* Elf Ent 樹精 Ent i
Unit 5* Human Steam Punk 蒸汽龐克 Steam ?Punk|\bSP\b i
Unit 5* Human Pilot 飛行員 Pilot i
Unit 5* Human Hot - Blooded Xuanzang HotBlooded 熱血三藏 三藏 Hot - Blooded Xuanzang|Hot ?Blood(ed)?|\bHB\b i
Unit 5* Human Griffin Rider Griffin 格裏芬 Griffin ?Rider|Griffin|\bGR\b i
Unit 5* Human Aladdin 阿拉丁 Aladdin i
Unit 5* Human Priest 人族祭司 Priest i
Unit 5* Human Gunner 人族砲兵 Gunner i
Unit 5* Human Fire Mage 人族火巫 Fire ?Mage|\bFM\b i
Unit 5* Human Golem 泰坦 Golem i
Unit 4* Orc Orc Ax Unit OA 獸族斧戰士 獸族斧戰士(飛斧) Orc Ax Unit|\bOAU?\b i
Unit 4* Undead Black Magic Wizard BMW 詛咒法師 Black Magic Wizard|\bBMW\b i
Unit 4* Elf Wind Mage WM 精靈風暴法師 Wind Mage|\bWM\b i
Unit 4* Human Cavalry Knight 人族騎士 Cavalry Knight|Cavalry|\bCK\b i
Unit 3* Orc Orc Hammer Unit OHU 獸族斧戰兵 獸族斧戰兵(嘲諷) Orc Hammer Unit|\bOHU\b i
Unit 3* Orc Orc Wing 獸族冰鳥 Orc Wing|\bOW\b i
Unit 3* Undead Great Hammer Unit GHU 骷髏戰鎚兵 Great Hammer Unit|\bGHU?\b i
Unit 3* Undead Skeleton Warrior SW 骷髏戰士 Skeleton Warrior|\bSW\b i
Unit 3* Elf Green Eagle 綠鷹 Green Eagle|Eagle|\bGE\b i
Unit 3* Elf High Elf Archer HEA 遠古精靈弓兵 High Elf Archer|\bHEA\b i
Unit 3* Human Hammer Knight HK 人族戰錘兵 Hammer Knight|\bHK\b i
Unit 3* Human Musketeer 人族槍兵 Musketeer|Musk i
Unit 2* Orc Frost Mage 獸族冰巫 Frost ?Mage|Frost i
Unit 2* Orc Orc Hunter OH 獸族獵人 Orc Hunter|\bOH\b i
Unit 2* Undead Warlock 電術士 Warlock i
Unit 2* Undead Ghost 幽鬼 Ghost i
Unit 2* Elf Poison Archer 毒弓兵 Poison Archer|\bPA\b i
Unit 2* Elf Elf Warrior 精靈戰士 Elf Warrior|\bEW\b i
Unit 2* Human Fire Bird 火鳥 Fire ?Bird|\bFB\b i
Unit 2* Human Heavy Infantry 人族重步兵 Heavy ?Inf(antry)?|\bHI\b i
Unit 1* Orc Orc Fighter 獸族戰士 [Oo]rc [Ff]ighter|\bOF\b i
Unit 1* Undead Skeleton Unit 骷髏兵 Skeleton Unit
Unit 1* Elf Elf Archer 精靈弓兵 Elf Archer|\bEA\b i
Unit 1* Human Infantry 人族步兵 Infantry|\bInf\b i
Modifier normal + i
Modifier S. 高階 \bS\. * i remove trailing space if needed
Modifier T${level} T${level} T(?<level>[1-3]) * i remove trailing space if needed
Modifier ${quantity}x ${quantity}x \b(?<quantity>\d)x + i
Modifier Luck 運氣 luck i
Modifier Speed 速度 speed i
??? all 全 all + i
??? rest 剩下全 rest + i
??? tank 坦 tank i
??? any 任意 any|filler i
??? or 或 \bor\b i
Formatting ", " ", " ",? *(and|then) +" i
Formatting ", " ", " ",(?! )"
Formatting "," "," " +,"
Formatting +
Formatting \.$
Formatting 1x ?
<Query Kind="Program">
<Reference>&lt;RuntimeDirectory&gt;\System.Net.Http.dll</Reference>
<NuGetReference>AngleSharp</NuGetReference>
<NuGetReference>LumenWorksCsvReader</NuGetReference>
<Namespace>AngleSharp</Namespace>
<Namespace>AngleSharp.Dom</Namespace>
<Namespace>AngleSharp.Html</Namespace>
<Namespace>AngleSharp.Parser.Html</Namespace>
<Namespace>System.Net.Http</Namespace>
<Namespace>System.Threading.Tasks</Namespace>
<Namespace>LumenWorks.Framework.IO.Csv</Namespace>
</Query>
void Main()
{
Util.NewProcess = true;
const string SolutionTheadUrl = @"https://www.reddit.com/r/EndlessFrontier/comments/5bco5z/tower_of_trial_round_74/";
var comments = Reddit.GetComments(SolutionTheadUrl);
comments
.Dump("raw comment data", 0)
.ApplyQuickFixes(GetQuickFixes().ToArray())
.ApplyFixes(GetSolutionFixes())
.RemoveNoSolutionTextOrComment(showRemovedContent: true)
.Dump("solutions", 0)
.AddCustomSolution(GetCustomSolution())
.Cleanup()
.SingularizeUnitName()
.FormatEnglish()
//.TranslateToChinese()
.AggregateAndSortSolutions()
.OnDemand()
.Dump("sorted solutions");
}
// Define other methods and classes here
/// <summary></summary>
string GetSolutionFixes()
{
// note: delete fix cannot be placed last...
return @"
-----t1_exampl1@DEADBEEF-----
-----t1_exampl2@BAADF00D-----
note: the above fix will remove the referenced comment
you can edit the comment however you like here.
usually it is used to:
- voiding a solution, by removing the line or screwing up the syntax
- fix formatting issues like`Lich<sup>3</sup>` which is read as `Lich3` and then formatted to `Lich 3x`...
- fix other minor aesthetic issues like adding comma separator between units
"
.Trim();
}
/// <summary>Insert additional solutions that are not found on the solution thread, like from Discord</summary>
string GetCustomSolution()
{
return @"
99: all T3 SW
"
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Where(x => !string.IsNullOrWhiteSpace(x))
.Where(x => !x.StartsWith("//"))
.Aggregate("", (a, b) => a + '\n' + b)
.Trim();
}
/// <summary>Used to fix simple error like typo, or repeated errors by the same person</summary>
/// <remarks>not every short name should be added into the "translation rules", at least not until it is widely accepted</remarks>
IEnumerable<Func<string, string>> GetQuickFixes()
{
Func<string, string, Func<string, string>> replace = (a, b) => x => x.Replace(a, b);
Func<string, string, Func<string, string>> rgxreplace = (a, b) => x => Regex.Replace(x, a, b);
yield return replace("Lock", "Lich");
yield return replace("cloak - ", "");
yield return replace("s -> credit to asyranilin", "");
yield return replace("Infantery", "Infantry");
yield return rgxreplace("Sylp(hid)?", "Sylph");
yield return replace("Punk", "SteamPunk");
yield return replace("Griffon Rider", "GR");
yield return replace("probably a boatload of luck", "");
}
public class Reddit
{
public static List<RedditComment> GetComments(string url)
{
var html = new HttpClient().GetStringAsync(url).Result;
var document = new HtmlParser().Parse(html);
return document.QuerySelector(".sitetable.nestedlisting")
.QuerySelectorAll(".thing.comment")
.Select(RedditComment.Parse)
.ToList();
}
}
public class RedditComment
{
public string ID { get; set; }
//public string Author { get; set; }
public string Text { get; set; }
public string Hash { get; set; }
public Hyperlinq FixMe => new Hyperlinq(() =>
$"\n-----{ID}@{Hash}-----\n{Text}"
.Dump($"fix for " + ID),
"FixMe");
public static RedditComment Parse(IElement element)
{
return new RedditComment
{
ID = element.GetAttribute("data-fullname"),
//Author = element.GetAttribute("data-author"),
Text = element.QuerySelector(">.entry>form.usertext>.usertext-body").TextContent.Trim(),
Hash = element.QuerySelector(">.entry>form.usertext>.usertext-body").TextContent.Trim().GetHashCode().ToString("X")
};
}
}
public class TranslationRule
{
public string Chinese { get; set; }
public string English { get; set; }
public string RegexPattern { get; set; }
public RegexOptions RegexOptions { get; set; }
public string Translate(string input, Func<TranslationRule, string> languageSelector)
{
return Regex.Replace(input, RegexPattern, languageSelector(this), RegexOptions);
}
}
public class Translator : List<TranslationRule>
{
public static Translator Instance { get; }
static Translator()
{
var path = Path.Combine(
Path.GetDirectoryName(Util.CurrentQueryPath),
"translation rules.txt");
using (var file = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var stream = new StreamReader(file, Encoding.GetEncoding("GBK")))
using (var csv = new CsvReader(stream, true, '\t', CsvReader.DefaultQuote, CsvReader.DefaultEscape, CsvReader.DefaultComment, ValueTrimmingOptions.None))
{
Func<string, string, string> coalesce =
(a, b) => string.IsNullOrWhiteSpace(a) ? b : a;
var regexOptions = new Dictionary<char, RegexOptions>
{
['i'] = RegexOptions.IgnoreCase,
['m'] = RegexOptions.Multiline,
['s'] = RegexOptions.Singleline,
['n'] = RegexOptions.ExplicitCapture,
['x'] = RegexOptions.IgnorePatternWhitespace,
};
Instance = new Translator();
Instance.AddRange(csv.Select(x => new TranslationRule()
{
English = coalesce(x[4], x[3]),
Chinese = coalesce(x[6], x[5]),
RegexPattern = x[7],
RegexOptions = x[8].ToCharArray()
.Select(c => regexOptions[c])
.Aggregate(RegexOptions.None, (a, b) => a | b)
}));
}
}
public string Translate(string input, Func<TranslationRule, string> languageSelector)
{
foreach (var rule in this.Where(x => !string.IsNullOrWhiteSpace(x.RegexPattern)))
{
input = rule.Translate(input, languageSelector);
}
return input;
}
}
public static class CommentHelper
{
/// <summary>Remove any line from the comment which doesnt abide the solution synax: {floor}[separators] {solution...} </summary>
public static IList<RedditComment> RemoveNoSolutionTextOrComment(this IList<RedditComment> comments, bool showRemovedContent = true)
{
var removedTexts = Enumerable
.Repeat(new
{
ID = default(string),
Deleted = default(bool),
Text = default(object),
FixMe = default(Hyperlinq),
}, 0)
.ToList();
foreach (var comment in comments.ToList())
{
var lines = comment.Text.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => new
{
Text = x,
Match = Regex.Match(x, @"^(floor ?)?(?<floor>\d+(\-\d+)?) ?[:\-f)] *(?<solution>.+)", RegexOptions.IgnoreCase)
})
.ToList();
#region Debug code
if (showRemovedContent && lines.Any(x => !x.Match.Success))
{
removedTexts.Add(new
{
comment.ID,
Deleted = !lines.Any(x => x.Match.Success),
Text = Util.VerticalRun(lines.Select(line =>
Util.HighlightIf(line.Text, _ => !line.Match.Success))),
FixMe = comment.FixMe,
});
}
#endregion
if (!lines.Any(x => x.Match.Success))
{
comments.Remove(comment);
continue;
}
comment.Text = string.Join("\n", lines
.Where(x => x.Match.Success)
.Select(x => x.Match.Result("${floor}: ${solution}")));
}
#region Debug code
if (showRemovedContent)
{
removedTexts
.OnDemand()
.Dump("removed content");
}
#endregion
return comments;
}
/// <summary>Fully replace or remove comment content. The hash is used to check is the content is invalidated by an edit and to throw an error should it happen.</summary>
/// <remarks>An empty fix without any content removes the referenced comment</remarks>
public static IList<RedditComment> ApplyFixes(this IList<RedditComment> comments, string solutionFixes)
{
var fixes = Regex.Split(solutionFixes, "(?=-----.+@.+-----)")
.Skip(1)
.Select(text => text.Split('\n'))
.Select(lines => new
{
CommentID = Regex.Match(lines[0], "^-----(?<id>.+)@(?<hash>.+)-----\r$").Result("${id}"),
CommentHash = Regex.Match(lines[0], "^-----(?<id>.+)@(?<hash>.+)-----\r$").Result("${hash}"),
UpdatedCommentText = string.Join("\n", lines.Skip(1))
});
foreach (var fix in fixes)
{
var comment = comments.FirstOrDefault(x => x.ID == fix.CommentID);
if (comment == null) continue;
if (comment.Hash != fix.CommentHash)
{
comment.FixMe.Action.Invoke();
throw new InvalidDataException("solution has been updated");
}
if (string.IsNullOrWhiteSpace(fix.UpdatedCommentText))
{
comments.Remove(comment);
continue;
}
comment.Text = fix.UpdatedCommentText;
}
return comments;
}
/// <summary>Apply simple text substitution <see cref="GetQuickFixes()"></summary>
public static IList<RedditComment> ApplyQuickFixes(this IList<RedditComment> comments, params Func<string, string>[] solutionFixes)
{
foreach (var comment in comments)
{
comment.Text = solutionFixes.Aggregate(comment.Text, (text, fix) => fix(text));
}
return comments;
}
/// <summary>Change plural form to singular form</summary>
/// <remarks>for repeated offender mustly</remarks>
public static IList<RedditComment> SingularizeUnitName(this IList<RedditComment> comments)
{
foreach (var comment in comments)
{
comment.Text = Regex.Replace(
comment.Text,
"(?<noun>mage|warrior|knight|musketeer)s",
"${noun}",
RegexOptions.IgnoreCase);
}
return comments;
}
/// <summary>Remove parenthesis and anything in between</summary>
public static IList<RedditComment> Cleanup(this IList<RedditComment> comments)
{
foreach (var comment in comments)
{
comment.Text = Regex.Replace(comment.Text, @"\(.+\)", "");
}
return comments;
}
public static IList<RedditComment> AddCustomSolution(this IList<RedditComment> comments, string solution)
{
if (!string.IsNullOrWhiteSpace(solution))
{
comments.Add(new RedditComment()
{
Text = solution
});
}
return comments;
}
public static IList<RedditComment> FormatEnglish(this IList<RedditComment> comments)
{
foreach (var comment in comments)
{
comment.Text = Translator.Instance.Translate(comment.Text, r => r.English);
}
return comments;
}
/// <remarks>This is also used to quickly validate all input are sanitized, as any no-formatted part is contrasted with chinese</remarks>
public static IList<RedditComment> TranslateToChinese(this IList<RedditComment> comments)
{
foreach (var comment in comments)
{
comment.Text = Translator.Instance.Translate(comment.Text, r => r.Chinese);
}
return comments;
}
/// <summary>Convert the comments into a list of solution</summary>
public static string AggregateAndSortSolutions(this IList<RedditComment> comments)
{
const string MarkUpNewLine = " ";
return string.Join("\n", comments
.SelectMany(x => x.Text.Split('\n'))
.Select(x => x.Trim() + MarkUpNewLine)
.Distinct()
.OrderBy(x => int.Parse(Regex.Match(x, @"^\d+").Value)));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment