124 lines
3.6 KiB
TypeScript
124 lines
3.6 KiB
TypeScript
import { assertExists } from "https://deno.land/std@0.224.0/assert/assert_exists.ts";
|
|
|
|
import { Guessing } from "./guesser/guesser.ts";
|
|
import { Gaming, GuessResult } from "./game/game.ts";
|
|
import { last, wait, zip } from "./utils.ts";
|
|
|
|
export class Runner<Ga extends Gaming, Gu extends Guessing> {
|
|
game;
|
|
guesser;
|
|
delay_ms;
|
|
turns;
|
|
logging;
|
|
max;
|
|
|
|
constructor(game: Ga, guesser: Gu, logging: LoggingStrategy, delay_ms = 0, max: number | undefined = undefined) {
|
|
this.game = game;
|
|
this.guesser = guesser;
|
|
this.delay_ms = delay_ms;
|
|
this.turns = [] as Turn[];
|
|
this.logging = logging;
|
|
this.max = max;
|
|
}
|
|
|
|
async play_all() {
|
|
this.logging.on_start(this.game.length(), ...this.guesser.declare_properties());
|
|
while (true) {
|
|
const result = await this.guesser.guess(async (guess, ...properties) => {
|
|
const result = await this.game.guess(guess);
|
|
this.turns.push({ guess, result });
|
|
this.logging.on_guess(guess, result, ...properties);
|
|
return result;
|
|
});
|
|
if (result !== null) break;
|
|
if (this.max !== undefined) if (this.turns.length >= this.max) return this.turns;
|
|
await wait(this.delay_ms);
|
|
}
|
|
this.logging.on_finish(this.turns);
|
|
return this.turns;
|
|
}
|
|
}
|
|
|
|
type Turn = { guess: string; result: GuessResult };
|
|
|
|
function format_result(result: GuessResult) {
|
|
if (result.kind === "success") return "success";
|
|
let line = "";
|
|
for (const info of result.informations) {
|
|
if (info.kind === "abscent") line += ".";
|
|
if (info.kind === "somewhere") line += "*";
|
|
if (info.kind === "there") line += "#";
|
|
}
|
|
return line;
|
|
}
|
|
|
|
export interface LoggingStrategy {
|
|
on_start(length: number, ...properties: string[]): void;
|
|
on_guess(guess: string, result: GuessResult, ...properties: unknown[]): void;
|
|
on_finish(turns: Turn[]): void;
|
|
}
|
|
|
|
export class VerboseLogging implements LoggingStrategy {
|
|
properties;
|
|
length;
|
|
|
|
constructor() {
|
|
this.length = 0;
|
|
this.properties = [] as string[];
|
|
}
|
|
|
|
on_start(_length: number, ...properties: string[]) {
|
|
this.length = Math.max(8, ...properties.map((p) => p.length));
|
|
this.properties = properties;
|
|
}
|
|
|
|
on_guess(guess: string, result: GuessResult, ...properties: unknown[]): void {
|
|
for (const [name, value] of zip(this.properties, properties)) this.log_entry(name, value);
|
|
this.log_entry("Guessing", guess);
|
|
this.log_entry("Got", format_result(result));
|
|
console.log();
|
|
}
|
|
|
|
on_finish(turns: Turn[]): void {
|
|
const last_ = last(turns);
|
|
assertExists(last_);
|
|
console.log("Stopped on", last_.guess, ".");
|
|
console.log("With", last_.result.kind, ".");
|
|
console.log("In", turns.length, "steps");
|
|
}
|
|
|
|
log_entry(name: string, value: unknown) {
|
|
console.log(name.padStart(this.length) + ": " + value);
|
|
}
|
|
}
|
|
|
|
export class TableLogging implements LoggingStrategy {
|
|
columns;
|
|
|
|
constructor() {
|
|
this.columns = [] as (readonly [string, number])[];
|
|
}
|
|
|
|
on_start(length: number, ...properties: string[]): void {
|
|
this.columns = properties.map((p) => [p, p.length] as const);
|
|
this.columns.splice(0, 0, ["guess", length], ["result", Math.max(7, length)]);
|
|
let line = "";
|
|
for (const [name, width] of this.columns) line += name.padStart(width) + " ";
|
|
console.log(line);
|
|
console.log();
|
|
}
|
|
|
|
on_guess(guess: string, result: GuessResult, ...properties: unknown[]): void {
|
|
const properties_ = [guess, format_result(result), ...properties];
|
|
let line = "";
|
|
for (const [[_, width], value] of zip(this.columns, properties_)) line += `${value}`.padStart(width) + " ";
|
|
console.log(line);
|
|
}
|
|
|
|
on_finish(turns: Turn[]): void {
|
|
const last_ = last(turns);
|
|
assertExists(last_);
|
|
console.log();
|
|
console.log(last_.result.kind, "in", turns.length, "steps");
|
|
}
|
|
}
|