From e058363a52ca03c392311031c0d9466f10d66563 Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Tue, 30 Apr 2024 17:40:49 +0200 Subject: [PATCH 1/3] add manual proxied game solving --- build.sh | 1 + src/lib/game/proxy.ts | 43 +++++++++++++++++++++++++++++++++++++++++++ src/lib/lib.ts | 1 + src/manual_proxy.ts | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 src/lib/game/proxy.ts create mode 100644 src/manual_proxy.ts diff --git a/build.sh b/build.sh index 57e17f1..52377e9 100755 --- a/build.sh +++ b/build.sh @@ -4,4 +4,5 @@ cd "$(dirname "$(realpath "$0")")" mkdir -p bin +deno compile -o bin/manual_proxy src/manual_proxy.ts deno compile -o bin/simulation src/simulation.ts diff --git a/src/lib/game/proxy.ts b/src/lib/game/proxy.ts new file mode 100644 index 0000000..1e55085 --- /dev/null +++ b/src/lib/game/proxy.ts @@ -0,0 +1,43 @@ +import { assertExists } from "https://deno.land/std@0.224.0/assert/assert_exists.ts"; + +import { Awaitable } from "../utils.ts"; +import { Gaming, GuessResult, Info } from "./game.ts"; + +export class ManualProxy implements Gaming { + word_length; + + constructor(length: number) { + this.word_length = length; + } + + guess(guess: string, _known: string): Awaitable { + console.log(" Guessing:", guess); + return read_until_correct(this.word_length); + } + + length(): number { + return this.word_length; + } +} + +function read_until_correct(length: number): GuessResult { + while (true) { + const input = prompt(" Result:"); + assertExists(input); + const informations = parse_input(input, length); + if (informations === null) continue; + if (informations.every((i) => i.kind === "there")) return { kind: "success" }; + return { kind: "failure", informations }; + } +} + +function parse_input(input: string, length: number) { + const parsed = [] as Info[]; + for (const character of input.trim()) { + if (character === "n") parsed.push({ kind: "abscent" }); + if (character === "i") parsed.push({ kind: "somewhere" }); + if (character === "y") parsed.push({ kind: "there" }); + } + if (parsed.length !== length) return null; + else return parsed; +} diff --git a/src/lib/lib.ts b/src/lib/lib.ts index 3ad2e8e..bd84cb1 100644 --- a/src/lib/lib.ts +++ b/src/lib/lib.ts @@ -1,4 +1,5 @@ export { Dict } from "./dict.ts"; export { Guesser } from "./guesser.ts"; export { Simulator } from "./game/simulator.ts"; +export { ManualProxy } from "./game/proxy.ts"; export { Runner } from "./runner.ts"; diff --git a/src/manual_proxy.ts b/src/manual_proxy.ts new file mode 100644 index 0000000..c0a80b7 --- /dev/null +++ b/src/manual_proxy.ts @@ -0,0 +1,38 @@ +#!/usr/bin/env -S deno run --allow-read + +import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.4/command/mod.ts"; + +import { Dict, Guesser, ManualProxy, Runner } from "./lib/lib.ts"; +import { VerboseLogging } from "./lib/runner.ts"; + +import { francais } from "../data/data.ts"; + +async function main() { + const args = await new Command().name("manual_proxy") + .description( + "Program to solve manually proxied TUSMO games with guesser controller.", + ) + .option( + "-f, --file ", + "Sets dictionnary to use words from (defaults to internal french dict).", + ).option( + "-n, --length ", + "Length of the word to use from the dictionnary.", + { default: 6 }, + ).option( + "-w, --wait ", + "Time to wait between guesses, in ms.", + { default: 0 }, + ).parse(Deno.args); + + let dict = Dict.from_lines(francais, args.options.length); + if (args.options.file !== undefined) dict = await Dict.from_text_file(args.options.file, args.options.length); + + const guesser = new Guesser(dict); + const game = new ManualProxy(args.options.length); + + const runner = new Runner(game, guesser, new VerboseLogging(), args.options.wait); + await runner.play_all(); +} + +if (import.meta.main) await main(); From 626e639f590c41c416762d6d10619a687ebf57f4 Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Tue, 30 Apr 2024 18:17:23 +0200 Subject: [PATCH 2/3] add manual proxy program --- src/lib/dict.ts | 7 +++++++ src/lib/game/proxy.ts | 15 +++++++++----- src/lib/guesser.ts | 9 +++++---- src/lib/prompt.ts | 46 +++++++++++++++++++++++++++++++++++++++++++ src/manual_proxy.ts | 20 ++++++++----------- 5 files changed, 76 insertions(+), 21 deletions(-) create mode 100644 src/lib/prompt.ts mode change 100644 => 100755 src/manual_proxy.ts diff --git a/src/lib/dict.ts b/src/lib/dict.ts index 50603a6..cb679c9 100644 --- a/src/lib/dict.ts +++ b/src/lib/dict.ts @@ -25,6 +25,13 @@ export class Dict { return Dict.from_lines(content.split("\n"), length); } + constraint(at: number, letter: string) { + const to_delete = new Set(); + for (const word of this.words.values()) if (word[at] !== letter) to_delete.add(word); + for (const item of to_delete) this.words.delete(item); + return to_delete.size; + } + [Symbol.for("Deno.customInspect")]() { return `Dict { ${this.words.size} words, ${this.letters.size} letters }`; } diff --git a/src/lib/game/proxy.ts b/src/lib/game/proxy.ts index 1e55085..ced5f21 100644 --- a/src/lib/game/proxy.ts +++ b/src/lib/game/proxy.ts @@ -12,7 +12,9 @@ export class ManualProxy implements Gaming { guess(guess: string, _known: string): Awaitable { console.log(" Guessing:", guess); - return read_until_correct(this.word_length); + const result = read_until_correct(this.word_length); + console.log(); + return result; } length(): number { @@ -25,7 +27,10 @@ function read_until_correct(length: number): GuessResult { const input = prompt(" Result:"); assertExists(input); const informations = parse_input(input, length); - if (informations === null) continue; + if (informations === null) { + console.log(" incorrect input, try again"); + continue; + } if (informations.every((i) => i.kind === "there")) return { kind: "success" }; return { kind: "failure", informations }; } @@ -34,9 +39,9 @@ function read_until_correct(length: number): GuessResult { function parse_input(input: string, length: number) { const parsed = [] as Info[]; for (const character of input.trim()) { - if (character === "n") parsed.push({ kind: "abscent" }); - if (character === "i") parsed.push({ kind: "somewhere" }); - if (character === "y") parsed.push({ kind: "there" }); + if (character === ".") parsed.push({ kind: "abscent" }); + if (character === "-") parsed.push({ kind: "somewhere" }); + if (character === "+") parsed.push({ kind: "there" }); } if (parsed.length !== length) return null; else return parsed; diff --git a/src/lib/guesser.ts b/src/lib/guesser.ts index c0afca6..841dc88 100644 --- a/src/lib/guesser.ts +++ b/src/lib/guesser.ts @@ -53,13 +53,13 @@ export class Guesser implements Guessing { return null; } - learn_does_not_exist(letter: string) { + public learn_does_not_exist(letter: string) { const letter_info = this.informations.get(letter); assertExists(letter_info); letter_info.exists = false; } - learn_does_exist(letter: string) { + public learn_does_exist(letter: string) { const letter_info = this.informations.get(letter); assertExists(letter_info); letter_info.exists = true; @@ -68,7 +68,8 @@ export class Guesser implements Guessing { for (const info of this.informations.values()) if (info.exists === "unknown") info.exists = false; } - learn_letter_at(letter: string, at: number) { + public learn_letter_at(letter: string, at: number) { + this.learn_does_exist(letter); const letter_info = this.informations.get(letter); assertExists(letter_info); letter_info.at.add(at); @@ -78,7 +79,7 @@ export class Guesser implements Guessing { } } - learn_letter_not_at(letter: string, at: number) { + public learn_letter_not_at(letter: string, at: number) { const letter_info = this.informations.get(letter); assertExists(letter_info); letter_info.not_at.add(at); diff --git a/src/lib/prompt.ts b/src/lib/prompt.ts new file mode 100644 index 0000000..01f42a8 --- /dev/null +++ b/src/lib/prompt.ts @@ -0,0 +1,46 @@ +import { assertExists } from "https://deno.land/std@0.224.0/assert/assert_exists.ts"; + +import { enumerate } from "./utils.ts"; + +export function initialize_prompt() { + console.log("Please input initial state of the game. Format is :"); + console.log(" letter [a-z] known letter"); + console.log(" dot . unknown letter"); + console.log("example : .rb.."); + console.log(""); + const inputted = parse_initialization_until_correct(); + console.log(""); + console.log("From now on, please try the following guesses and report results. Format is :"); + console.log(" plus + correct"); + console.log(" minus - wrong placement"); + console.log(" dot . incorrect"); + console.log("example : -++.."); + console.log(); + return inputted; +} + +function parse_initialization_until_correct() { + let input = prompt("initial :"); + while (true) { + assertExists(input); + const parsed = parse_initialization(input); + if (parsed === null) input = prompt("Invalid, please try again :"); + else return parsed; + } +} + +function parse_initialization(input: string) { + const code_a = "a".charCodeAt(0); + const code_z = "z".charCodeAt(0); + let length = 0; + const constraints = [] as [number, string][]; + for (const [index, char] of enumerate(input.trim().toLowerCase())) { + length += 1; + if (char === ".") continue; + const code = char.charCodeAt(0); + if (code < code_a) return null; + if (code > code_z) return null; + constraints.push([index, char]); + } + return { length, constraints }; +} diff --git a/src/manual_proxy.ts b/src/manual_proxy.ts old mode 100644 new mode 100755 index c0a80b7..4ffaf87 --- a/src/manual_proxy.ts +++ b/src/manual_proxy.ts @@ -3,6 +3,7 @@ import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.4/command/mod.ts"; import { Dict, Guesser, ManualProxy, Runner } from "./lib/lib.ts"; +import { initialize_prompt } from "./lib/prompt.ts"; import { VerboseLogging } from "./lib/runner.ts"; import { francais } from "../data/data.ts"; @@ -15,23 +16,18 @@ async function main() { .option( "-f, --file ", "Sets dictionnary to use words from (defaults to internal french dict).", - ).option( - "-n, --length ", - "Length of the word to use from the dictionnary.", - { default: 6 }, - ).option( - "-w, --wait ", - "Time to wait between guesses, in ms.", - { default: 0 }, ).parse(Deno.args); - let dict = Dict.from_lines(francais, args.options.length); - if (args.options.file !== undefined) dict = await Dict.from_text_file(args.options.file, args.options.length); + const init = initialize_prompt(); + + let dict = Dict.from_lines(francais, init.length); + if (args.options.file !== undefined) dict = await Dict.from_text_file(args.options.file, init.length); + for (const [index, letter] of init.constraints) dict.constraint(index, letter); const guesser = new Guesser(dict); - const game = new ManualProxy(args.options.length); + const game = new ManualProxy(init.length); - const runner = new Runner(game, guesser, new VerboseLogging(), args.options.wait); + const runner = new Runner(game, guesser, new VerboseLogging()); await runner.play_all(); } From e2e2852739e9882519e52949ca9df2b655493814 Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Tue, 30 Apr 2024 18:21:22 +0200 Subject: [PATCH 3/3] update readme --- README.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/README.md b/README.md index c79560d..f3c80b6 100644 --- a/README.md +++ b/README.md @@ -28,3 +28,67 @@ Programme pour résolution de Tusmo. # -n, --length - Length of the word to use from the dictionnary. (Default: 6) # -w, --wait - Time to wait between guesses, in ms. (Default: 0) ``` + +### Proxy + +```sh +./src/manual_proxy.ts +# Please input initial state of the game. Format is : +# letter [a-z] known letter +# dot . unknown letter +# example : .rb.. + +# initial : a.... + +# From now on, please try the following guesses and report results. Format is : +# plus + correct +# minus - wrong placement +# dot . incorrect +# example : -++.. + +# Guessing: aires +# Result: +.--. + +# Knows: ..... +# Guessing: aires +# Got: #.**. + +# Guessing: acere +# Result: +..++ + +# Knows: a.... +# Guessing: acere +# Got: #..## + +# Guessing: arabe +# Result: ++.-+ + +# Knows: a..re +# Guessing: arabe +# Got: ##.*# + +# Guessing: aveux +# Result: +.-.. + +# Knows: ar.re +# Guessing: aveux +# Got: #.*.. + +# Guessing: album +# Result: +.+.. + +# Knows: ar.re +# Guessing: album +# Got: #.#.. + +# Guessing: arbre +# Result: +++++ + +# Knows: arbre +# Guessing: arbre +# Got: success + +# Stopped on arbre . +# With success . +# In 6 steps +```