Solução do Wordle usando operadores da API de query do MongoDB
Avalie esse Artigo
Este artigo detalha uma das minhas viagens de aprendizado do MongoDB Atlas. Entrei no MongoDB no final de 2022 como Developer Advocate para o Atlas Search. Com algumas décadas de experiência no Lucene, eu reconheço a pesquisa, mas tinha pouca experiência com o MongoDB em si. Como parte da minha iniciação, eu precisava aprender a MongoDB Query APIe combinei meu aprendizado com meu interesse no Wordle.
O jogo online Wordle conquistou o mundo em 2022. Para muitos, inclusive eu, Wordle se tornou parte da rotina diária. Se você não conhece o Wordle, primeiro deixe-me pedir desculpas por apresentá-lo ao seu próximo coletor de tempo favorito. O jogo de adivinhação de palavras Wordle oferece seis chances de adivinhar a palavra de cinco letras do dia. Depois de adivinhar, cada letra da palavra adivinhada é marcada com pistas que indicam quão bem ela corresponde à resposta. Vamos direto para um exemplo, com nosso primeiro palpite sendo a palavra
ZESTY
. Wordle nos dá estas dicas após essa suposição:As dicas nos informam que a letra E está na palavra objetivo, mas não na segunda posição, e que as letras
Z
, S
, T
e Y
não estão na solução em nenhuma posição. Nossa próxima suposição leva em conta essas dicas, dando-nos mais informações sobre a resposta:Você sabe a resposta neste ponto? Antes de revelá-lo, vamos aprender um pouco de MongoDB e criar uma ferramenta para nos ajudar a escolher possíveis soluções com base nas dicas que temos.
Podemos usar facilmente a camada gratuita para sempre do MongoDB hospedado, chamada Atlas. Para jogar junto, visite a página inicial do Atlas e crie uma nova conta ou faça login na sua existente.
Depois de ter uma conta Atlas, crie um banco de dados para conter uma coleção de palavras. Todas as palavras possíveis que podem ser adivinhadas ou usadas como respostas diárias são incorporadas ao código-fonte do próprio aplicativo Wordle de página única. Essas palavras foram extraídas em uma lista que podemos inserir rapidamente em nossa coleção do Atlas.
Criei um repositório para os dados e código aqui. O README mostra como importar a lista de palavras para a sua collection do Atlas para que você possa jogar junto.
As operações de query necessárias são:
- Encontre palavras com uma letra específica em uma posição exata.
- Encontre palavras que não contenham nenhum de um conjunto de letras.
- Encontre palavras que contenham um conjunto de letras especificadas, mas não em nenhuma posição conhecida.
Para acomodar esses tipos de critérios, um documento do word tem a seguinte aparência, usando a palavra MONGO para ilustrar:
1 { 2 "_id":"MONGO", 3 "letter1":"M", 4 "letter2":"O", 5 "letter3":"N", 6 "letter4":"G", 7 "letter5":"O", 8 "letters":["M","O","N","G"] 9 }
Cada palavra é seu próprio documento e estruturado para facilitar os tipos de consultas necessárias. Venho de um histórico de pesquisa de texto completo, em que faz sentido dividir os documentos em unidades atômicas localizáveis para obter capacidade de consulta e desempenho limpos. Há, sem dúvidas, outras maneiras de implementar a estrutura de documentos e os padrões de query para esse desafio, mas aguarde enquanto aprendermos a usar a API de query do MongoDB com essa estrutura específica. Cada posição de letra da palavra tem seu próprio campo, para que possamos consultar correspondências exatas. Há também um campo catch-all contendo uma matriz de todos os caracteres exclusivos da palavra, para que as consultas não precisem necessariamente se preocupar com posições.
Vamos desenvolver a API de query do MongoDB para encontrar palavras que correspondam às dicas da nossa suposição inicial. Primeiro, quais palavras não contêm
Z
, S
, T
ou Y
? Usando operadores de queryda API de query do MongoDB em uma chamada de API.find()
, podemos usar o operador$nin
(não em) da seguinte forma:1 { 2 "letters":{ 3 "$nin":["Z","S","T","Y"] 4 } 5 }
Independentemente disso, um
.find()
para todas as palavras que têm uma letra E
mas não na segunda posição fica assim, usando o operador$all
, pois pode haver potencialmente várias letras que sabemos que estão na solução, mas não em qual posição elas estão em:1 { 2 "letters":{ 3 "$all":["E"] 4 }, 5 "letter2":{"$nin":["E"]} 6 }
Para encontrar as possíveis soluções, combinamos todos os critérios para todas as dicas. Após nossa suposição
ZESTY
, os critérios.find()
completos são:1 { 2 "letters":{ 3 "$nin":["Z","S","T","Y"], 4 "$all":["E"] 5 }, 6 "letter2":{"$nin":["E"]} 7 }
Fora do universo de todas as 2,309 palavras, há 394 palavras possíveis após nosso primeiro cúmulo.
Agora vamos ao nosso segundo palpite,
BREAD
, que nos deu várias outras informações sobre a resposta. Agora sabemos que a resposta também não contém as letras B
ou D
, então adicionamos isso ao nosso campo de letras $nin
cláusula. Também sabemos que a resposta tem um R
e A
em algum lugar, mas não nas posições que inicialmente adivinhamos. E agora sabemos que a terceira letra é um E
, que é correspondido usando o operador$eq
. Combinando todas essas informações de nossos palpites, ZESTY
e BREAD
, acabamos com este critério:1 { 2 "letters":{ 3 "$nin":["Z","S","T","Y","B","D"], 4 "$all":["E","R","A"] 5 }, 6 "letter2":{"$nin":["E","R"]}, 7 "letter3":{"$eq":"E"}, 8 "letter4":{"$nin":["A"]} 9 }
A resposta já se revelou para você? Caso contrário, importe a lista de palavras para o Atlas e execute a agregação.
É entediante acumular todas as dicas em
.find()
critérios manualmente, e letras duplicadas na resposta podem apresentar um desafio ao traduzir as dicas codificadas por cores para a API de query do MongoDB, então escrevi um pouco de código Ruby para lidar com os detalhes. Na linha de comando, usando este código, as possíveis palavras após nossa primeira suposição ficam assim...1 $ ruby word_guesser.rb "ZESTY x~xxx" 2 {"letters":{"$nin":["Z","S","T","Y"],"$all":["E"]},"letter2":{"$nin":["E"]}} 3 ABIDE 4 ABLED 5 ABODE 6 ABOVE 7 . 8 . 9 . 10 WOVEN 11 WREAK 12 WRECK 13 394
A saída da execução de
word_guesser.rb
consiste primeiro na API de consulta do MongoDB gerada, seguida por todas as palavras correspondentes possíveis, dadas as dicas fornecidas, terminando com o número de palavras listadas. Os argumentos da linha de comando para o script de adivinhação de palavras são uma ou mais strings entre aspas que consistem na palavra adivinhada e uma representação das dicas fornecidas dessa palavra, onde x
é uma letra acinzentada, ~
é uma letra amarela e ^
é uma letra verde. Cabe ao solucionador humano escolher uma das palavras listadas para tentar a próxima tentativa. Após nossa segunda estimativa, o comando e a saída são:1 $ ruby word_guesser.rb "ZESTY x~xxx" "BREAD x~^~x" 2 {"letters":{"$nin":["Z","S","T","Y","B","D"],"$all":["E","R","A"]},"letter2":{"$nin":["E","R"]},"letter3":{"$eq":"E"},"letter4":{"$nin":["A"]}} 3 OPERA 4 1
Voilá, resolvido! Apenas uma palavra possível após a nossa segunda suposição.
Em resumo, este exercício descontraído me fez aprender os operadores da API de query do MongoDB, especificamente os operadores
$all
, $eq
e $nin
para esse desafio.Para saber mais sobre a API de query do MongoDB, confira estes recursos:
- Introdução ao Atlas e à MongoDB Query Language (MQL)(agora conhecida como MongoDB Query API)