Dizionario di Par🇮🇹le
Il dizionario di Par🇮🇹le (o Parle) è basato su dizionari disponibili pubblicamente.
- un dizionario "piccolo" di circa 60_000 parole (
dict/60_000_parole.txt
) preso da napolux/paroleitaliane - un dizionario "grande" di più di 4 milioni di parole (
dict/dictionary.txt
) preso da sigmasaur/AnagramSolver
Inoltre il dizionario usato da parolette è stato usato come fonte di ulteriore idee da aggiungere.
Questo documento spiega ed implementa il processo di generazione dei dizionari usati da Parle.
I due dizionari sorgente sono usati per generare i dizionari usati dal gioco, ovvero:
dict/curated.txt
: dizionario che contiene in ordine alfabetico tutte le parole di 5 lettere che sono usate come parole da indovinaredict/word_list.txt
: dizionario che contiene in ordine alfabetico tutte le parole di 5 lettere che possono essere usate come tentativi (contiene anche le parole indict/curated.txt
)dict/parole.txt
: dizionario che contiene l'ordine esatto delle parole da indovinare a partire dalla prima data del gioco (contiene tutte e sole le parole didict/curated.txt
in ordine sparso)
Il processo di generazione prevede i seguenti passi:
- estrarre le parole di 5 lettere dai dizionari piccolo e grande, limitandosi
alle parole che finiscono in una vocale, tranne la u
(il dizionario piccolo contiene radici di lemmi come ripar o simili).
Il risultato (in ordine alfabetico) è salvato in
dict/small5.txt
edict/big5.txt
. Estrarre dai dizionari le parole di 5 lettere che finiscono in consonante o in u (e.g. hotel, ...) e salvarle indict/hotels.txt
- il dizionario
dict/curated.txt
è costruito manualmente a partire dai dizionari fin qui generati. contestualmente sono creati manualmente due dizionaridict/word_list_add.txt
edict/word_list_remove.txt
che serviranno per generare il dizionariodict/word_list.txt
. - Il dizionario
dict/parole.txt
è generato dalle parole indict/curated.txt
con una generazione casuale (tenendo conto di un dizionariodict/itLines.js
di parole fisse iniziali usato per gestire i cambi di dizionario) - Infine viene generato il file
dict/itLines.js
con le linee di codice da usare nel codice affinché usi le liste didict/parole.txt
edict/word_list.txt
generate fino a qui.
Storia del processo di generazione del dizionario
Il processo è cambiato nel tempo:
- il dizionario piccolo è in uso dal 3 gennaio 2022 (giorno di nascita di Parle)
- il dizionario grande è in uso dal 4 gennaio 2022
- a partire dal 13 febbraio la numerazione delle parole è fatta a partire dal 3 gennaio (precedentemente la numerazione era fatta a partire dal 1 febbraio 2021, data scelta un po' per caso ed un po' per sbaglio)
- a partire dal 14 febbraio le parole da indovinare sono "curate", ovvero è stato fatto un processo di selezione (precedentemente erano selezionate in modo casuale a partire dal dizionario piccolo)
Il resto del documento (in inglese) contiene il codice che implementa il processo e può contenere qualche lieve spoiler.
Implementation
0. Filenames
All filenames used
const
folderDict = "dict/"
filenameSmallSource = folderDict & "60_000_parole.txt"
filenameBigSource = folderDict & "dictionary.txt"
filenameCurated = folderDict & "curated.txt"
filenameWordList = folderDict & "word_list.txt"
filenameParole = folderDict & "parole.txt"
filenameSmall5 = folderDict & "small5.txt"
filenameBig5 = folderDict & "big5.txt"
filenameHotel = folderDict & "hotels.txt"
filenameAddToWordList = folderDict & "word_list_add.txt"
filenameRemoveFromWordList = folderDict & "word_list_remove.txt"
filenameFixed = folderDict & "fixed.txt"
filenameParolette = folderDict & "parolette.txt"
filenameParoletteIdeas = folderDict & "parolette_ideas.txt"
filenameItLines = folderDict & "itLines.js"
filenameReactleValidGuesses = folderDict & "validGuesses.ts"
filenameReactleParole = folderDict & "wordlist.ts"
linkParolette = "[parolette](https://parolette.netlify.app)"
1. Extract 5 letter words from source dictionaries
type FilterFileStats = tuple[sourceLines, destLines: int]
proc filterFile(source, dest: string, condition: string -> bool): FilterFileStats =
## filter lines in source file according to condition and write valid lines in destination
## outputs line count of source and destination file
var outLines : seq[string]
for line in source.lines:
inc result.sourceLines
if condition(line):
inc result.destLines
outLines.add line
writeFile(dest): outLines.join("\n")
const
alphabet = toSet("abcdefghijklmnopqrstuvwxyz")
commonEndings = toSet("aeio")
otherEndings = alphabet - commonEndings
template letterOk(word: string, i: int): bool =
word[i] in alphabet
template lettersOk(word: string): bool =
letterOk(word, 1) and letterOk(word, 2) and letterOk(word, 3) and letterOk(word, 4)
# should be faster using the template lettersOk than using toSet(word) <= alphabet, right?
func buonaCond(word: string): bool =
len(word) == 5 and lettersOk(word) and word[^1] in commonEndings
func hotelCond(word: string): bool =
len(word) == 5 and lettersOk(word) and word[^1] in otherEndings
echo filterFile(filenameSmallSource, filenameSmall5, buonaCond)
echo filterFile(filenameBigSource, filenameBig5, buonaCond)
echo filterFile(filenameBigSource, filenameHotel, hotelCond)
(sourceLines: 60453, destLines: 2510) (sourceLines: 4261494, destLines: 7910) (sourceLines: 4261494, destLines: 1281)
2. Checks on manual dicts and generate word list
template make(ident: untyped) {. dirty .} =
let
ident = lines(`filename ident`).toSeq
`ident Set` = ident.toHashSet
echo `filename ident`, ": ", len(ident)
assert len(ident) == len(`ident Set`) # checks there are no doubles
template checkIsSorted(ident: untyped) =
var isSorted = true
for i in 0 ..< ident.high:
if cmp(ident[i], ident[i+1]) > 0:
echo ident[i], " > ", ident[i+1]
isSorted = false
if isSorted:
echo `filename ident`, " is sorted"
else:
echo "[ERROR]", `filename ident`, " is NOT sorted"
template checkContained(identA, identB: untyped) =
var isContained = true
for elem in `identA Set`:
if elem not_in `identB Set`:
echo elem, " not in ", `filename identB`
isContained = false
if isContained:
echo `filename identA`, " is contained in ", `filename identB`
else:
echo "[ERROR]", `filename identA`, " is NOT contained in ", `filename identB`
make small5
checkIsSorted small5
make big5
checkIsSorted big5
make hotel
checkIsSorted hotel
make curated
checkIsSorted curated
make fixed
checkContained fixed, curated
# checkContained small5, big5 # not expected to happen (and it does not happen)
make addToWordList
make removeFromWordList
checkContained removeFromWordList, big5
dict/small5.txt: 2510 dict/small5.txt is sorted dict/big5.txt: 7910 dict/big5.txt is sorted dict/hotels.txt: 1281 dict/hotels.txt is sorted dict/curated.txt: 1524 dict/curated.txt is sorted dict/fixed.txt: 49 dict/fixed.txt is contained in dict/curated.txt dict/word_list_add.txt: 86 dict/word_list_remove.txt: 167 dict/word_list_remove.txt is contained in dict/big5.txt
dictionary from italian version of wordle parolette is used to generated more ideas that have been incorporated in the curated dictionary (or in word list).
make parolette
let wordIdeas = sorted(toSeq(paroletteSet - curatedSet - big5Set))
dump len(wordIdeas)
writeFile(filenameParoletteIdeas, wordIdeas.join("\n"))
dict/parolette.txt: 1917 len(wordIdeas) = 234
Generation of final word list
let
wordListSet = curatedSet + big5Set + addToWordListSet - removeFromWordListSet
wordList = sorted(toSeq(wordListSet))
dump len(wordList)
checkContained curated, wordList # almost obvious
writeFile(filenameWordList, wordList.join("\n"))
len(wordList) = 7834 dict/curated.txt is contained in dict/word_list.txt
3. Generate list of solution words
var paroleFuture = toSeq(curatedSet - fixedSet)
randomize(880222+190509+810713)
shuffle(paroleFuture)
let
parole = fixed & paroleFuture
writeFile(filenameParole, parole.join("\n"))
assert len(parole) == len(curated)
4. Generate itLines.js
let
paroleForJs = parole.mapIt(&"\"{it}\"").join(", ")
wordListForJs = (wordListSet - curatedSet).toSeq.sorted.mapIt(&"\"{it}\"").join(", ")
writeFile(filenameItLines): &"""
var Aa = [{paroleForJs}],
La = [{wordListForJs}],
"""
5. Generate files for Reactle
let
reactleValidGuesses = wordList.mapIt(&" '{it}',").join("\n")
reactleParole = parole.mapIt(&" '{it}',").join("\n")
writeFile(filenameReactleValidGuesses): &"""export const VALID_GUESSES = [
{reactleValidGuesses}
]
"""
writeFile(filenameReactleParole): &"""export const WORDS = [
{reactleParole}
]
"""
Nim(ib) notes
These notes may be of interest to you only if you know what Nim and Nimib are (language and library used to generate this document).
- imports are not shown (see Show Source below)
- const filenames used in initial paragraph but shown later