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 { 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"); } }