Initial commit — Wordle/letter word solver and scorer
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
public class WordleUtil
|
||||
{
|
||||
public enum CharacterResponse
|
||||
{
|
||||
Actual,
|
||||
OtherPosition,
|
||||
NotInWord
|
||||
}
|
||||
|
||||
public struct PositionCriteria
|
||||
{
|
||||
public char? Correct;
|
||||
public string OtherPosition;
|
||||
|
||||
public string GetRegexRestriction() =>
|
||||
Correct.HasValue
|
||||
? Correct.Value.ToString()
|
||||
: !string.IsNullOrEmpty(OtherPosition)
|
||||
? "[^" + OtherPosition + "]"
|
||||
: ".";
|
||||
|
||||
public void RemoveLetter(char letter)
|
||||
{
|
||||
OtherPosition = OtherPosition?.Replace(letter.ToString(), string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public class WordleState
|
||||
{
|
||||
public string InWord { get; private set; } = string.Empty;
|
||||
public string NotInWord { get; private set; } = string.Empty;
|
||||
public PositionCriteria[] Criteria { get; } = new PositionCriteria[5];
|
||||
|
||||
public bool IsSolved() => Criteria.All(c => c.Correct.HasValue);
|
||||
|
||||
private void AddInWord(char letter)
|
||||
{
|
||||
if (!InWord.Contains(letter))
|
||||
InWord += letter;
|
||||
}
|
||||
|
||||
private void AddNotInWord(char letter)
|
||||
{
|
||||
if (!NotInWord.Contains(letter))
|
||||
NotInWord += letter;
|
||||
}
|
||||
|
||||
public void Update(string word, CharacterResponse[] response)
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var letter = word[i];
|
||||
switch (response[i])
|
||||
{
|
||||
case CharacterResponse.Actual:
|
||||
// remove this letter from the criteria
|
||||
foreach (var positionCriteria in Criteria)
|
||||
positionCriteria.RemoveLetter(letter);
|
||||
AddInWord(letter);
|
||||
Criteria[i].Correct = letter;
|
||||
break;
|
||||
case CharacterResponse.OtherPosition:
|
||||
AddInWord(letter);
|
||||
Criteria[i].OtherPosition += letter;
|
||||
break;
|
||||
case CharacterResponse.NotInWord:
|
||||
AddNotInWord(letter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetRegexRestrictions(PositionCriteria[] criteria)
|
||||
{
|
||||
return
|
||||
criteria
|
||||
.Aggregate(string.Empty,
|
||||
(r, criterion)
|
||||
=> r + criterion.GetRegexRestriction()
|
||||
);
|
||||
}
|
||||
|
||||
public IList<string> Words { get; }
|
||||
private Dictionary<int, double> _repeatedLetterFactor;
|
||||
|
||||
public WordleUtil()
|
||||
: this(@"WordleWords.txt")
|
||||
{
|
||||
}
|
||||
|
||||
public WordleUtil(string filename)
|
||||
: this(filename.GetFileLines())
|
||||
{
|
||||
}
|
||||
|
||||
public WordleUtil(IEnumerable<string> words)
|
||||
{
|
||||
Words = new List<string>();
|
||||
foreach (var word in words)
|
||||
{
|
||||
var w = word.Canonicalize();
|
||||
var split = w.Split(',');
|
||||
Words.Add(split[0]);
|
||||
}
|
||||
|
||||
Words.Remove("ADMIN");
|
||||
Words.Remove("INBOX");
|
||||
|
||||
var re =
|
||||
from w in Words
|
||||
let repeats = RepeatedLettersCount(w)
|
||||
group repeats by repeats
|
||||
into g
|
||||
select g;
|
||||
var max = re.Max(r => r.Count());
|
||||
_repeatedLetterFactor =
|
||||
re.Select(r => Tuple.Create(r.Key, r.Count() * 1.0 / max))
|
||||
.ToDictionary(s => s.Item1, s => s.Item2);
|
||||
}
|
||||
|
||||
public static CharacterResponse[] GetGuessResult(string actual, string guess)
|
||||
{
|
||||
if (actual.Length != 5 || guess.Length != 5)
|
||||
throw new ArgumentOutOfRangeException("not right size word or guess");
|
||||
var response = new CharacterResponse[5];
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
if (guess[i] == actual[i])
|
||||
response[i] = CharacterResponse.Actual;
|
||||
else if (actual.Contains(guess[i]))
|
||||
response[i] = CharacterResponse.OtherPosition;
|
||||
else
|
||||
response[i] = CharacterResponse.NotInWord;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public static bool RepeatedLetters(string word)
|
||||
{
|
||||
var uniqueLetters = new HashSet<char>();
|
||||
|
||||
foreach (var c in word)
|
||||
uniqueLetters.Add(c);
|
||||
|
||||
return uniqueLetters.Count < word.Length;
|
||||
}
|
||||
|
||||
|
||||
public static int RepeatedLettersCount(string word)
|
||||
{
|
||||
var uniqueLetters = new HashSet<char>();
|
||||
|
||||
foreach (var c in word)
|
||||
uniqueLetters.Add(c);
|
||||
|
||||
return word.Length - uniqueLetters.Count;
|
||||
}
|
||||
|
||||
public double FactorRepeatedLetters(string word, double score)
|
||||
{
|
||||
return score * _repeatedLetterFactor[RepeatedLettersCount(word)];
|
||||
}
|
||||
public Dictionary<char, int>[] GetLetterPositionFreq(int wordLen)
|
||||
{
|
||||
var ws = Words.Where(w => w.Length == wordLen).ToList();
|
||||
|
||||
var freqList = new Dictionary<char, int>[wordLen];
|
||||
for (var i = 0; i < wordLen; i++)
|
||||
{
|
||||
var letters = Enumerable.Range(0, 26).Select(r => Convert.ToChar('A' + r));
|
||||
freqList[i] =
|
||||
letters.ToDictionary(c => c,
|
||||
c => ws.Count(w => w[i] == c)
|
||||
);
|
||||
}
|
||||
|
||||
return freqList;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user