ex-oop-03-voctrainer/VocabularyTrainer/Trainer.cs

184 lines
No EOL
6.2 KiB
C#

using Figgle;
namespace VocabularyTrainer;
/// <summary>
/// Vocabulary trainer.
/// Based on a supplied vocabulary training cycles are performed.
/// </summary>
public sealed class Trainer
{
private const int WordIdx = 0;
private const int TranslationIdx = 1;
private const int CycleCount = 3;
private readonly VocabularyItem[] _vocabularyItems;
/// <summary>
/// Constructs a new <see cref="Trainer"/> instance based on the given vocabulary.
/// </summary>
/// <param name="wordsAndTranslations">Raw vocabulary read from a file</param>
public Trainer(string[][] wordsAndTranslations)
{
_vocabularyItems = CreateVocabularyItems(wordsAndTranslations);
}
/// <summary>
/// Performs a training cycle for <see cref="CycleCount"/> words.
/// </summary>
public void PerformTrainingCycle()
{
Console.Clear();
Console.ForegroundColor = ConsoleColor.DarkCyan;
Console.WriteLine(FiggleFonts.Standard.Render("Vocabulary Trainer"));
Console.WriteLine($"Starting a new training cycle with {CycleCount} tries ...");
var alreadyAsked = new bool[_vocabularyItems.Length];
for (var i = 0; i < CycleCount; i++)
{
Console.ForegroundColor = ConsoleColor.Yellow;
int nextWord = PickNextWord(alreadyAsked);
Console.Write($"{i + 1}: {_vocabularyItems[nextWord].NativeWord, -10} = ");
Console.ResetColor();
string translationInput = Console.ReadLine()!;
alreadyAsked[nextWord] = true;
bool isTranslationCorrect = _vocabularyItems[nextWord].TestTranslation(translationInput);
if (isTranslationCorrect)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("OK!");
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"No, {_vocabularyItems[nextWord].NativeWord} = {_vocabularyItems[nextWord].Translation}");
return;
}
Console.ResetColor();
}
Console.WriteLine();
}
/// <summary>
/// Prints training statistics to the terminal.
/// </summary>
public void PrintStatistics()
{
Sort();
Console.Clear();
Console.ForegroundColor = ConsoleColor.DarkCyan;
Console.WriteLine(FiggleFonts.Standard.Render("Training Statistics"));
Console.WriteLine($"{"English",-10} {"German",-10} {"Asked",-5} {"Correct",-7}");
Console.WriteLine(new string('-', 35));
Console.ForegroundColor = ConsoleColor.DarkYellow;
foreach (var vocabularyItem in _vocabularyItems)
{
Console.WriteLine(vocabularyItem);
}
Console.WriteLine();
Console.ResetColor();
}
/// <summary>
/// Picks the next word by random.
/// If all words have already been used any one is chosen.
/// </summary>
/// <param name="alreadyAsked">An array of flags indicating which words have already been used</param>
/// <returns>Index of next word to use</returns>
private int PickNextWord(bool[] alreadyAsked)
{
//Instead of a while loop trying every combination, we randomize only between the useAble words
int useAbleWords = UseAbleWords(alreadyAsked, out int[] indexOfUseAbleWords);
int randomIndex = RandomProvider.Random.Next(0, useAbleWords - 1);
return indexOfUseAbleWords[randomIndex];
int UseAbleWords(bool[] alreadyAsked, out int[] indexOfUseAbleWords)
{
int count = CountUseAbleWords(alreadyAsked);
indexOfUseAbleWords = new int[count];
int j = 0;
for (int i = 0; i < alreadyAsked.Length; i++)
{
if (!alreadyAsked[i])
{
indexOfUseAbleWords[j] = i;
j++;
}
}
return count;
}
int CountUseAbleWords(bool[] alreadyAsked)
{
int count = 0;
for (int i = 0; i < alreadyAsked.Length; i++)
{
if (!alreadyAsked[i])
{
count++;
}
}
return count;
}
}
/// <summary>
/// Sorts the vocabulary items using the CompareTo method of the <see cref="VocabularyItem"/> class.
/// </summary>
private void Sort()
{
for (int i = 0; i < _vocabularyItems.Length - 1; i++)
{
int result = _vocabularyItems[i].CompareTo(_vocabularyItems[i + 1]);
if (result > 0)
{
Swap(i, i + 1);
}
}
void Swap(int i, int j)
{
(_vocabularyItems[i], _vocabularyItems[j]) = (_vocabularyItems[j], _vocabularyItems[i]);
}
}
/// <summary>
/// Creates a <see cref="VocabularyItem"/> array from the raw words.
/// </summary>
/// <param name="wordsAndTranslations">Raw vocabulary read from a file</param>
/// <returns>A <see cref="VocabularyItem"/> for each (valid) word</returns>
private static VocabularyItem[] CreateVocabularyItems(string[][] wordsAndTranslations)
{
VocabularyItem[] vocabularyItemsTemp = new VocabularyItem[wordsAndTranslations.Length];
bool[] definedItems = new bool[wordsAndTranslations.Length];
int usedItems = 0;
for (var i = 0; i < wordsAndTranslations.Length; i++)
{
if (wordsAndTranslations[i].Length == 2)
{
usedItems++;
definedItems[i] = true;
vocabularyItemsTemp[i] = new VocabularyItem(wordsAndTranslations[i][WordIdx], wordsAndTranslations[i][TranslationIdx]);
}
}
VocabularyItem[] vocabularyItems = new VocabularyItem[usedItems];
int j = 0;
for (int i = 0; i < wordsAndTranslations.Length; i++)
{
if (definedItems[i])
{
vocabularyItems[j] = vocabularyItemsTemp[i];
j++;
}
}
return vocabularyItems;
}
}