start implementing reducing guesser
This commit is contained in:
parent
d0de8f867f
commit
756c989eab
3 changed files with 84 additions and 0 deletions
|
@ -35,6 +35,11 @@ export class Dict {
|
||||||
[Symbol.for("Deno.customInspect")]() {
|
[Symbol.for("Deno.customInspect")]() {
|
||||||
return `Dict { ${this.words.size} words, ${this.letters.size} letters }`;
|
return `Dict { ${this.words.size} words, ${this.letters.size} letters }`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
const words = new Set(this.words.values());
|
||||||
|
return new Dict(words, this.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function contains_any(text: string, words: string[]) {
|
export function contains_any(text: string, words: string[]) {
|
||||||
|
|
77
src/lib/guesser/reducing.ts
Normal file
77
src/lib/guesser/reducing.ts
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import { Dict } from "../dict.ts";
|
||||||
|
import { GuessResult, Info } from "../game/game.ts";
|
||||||
|
import { Value } from "../utils.ts";
|
||||||
|
import { Awaitable, enumerate, range, zip } from "../utils.ts";
|
||||||
|
import { Guessing } from "./guesser.ts";
|
||||||
|
|
||||||
|
export class ReducingGuesser implements Guessing {
|
||||||
|
length;
|
||||||
|
words;
|
||||||
|
possibilities;
|
||||||
|
|
||||||
|
public constructor(words: Dict) {
|
||||||
|
this.words = words;
|
||||||
|
this.length = words.length;
|
||||||
|
this.possibilities = words.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async guess(try_: (guess: string, known: string) => Awaitable<GuessResult>) {
|
||||||
|
const guess = this.make_guess();
|
||||||
|
const result = await try_(guess, "");
|
||||||
|
if (result.kind === "success") return result;
|
||||||
|
this.learn(guess, result.informations);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
make_guess() {
|
||||||
|
const letters = [...this.words.letters];
|
||||||
|
const letters_ranks = [...range(0, this.length)].map(() => new Map(letters.map((l) => [l, { value: 0 }])));
|
||||||
|
for (const word of this.possibilities.words) {
|
||||||
|
for (const [index, letter] of enumerate(word)) {
|
||||||
|
letters_ranks[index].get(letter)!.value += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const candidates = [...this.words.words];
|
||||||
|
const scored = candidates.map((word) => [word, score_word_from_ranks(word, letters_ranks)] as const);
|
||||||
|
const [best] = scored.reduce((a, b) => a[1] > b[1] ? a : b);
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
learn(word: string, infos: Info[]) {
|
||||||
|
const to_delete = new Set<string>();
|
||||||
|
const pos = this.possibilities;
|
||||||
|
for (const [index, [letter, info]] of enumerate(zip(word, infos))) {
|
||||||
|
if (info.kind === "there") for (const word of pos.words) if (word[index] !== letter) to_delete.add(word);
|
||||||
|
if (info.kind === "somewhere") for (const word of pos.words) if (word[index] === letter) to_delete.add(word);
|
||||||
|
if (info.kind === "abscent") for (const word of pos.words) if (word.includes(letter)) to_delete.add(word);
|
||||||
|
}
|
||||||
|
for (const d of to_delete) pos.words.delete(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note : does not take into account knowledge we already have.
|
||||||
|
function score_word_from_ranks(word: string, ranks: Map<string, Value<number>>[]) {
|
||||||
|
let result = 0;
|
||||||
|
for (const [index, letter] of enumerate(word)) {
|
||||||
|
// note : bonus for ANY letter.
|
||||||
|
for (const index_ranks of ranks) {
|
||||||
|
const letter_rank = index_ranks.get(letter)!.value;
|
||||||
|
result += letter_rank;
|
||||||
|
}
|
||||||
|
// note : bonus for THIS letter.
|
||||||
|
const index_ranks = ranks[index];
|
||||||
|
const letter_rank = index_ranks.get(letter)!.value;
|
||||||
|
result += letter_rank;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
note : The algorithm must proceed as follow :
|
||||||
|
|
||||||
|
1. establish a list of possible words
|
||||||
|
2. loop :
|
||||||
|
2.1 for each word, for each letter, estimate by how much this letter cuts possibilities for all three possible outcomes.
|
||||||
|
(2.1-bis weigts those scores by the frequency of the word ?)
|
||||||
|
2.2 play the word which individual letters cuts most of the possible space.
|
||||||
|
*/
|
|
@ -30,3 +30,5 @@ export function last<T>(iterable: Iterable<T>) {
|
||||||
for (const item of iterable) last = item;
|
for (const item of iterable) last = item;
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Value<T> = { value: T };
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue