feat: operator corrections injected into similar questions
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package dev.marcospaulo.canalhandia;
|
||||
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Operator corrections, injected when a new question resembles one that was
|
||||
* answered wrongly before.
|
||||
*
|
||||
* <p>This is the cheap alternative to fine-tuning: a wrong answer becomes
|
||||
* context, so the same mistake stops recurring.
|
||||
*/
|
||||
final class Corrections {
|
||||
|
||||
record Entry(String question, String answer) {
|
||||
}
|
||||
|
||||
private final File file;
|
||||
private final List<Entry> entries = new ArrayList<>();
|
||||
|
||||
Corrections(File file) {
|
||||
this.file = file;
|
||||
load();
|
||||
}
|
||||
|
||||
void load() {
|
||||
entries.clear();
|
||||
if (!file.exists()) {
|
||||
return;
|
||||
}
|
||||
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file);
|
||||
for (String key : yaml.getKeys(false)) {
|
||||
String question = yaml.getString(key + ".pergunta");
|
||||
String answer = yaml.getString(key + ".resposta");
|
||||
if (question != null && answer != null) {
|
||||
entries.add(new Entry(question, answer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add(String question, String answer) {
|
||||
entries.add(new Entry(question, answer));
|
||||
YamlConfiguration yaml = new YamlConfiguration();
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
yaml.set("c" + i + ".pergunta", entries.get(i).question());
|
||||
yaml.set("c" + i + ".resposta", entries.get(i).answer());
|
||||
}
|
||||
try {
|
||||
yaml.save(file);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("não consegui gravar " + file, e);
|
||||
}
|
||||
}
|
||||
|
||||
List<Entry> all() {
|
||||
return List.copyOf(entries);
|
||||
}
|
||||
|
||||
/** Corrections sharing at least one significant word with the question. */
|
||||
static List<Entry> matching(List<Entry> all, String question) {
|
||||
Set<String> asked = significantWords(question);
|
||||
List<Entry> out = new ArrayList<>();
|
||||
for (Entry entry : all) {
|
||||
Set<String> known = significantWords(entry.question());
|
||||
known.retainAll(asked);
|
||||
if (known.size() >= 1) {
|
||||
out.add(entry);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static Set<String> significantWords(String text) {
|
||||
Set<String> words = new HashSet<>();
|
||||
for (String word : text.toLowerCase(Locale.ROOT).split("[^\\p{L}0-9]+")) {
|
||||
// Short words are almost all articles and prepositions in Portuguese.
|
||||
if (word.length() > 4) {
|
||||
words.add(word);
|
||||
}
|
||||
}
|
||||
return words;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package dev.marcospaulo.canalhandia;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.util.List;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class CorrectionsTest {
|
||||
|
||||
@Test
|
||||
void matchesOnSharedSignificantWords() {
|
||||
List<Corrections.Entry> all = List.of(
|
||||
new Corrections.Entry("como pegar um camelo",
|
||||
"camelos são mansos, basta pôr uma sela"));
|
||||
assertEquals(1, Corrections.matching(all, "como eu pego camelo?").size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ignoresUnrelatedCorrections() {
|
||||
List<Corrections.Entry> all = List.of(
|
||||
new Corrections.Entry("como pegar um camelo", "…"));
|
||||
assertTrue(Corrections.matching(all, "onde acho diamante?").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shortWordsDoNotCreateMatches() {
|
||||
List<Corrections.Entry> all = List.of(
|
||||
new Corrections.Entry("o que e um creeper", "explode"));
|
||||
assertTrue(Corrections.matching(all, "o que e um zumbi").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleSharedContentWordMatches() {
|
||||
// "camelo" is the only word > 4 chars shared with the conjugated
|
||||
// question; the threshold is one, not two, precisely for this case.
|
||||
List<Corrections.Entry> all = List.of(
|
||||
new Corrections.Entry("como pegar um camelo", "ponha uma sela"));
|
||||
assertEquals(1, Corrections.matching(all, "como eu pego camelo?").size());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user