What If I Told You I Created a Mastermind?
TheoForger
Posted on September 21, 2024
Once upon a time, there was a party game enjoyer. He was invited to this new game called Codenames. He happily joined the board, hoping to spin some wheels and roll some dice, like any other games he plays. To his surprise, he was tasked to be a "spymaster" and this is the first thing he saw:
He scanned the board and searched his brain for that one pop culture reference that barely connects two words together. Unfortunately, he hadn't any clue. His friends were eagerly waiting for his move. What is he going to do?
Mastermind!
Mastermind is a CLI tool designed to generate clue words for spymasters in the game of Codenames, leveraging large language models (LLMs) of your choice! Because what better ways to use a language model by asking them to link words?
This project supports any OpenAI compatible API. Written in Rust, to avoid the inevitable destiny of being rewritten in Rust.
How does it work?
Very straightforward! You just need two text files. One for the words with your team (to link), one for everything else (to avoid).
Something like:
link.txt | avoid.txt |
---|---|
Then, simply run
mastermind link.txt avoid.txt
and you'll see the clues!
╭───────────┬───────┬─────────────────────╮
│ Clue ┆ Count ┆ Linked Words │
╞═══════════╪═══════╪═════════════════════╡
│ water ┆ 2 ┆ sound, scuba diver │
├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ parking ┆ 2 ┆ park, penny │
├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ circus ┆ 2 ┆ bond, bee │
├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ city ┆ 2 ┆ park, tokyo │
├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ music ┆ 2 ┆ sound, penny │
├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ diving ┆ 2 ┆ scuba diver, walrus │
├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ city life ┆ 2 ┆ tokyo, hospital │
├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ big ┆ 2 ┆ walrus, park │
├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ variety ┆ 2 ┆ bond, scuba diver │
├╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ swim ┆ 2 ┆ scuba diver, walrus │
╰───────────┴───────┴─────────────────────╯
Fantastic! Now you don't have to stand your friends' awkward silence anymore. But don't forget you can't talk anyway because you are a spymaster, ha!
There are also other options, feel free to explore:
-
-g
,--get-models
: Print all available language models -
-m
,--set-model
: Select a language model -
-o
,--output
: Specify an output file -
-t
,--token-usage
: Print token usage -
-h
,--help
: Print help -
-V
,--version
: Print version
In the Making
Honestly, choosing Rust was a very ambitious decision and also a bit of a gamble for me. This is my very first open source project, as well as the very first program I wrote in Rust. I knew from the start that I might just have bit more than I could chew, and had to start over. Fortunately, I managed to power through.
I have been learning Rust on and off throughout the months and I really enjoyed it. Something about the ownership model really speaks to me. I also like how the compiler babysits you and give you corrections, until you have an error-free and memory efficient code.
It was really hard at the beginning. I was carrying a lot of fragmented knowledge of rust and tried to make something work without any other consideration. I had another blog post around the early stages of this project if you want to read more.
At that stage I had everything in one main.rs
file, which was fine for a while. However, as the project grows, I created more modules to make things more organized. Here's the project structure now:
src
├── api
│ ├── chat_completions.rs
│ ├── language_models.rs
│ └── mod.rs
├── clue.rs
├── json_models
│ ├── chat_completion.rs
│ ├── language_model.rs
│ └── mod.rs
├── lib.rs
├── main.rs
└── tests
├── expected_outputs
│ ├── chat_completions.txt
│ └── language_models.txt
├── mock_responses
│ ├── chat_completions.json
│ └── language_models.json
└── mod.rs
Some of the things I learned
- Rust's module system. It's more sophisticated than I thought. It took a lot of practice to figure out the
use
andmod
keywords. Also,lib.rs
andmain.rs
belong to different crate (the former to a library crate and the latter to a binary crate). - Starting the project with a module schema in mind is much better than moving things around later on.
-
&str
: A string slice doesn't have an owner, which means it cannot be used as a return type. - The main function should be as simple as possible so that the workflow of the program is clear. Keep the complex code inside a different module.
- When using closure, you can write it like
|_| {}
to discard the captures. -
vector[i]
is the same asvector.get(i).unwrap()
, which means the program will panic ifi
is out of bounds. -
map()
/for_each()
isn't always better than the good oldfor
loop. Shorter code aren't always more efficient, nor are they always easier to read -
.env
files are usually read at runtime, not compile time - Sometimes there's simply no "best practice". For certain things, the efforts might as well be spent elsewhere, than trying to find the "standard" way to write your code.
Some of my best memories
- Working with people was a lot of fun. I am usually the anxious type when it comes to new people. These few weeks I've been pushing myself out of my comfort zone, while having genuinely interesting conversations with really nice people.
- Learning about different crates and libraries out there was fascinating. Coming from a C/C++ student's background, we had been told to not use any third party libraries and do everything manually. This really changes my perception of coding.
- Learning Rust along the way has been an award in itself. The more I use this language, the more I love it.
- That feeling when you have everything organized the way you wanted, and when all the features "just work", like clockworks!
Overall, it's been a fantastic journey! I wouldn't have expected myself to be so incredibly busy, while having this much fun at the same time! Can't wait to see where it'll take me!
Posted on September 21, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.