Compare commits
3 commits
f7ebdbf9b3
...
e2e2852739
Author | SHA1 | Date | |
---|---|---|---|
e2e2852739 | |||
626e639f59 | |||
e058363a52 |
8 changed files with 206 additions and 4 deletions
64
README.md
64
README.md
|
@ -28,3 +28,67 @@ Programme pour résolution de Tusmo.
|
||||||
# -n, --length <length> - Length of the word to use from the dictionnary. (Default: 6)
|
# -n, --length <length> - Length of the word to use from the dictionnary. (Default: 6)
|
||||||
# -w, --wait <wait> - Time to wait between guesses, in ms. (Default: 0)
|
# -w, --wait <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 : -++..
|
||||||
|
|
||||||
|
# <proxy> Guessing: aires
|
||||||
|
# <proxy> Result: +.--.
|
||||||
|
|
||||||
|
# Knows: .....
|
||||||
|
# Guessing: aires
|
||||||
|
# Got: #.**.
|
||||||
|
|
||||||
|
# <proxy> Guessing: acere
|
||||||
|
# <proxy> Result: +..++
|
||||||
|
|
||||||
|
# Knows: a....
|
||||||
|
# Guessing: acere
|
||||||
|
# Got: #..##
|
||||||
|
|
||||||
|
# <proxy> Guessing: arabe
|
||||||
|
# <proxy> Result: ++.-+
|
||||||
|
|
||||||
|
# Knows: a..re
|
||||||
|
# Guessing: arabe
|
||||||
|
# Got: ##.*#
|
||||||
|
|
||||||
|
# <proxy> Guessing: aveux
|
||||||
|
# <proxy> Result: +.-..
|
||||||
|
|
||||||
|
# Knows: ar.re
|
||||||
|
# Guessing: aveux
|
||||||
|
# Got: #.*..
|
||||||
|
|
||||||
|
# <proxy> Guessing: album
|
||||||
|
# <proxy> Result: +.+..
|
||||||
|
|
||||||
|
# Knows: ar.re
|
||||||
|
# Guessing: album
|
||||||
|
# Got: #.#..
|
||||||
|
|
||||||
|
# <proxy> Guessing: arbre
|
||||||
|
# <proxy> Result: +++++
|
||||||
|
|
||||||
|
# Knows: arbre
|
||||||
|
# Guessing: arbre
|
||||||
|
# Got: success
|
||||||
|
|
||||||
|
# Stopped on arbre .
|
||||||
|
# With success .
|
||||||
|
# In 6 steps
|
||||||
|
```
|
||||||
|
|
1
build.sh
1
build.sh
|
@ -4,4 +4,5 @@ cd "$(dirname "$(realpath "$0")")"
|
||||||
|
|
||||||
mkdir -p bin
|
mkdir -p bin
|
||||||
|
|
||||||
|
deno compile -o bin/manual_proxy src/manual_proxy.ts
|
||||||
deno compile -o bin/simulation src/simulation.ts
|
deno compile -o bin/simulation src/simulation.ts
|
||||||
|
|
|
@ -25,6 +25,13 @@ export class Dict {
|
||||||
return Dict.from_lines(content.split("\n"), length);
|
return Dict.from_lines(content.split("\n"), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constraint(at: number, letter: string) {
|
||||||
|
const to_delete = new Set<string>();
|
||||||
|
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")]() {
|
[Symbol.for("Deno.customInspect")]() {
|
||||||
return `Dict { ${this.words.size} words, ${this.letters.size} letters }`;
|
return `Dict { ${this.words.size} words, ${this.letters.size} letters }`;
|
||||||
}
|
}
|
||||||
|
|
48
src/lib/game/proxy.ts
Normal file
48
src/lib/game/proxy.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
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<GuessResult> {
|
||||||
|
console.log("<proxy> Guessing:", guess);
|
||||||
|
const result = read_until_correct(this.word_length);
|
||||||
|
console.log();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
length(): number {
|
||||||
|
return this.word_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_until_correct(length: number): GuessResult {
|
||||||
|
while (true) {
|
||||||
|
const input = prompt("<proxy> Result:");
|
||||||
|
assertExists(input);
|
||||||
|
const informations = parse_input(input, length);
|
||||||
|
if (informations === null) {
|
||||||
|
console.log("<proxy> incorrect input, try again");
|
||||||
|
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 === ".") 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;
|
||||||
|
}
|
|
@ -53,13 +53,13 @@ export class Guesser implements Guessing {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
learn_does_not_exist(letter: string) {
|
public learn_does_not_exist(letter: string) {
|
||||||
const letter_info = this.informations.get(letter);
|
const letter_info = this.informations.get(letter);
|
||||||
assertExists(letter_info);
|
assertExists(letter_info);
|
||||||
letter_info.exists = false;
|
letter_info.exists = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
learn_does_exist(letter: string) {
|
public learn_does_exist(letter: string) {
|
||||||
const letter_info = this.informations.get(letter);
|
const letter_info = this.informations.get(letter);
|
||||||
assertExists(letter_info);
|
assertExists(letter_info);
|
||||||
letter_info.exists = true;
|
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;
|
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);
|
const letter_info = this.informations.get(letter);
|
||||||
assertExists(letter_info);
|
assertExists(letter_info);
|
||||||
letter_info.at.add(at);
|
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);
|
const letter_info = this.informations.get(letter);
|
||||||
assertExists(letter_info);
|
assertExists(letter_info);
|
||||||
letter_info.not_at.add(at);
|
letter_info.not_at.add(at);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export { Dict } from "./dict.ts";
|
export { Dict } from "./dict.ts";
|
||||||
export { Guesser } from "./guesser.ts";
|
export { Guesser } from "./guesser.ts";
|
||||||
export { Simulator } from "./game/simulator.ts";
|
export { Simulator } from "./game/simulator.ts";
|
||||||
|
export { ManualProxy } from "./game/proxy.ts";
|
||||||
export { Runner } from "./runner.ts";
|
export { Runner } from "./runner.ts";
|
||||||
|
|
46
src/lib/prompt.ts
Normal file
46
src/lib/prompt.ts
Normal file
|
@ -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 };
|
||||||
|
}
|
34
src/manual_proxy.ts
Executable file
34
src/manual_proxy.ts
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
#!/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 { initialize_prompt } from "./lib/prompt.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 <path:string>",
|
||||||
|
"Sets dictionnary to use words from (defaults to internal french dict).",
|
||||||
|
).parse(Deno.args);
|
||||||
|
|
||||||
|
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(init.length);
|
||||||
|
|
||||||
|
const runner = new Runner(game, guesser, new VerboseLogging());
|
||||||
|
await runner.play_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.meta.main) await main();
|
Loading…
Add table
Add a link
Reference in a new issue