wordulator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LISCENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright © 2026 Arlo Filley
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/ReadMe.md ADDED
@@ -0,0 +1,95 @@
1
+ # WORDULATOR
2
+ ## An Automated Entropy Based Wordle Guessing Bot
3
+
4
+ **Do you average 6 guesses at Wordle like I do?**
5
+ **If so then this is the repository you've been looking for!**
6
+
7
+ This is a Node.js implementation of a Wordle guessing bot. Uses entropy and several
8
+ heuristics to average 3.83 guesses per answer with 99% accuracy! *Tested over
9
+ 100 different cases, accuracy is defined as getting the answer within the 6 allowed
10
+ guesses*
11
+
12
+ This project was built over the course of 4 days so don't expect it to be fast
13
+ or optimal. It was however an interesting dive into entropy, bitwise optimisation,
14
+ and implementing the Wordle ruleset!
15
+
16
+ ## Installation
17
+ - Clone repo
18
+ - Navigate to `./` in terminal
19
+ - Run `npm run install-wordulator` - and wait to finish
20
+ - Installs base Wordle solutions and guesses
21
+ - Precomputes feedback matrix - This step might take a while
22
+ - Creates 5000 benchmark test cases from the solution list
23
+ - All Done
24
+
25
+ ### Usage
26
+ - Run in **user** mode using `node .`
27
+ - Run a standard **benchmark** using `npm run benchmark`
28
+ - Run a custom **benchmark** using `node . combo bench (# of tests)`
29
+ - Enjoy
30
+
31
+ ### Example Usage
32
+ ![](./docs/Example%20Usage.png)
33
+
34
+ ### Example Benchmark
35
+ ![](./docs/Example%20Benchmark.png)
36
+
37
+ ## Features
38
+ - Implements full Wordle ruleset - with automated feedback for efficient benchmarking
39
+ - Entropy guess scoring with several adjusting heuristics including
40
+ - Positional letter frequencies
41
+ - Non-overlapping letters
42
+ - Guess being a possible remaining answer
43
+ - Capable of using most\* 5 letter word lists (\*with A-Z charset)
44
+ - Precomputation of feedback matrix
45
+ - Memory mapping of feedback matrix allowing parallel program execution
46
+ - Automated benchmarking for running up to thousands of tests
47
+
48
+ ## Known Issues
49
+ - Feedback matrix consumes significant disk and memory space
50
+ - Limited to 5-letter Wordle variants
51
+ - Missing many possible runtime performance optimisations
52
+ - Assumes only ASCII characters a-z in wordlist
53
+
54
+ ## Ideas for Future Improvement
55
+ - Weighting heuristics scores based on possible words left
56
+ - Precomputed second guesses
57
+ - Massive Performance optimisations
58
+ - User-friendly web interface
59
+ - Rewrite in rust?
60
+
61
+ # How it Works
62
+ ## Feedback Modeling
63
+ This program models Wordle feedback per letter as green, yellow or grey and encodes
64
+ that into an 8 bit integer, using ~2 bits per letter position. Because Wordle has
65
+ 5 positions which each have 3 possibilities this means there are 243 possible feedbacks.
66
+ Each guess and answer pair is mapped into a deterministic feedback pattern represented
67
+ by the feedback matrix. The feedback matrix is precomputed before running the problem
68
+ as it significantly speeds up runtime performance at the cost of greater disk and
69
+ memory usage. The matrix allows simulating potential outcomes from each guess-answer
70
+ pair to model expected information gain
71
+
72
+ ## Scoring
73
+ Each possible guess in the total word list is evaluate based on its entropy. This
74
+ entropy in turn is based on simulating how the remaining possible answers would
75
+ be partitioned be a given feedback. Guesses with higher entropy eliminate more potential
76
+ answers than guesses with lower entropy\* (\*In the vast majority of cases). Each
77
+ score is then adjusted based on the positional frequency of each letter in the guess
78
+ based on the positional frequencies of remaining possible answers. The score is
79
+ also adjusted based on whether letters in the guess have already been seen before
80
+ in a previous guess. And finally the score is higher for guesses that are also contained
81
+ in the possible answer set. These heuristics are great for picking more optimal
82
+ guesses that still have high entropy.
83
+
84
+ ## Solving
85
+ To solve a given Wordle problem, the program:
86
+ 1. Makes a first predetermined guess (currently 'dares')
87
+ 2. Receives user or simulated feedback
88
+ 3. Create conditions the solution must fulfil
89
+ 4. Filters possible answers based on currently known solution conditions
90
+ 5. Makes another guess
91
+
92
+ This loop repeats steps 2-5 until:
93
+ - a) There is only one possible answer, the solution
94
+ - b) The program has used up its 6 guesses
95
+ - c) There is no possible answer that meets the solution conditions
Binary file
Binary file
package/main.js ADDED
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+
3
+ let { solve : combinedSolver } = require('./src/solvers/combined.js')
4
+ let { randomInt } = require('./src/lib/lib.js')
5
+
6
+ let words = require('./data/filter/words.json');
7
+ let test_data = require('./data/test/tests.json')
8
+
9
+ main();
10
+ async function main() {
11
+ try {
12
+ const args = process.argv.slice(2);
13
+ const type = typeof(args[0]) === 'string' ? args[0] : 'user';
14
+ const num = args[1] > 0 ? Number.parseInt(args[1]) : 100;
15
+
16
+ let solve = combinedSolver;
17
+
18
+ switch (type) {
19
+ case 'bench' : await benchmark(solve, num, test_data, console.log); break;
20
+ case 'benchmark' : await benchmark(solve, num, test_data, console.log); break; break;
21
+ case 'user' : await solve({ type: "user", rand: true, log: console.log }); break;
22
+ default: throw `Invalid Mode Selected ${type}`
23
+ }
24
+
25
+ process.exit(0);
26
+ } catch (err) {
27
+ console.error(err);
28
+ process.exit(1)
29
+ }
30
+ }
31
+
32
+ /**
33
+ *
34
+ * @param {function} solve
35
+ * @param {num} benchmark_num
36
+ * @param {object} weights
37
+ * @param {string[]} tests
38
+ * @param {function} log
39
+ */
40
+ async function benchmark(solve, benchmark_num = 100, tests, log) {
41
+ const results = [];
42
+ let total_guesses = 0;
43
+ let correct = 0;
44
+ let no_of_guesses = [0, 0, 0, 0, 0, 0];
45
+ let avg = 0;
46
+
47
+ for (let i=0; i < benchmark_num; i++) {
48
+ let ans = words[randomInt(words.length)];
49
+ let result;
50
+
51
+ if (tests !== undefined) ans = tests[i];
52
+ result = await solve({ type: 'benchmark', answer: ans, log: () => {} });
53
+
54
+ if (result.solved === true) {
55
+ total_guesses += result.guesses;
56
+ correct++;
57
+ no_of_guesses[result.guesses-1]++;
58
+ avg = total_guesses/(i+1);
59
+ } else {
60
+ avg = total_guesses/(i+1);
61
+ total_guesses += 8;
62
+ }
63
+
64
+ if (/*i % 20 === 0 &&*/ result.solved) {
65
+ log(`Solved ${correct}/${i+1} | guesses: ${result.guesses} | accuracy ${(correct / (i+1) * 100).toFixed(1)} | running avg: ${avg.toFixed(2)}`);
66
+ } else /*if (i % 20 === 0)*/ {
67
+ log(`Solved ${correct}/${i+1} | guesses: >6 | accuracy ${(correct / (i+1) * 100).toFixed(1)} | running avg: ${avg.toFixed(2)}`);
68
+ }
69
+
70
+ results.push(result);
71
+ }
72
+
73
+ log(`Correct - ${correct}/${benchmark_num} - ${correct / benchmark_num * 100}%`);
74
+ log(`Avg Guess - ${avg.toFixed(3)}`);
75
+ log('Guess Breakdown:')
76
+ for (let i=0; i<no_of_guesses.length; i++) {
77
+ if (i === 0) {
78
+ log(`\t${i+1} guess - ${no_of_guesses[0]}`);
79
+ } else {
80
+ log(`\t${i+1} guesses - ${no_of_guesses[i]}`);
81
+ }
82
+ }
83
+
84
+ }
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "wordulator",
3
+ "version": "1.0.0",
4
+ "description": "An Entropy Based Wordle Solver",
5
+ "keywords": ["wordle", "entropy"],
6
+ "license": "MIT",
7
+ "author": "Arlo Filley",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/ArloFilley/Wordulator"
11
+ },
12
+ "type": "commonjs",
13
+ "main": "main.js",
14
+ "bin": {
15
+ "wordulator": "node ./main.js"
16
+ },
17
+ "scripts": {
18
+ "test": "echo \"What the hell is a test???? YALL TESTING UR CODE??? GETTOUTAHEER!!!\" && exit 1",
19
+ "install-wordulator": "chmod u+x run/install.bash && run/install.bash && chmod u+x run/benchmark.bash",
20
+ "benchmark": "run/benchmark.bash",
21
+ "bench": "run/benchmark.bash"
22
+ },
23
+ "dependencies": {
24
+ "@luminati-io/mmap-io": "^1.1.7-lum.6",
25
+ "i": "^0.3.7",
26
+ "npm": "^11.8.0"
27
+ }
28
+ }
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+
3
+ echo "Running benchmark, results saved to ./bench/benchmark.txt"
4
+ time node . bench 100 | tee "./bench/benchmark.txt"
@@ -0,0 +1,61 @@
1
+ #!/bin/bash
2
+ echo "installing node modules"
3
+ npm i
4
+ wait
5
+ sleep 5
6
+
7
+ echo "Creating Directory Structure"
8
+ sleep 0.2
9
+ echo "Created ./data"
10
+ sleep 0.2
11
+ mkdir data
12
+ echo "Created ./data/filter"
13
+ mkdir data/filter
14
+ sleep 0.2
15
+ echo "Created ./data/proc"
16
+ mkdir data/proc
17
+ sleep 0.2
18
+ echo "Created ./data/raw"
19
+ mkdir data/raw
20
+ sleep 0.2
21
+ echo "Created ./data/test"
22
+ mkdir data/test
23
+ sleep 0.2
24
+ echo "Created ./bench"
25
+ mkdir bench
26
+
27
+ wait
28
+ sleep 5
29
+
30
+ echo "Copying Files Into Correct Places"
31
+ cp run/wordle.txt data/raw/wordle.txt
32
+ sleep 0.2
33
+ echo "Copied ./run/wordle.txt -> ./data/raw/wordle.txt"
34
+ cp run/solutions.txt data/raw/solutions.txt
35
+ sleep 0.2
36
+ echo "Copied ./run/solutions.txt -> ./data/raw/solutions.txt"
37
+
38
+ wait
39
+ sleep 1
40
+
41
+ echo "Filtering List For Valid Words"
42
+ node src/pre/filter_words.js data/raw/wordle.txt data/filter/words.json 5
43
+ node src/pre/filter_words.js data/raw/solutions.txt data/filter/solutions.json 5
44
+
45
+ wait
46
+ sleep 1
47
+
48
+ echo "Generating Feedback Matrix"
49
+ echo "This Step Might Take a While"
50
+ echo "The Feedback Matrix Can Be Found at data/proc/feedback_matrix.bin It Takes Up Roughly 200mb For 12k Words"
51
+ node ./src/pre/feedback_matrix.js data/filter/words.json data/proc/feedback_matrix.bin
52
+
53
+ wait
54
+ sleep 1
55
+
56
+ echo "Generating Benchmark Tests"
57
+ node src/pre/test.js data/filter/solutions.json data/test/tests.json 5000
58
+
59
+ wait
60
+ sleep 1
61
+ echo "Everything installed correctly :>"