188 lines
6.1 KiB
C#
188 lines
6.1 KiB
C#
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;
|
|
}
|
|
}
|
|
} |