Wordle y palabras en castellano

¿Se puede resolver de forma «automática»?

Javier Álvarez Liébana https://dadosdelaplace.github.io (Universidad Complutense de Madrid)
2022-01-15

Wordle: el juego de moda

00:01 (hora local). Aterrizaje efectuado sin dificultad. Propulsión convencial (ampliada). Velocidad de aterrizaje: 6:30 de la escala convencional (restringida). Velocidad en el momento del amaraje: 4 de la escala Bajo-U 109 de la escala Molina-Calvo. Cubicaje: AZ-0.3. Denominación local del lugar de aterrizaje: Sardanyola.

Así empieza uno de mis libros favoritos, «Sin noticias de Gurb», en el que Eduardo Mendoza nos contaba la historia de un extraterrestre recién aterrizado en Barcelona, con el objetivo de encontrar a un compañero perdido. Y es que si tuviéramos que elaborar un método en estas primeras semanas de 2022 para detectar si una persona acaba de llegar del espacio exterior, no habría uno mejor que preguntarle: «¿has jugado a WORDLE

Wordle, el juego de moda: <https://www.powerlanguage.co.uk/wordle/>

Figure 1: Wordle, el juego de moda: https://www.powerlanguage.co.uk/wordle/

Este sencillo juego, que imita la dinámica del famoso Master Mind, no tiene muchas reglas pero es «adictivo»: una palabra, 5 letras, y 6 intentos para adivinar el vocablo mientras la aplicación te indica en cada paso que letras están bien colocadas (o mal colocadas o si directamente no aparecen en la palabra). No solo choca su sencillez sino que además es una web distinta a las que hoy nos tiene acostumbrados la red: sin pop-ups, sin anuncios, sin cookies, sin vídeos que se reproducen solos. Nada. Solo un juego, una interfaz sencilla (pero visualmente atractiva) que no reporta ningún beneficio a su creador, el ingenierio de software Josh Wardle. Un juego que, aunque ha alcanzado la categoría de fenómeno de masas a finales de 2021 y principios de 2022, nació además de una historia de amor, como relata el autor al periodista del New York Times Daniel Victor en esta entrevistahttps://www.nytimes.com/2022/01/03/technology/wordle-word-game-creator.html

Por si alguien llega a esta entrada sin conocer el juego, lo explicamos. El objetivo consiste en adivinar una palabra de 5 letras.

Wordle, el juego de moda: <https://www.powerlanguage.co.uk/wordle/>

Figure 2: Wordle, el juego de moda: https://www.powerlanguage.co.uk/wordle/

En cada intento el juego nos indica con amarillo las letras que están en la palabra (pero mal colocadas), en verde las letras que están en la palabra y correctamente colocadas, y en gris las letras que no están en la palabra. Con esas pistas, el usuario tiene 6 intentos y solo podrá jugar una palabra al día (quizás esa sea una de las claves de las ganas de seguir jugando).

Wordle, el juego de moda: <https://www.powerlanguage.co.uk/wordle/>

Figure 3: Wordle, el juego de moda: https://www.powerlanguage.co.uk/wordle/

Desde unas semanas el juego también cuenta con su versión en castellano, https://wordle.danielfrg.com/, adaptado por Daniel Rodríguez, y su versión en catalán, https://gelozp.com/games/wordle/, adaptada por Gerard López, y no han sido pocos los medios que han dedicado sus espacios a hablar de él.

Incluso no son pocos los matemáticos y estadísticos que se han lanzado a intentar analizar el juego, las opciones de ganar y la forma en la que juegan sus usuarios. Es el caso de Esteban Moro, a quién entrevistaban hace unos días en El País contando su estrategia para el juego en inglés, el caso del investigador y divulgador Picanúmeros o yo mismo.

El castellano y sus letras

Todo lo contenido en este documento está libremente disponible en GitHub https://github.com/dadosdelaplace/blog-R

Paquetes de R que vamos a necesitar

Además para la creación de este tutorial he usado {rmarkdown} con el paquete {distill}, el paquete {tweetrmd} para incrustar enlaces de Twitter, el paquete {DT} para las tablas interactivas y {ggtext} para fuentes en las gráficas. Si quieres empezar a programar en R desde cero tienes por aquí materiales gratuitos https://dadosdelaplace.github.io/courses

CREA (Corpus de Referencia del Español Actual)

Dado que se trata de un juego de adivinar palabras en castellano, lo primero que vamos a hacer es analizar (de forma muy de «andar por casa») cómo se comportan las palabras y letras en el castellano, así que necesitamos es un conjunto de palabras con las que trabajar.

Seguramente se pueda scrappear la web oficial del juego, en castellano https://wordle.danielfrg.com/, pero ando escaso de tiempo así que no he podido extraer el historial de palabras que se han jugado hasta ahora (si alguien se anima, todo suyo/a).

Extraer un listado de palabras de la RAE tampoco es sencillo ya que la propia institución no lo pone fácil, hasta el absurdo que su listado de palabras y definiciones no son de uso libre y tiene copyright, como ha comentado en varias ocasiones Jaime Gómez Obregón

Dichos impedimentos hacen incluso difícil saber el número de palabras totales en castellano que la RAE incluye en el diccionario. Según la propia institución:

«Es imposible saber el número de palabras de una lengua. La última edición del diccionario académico (2014), registraba 93 111 artículos y 195 439 acepciones

Lo que si pone la RAE a nuestra disposición es el Corpus de Referencia del Español Actual (CREA). El CREA es un «conjunto de textos de diversa procedencia, almacenados en soporte informático, del que es posible extraer información para estudiar las palabras, sus significados y sus contextos». El corpus de referencia de la RAE cuenta con 152 560 documentos analizados, producidos en los países de habla hispana desde 1975 hasta 2004 (sesgo de selección, parte I), y seleccionados tanto de libros como de periódicos y revistas (sesgo de selección, parte II), y lo tienes en bruto en mi repositorio. Para su lectura podemos usar read_delim() del paquete stringr (cargado en el entorno {tidyverse}).

# Corpus de Referencia del Español Actual (CREA)
# https://corpus.rae.es/lfrecuencias.html
datos_brutos_CREA <- # read
  read_delim(file = "./CREA_bruto.txt", delim = "\t")

Preprocesado

Dicho fichero lo he preprocesado para hacer más fácil su lectura. El archivo preprocesado lo tienes disponible en CREA_procesado.csv y el código que he ejecutado lo tienes debajo.

📝Código

# Eliminamos columna de orden y separamos última columna en dos
datos_CREA <-
  datos_brutos_CREA[, -1] %>%
  separate(col = 2, sep = "\t",
           into = c("frec_abs", "frec_norm"))

# Renombramos columnas
names(datos_CREA) <- c("palabra", "frec_abs", "frec_norm")

# Convertimos a número que vienen como cadenas de texto
datos_CREA <- datos_CREA %>%
  mutate(frec_abs = as.numeric(gsub(",", "", frec_abs)),
         frec_norm = as.numeric(frec_norm))

# convertimos tildes
datos_CREA <-
  datos_CREA %>%
  mutate(palabra = gsub(" ", "", iconv(palabra, "latin1")))

 

La carga desde el archivo ya preprocesado puede hacerse con read_csv().

# Archivo ya preprocesado
datos_CREA <- read_csv(file = "./CREA_procesado.csv")

Tras cargarlo, dado que en el juego en castellano no se admiten tildes, pero si la letra ñ, he decidido eliminar todas las tildes, acentos y diéresis del CREA y he eliminado duplicados (por ejemplo, mi y tras quitar tildes). Tienes debajo en 📝Código un resumen numérico y el código R.

📝Código
# Quitamos tildes pero no queremos eliminar la ñ
datos_CREA <- datos_CREA %>%
  mutate(palabra =
           gsub("ö", "o",
                gsub("ä", "a",
                     gsub("ò", "o",
                          gsub("ï", "i",
                               gsub("ô", "o",
                                    gsub("â", "a",
                                         gsub("ë", "e",
                                              gsub("ê", "e",
                                                   gsub("ã", "a",
                                                        gsub("î", "i",
                                                             gsub("ù", "u",
                                                                  gsub("¢", "c",
                                                                       gsub("ì", "i",
                                                                            gsub("è", "e",
                                                                                 gsub("à", "a", gsub("ç", "c",
           gsub("á", "a",
                gsub("é", "e",
                     gsub("í", "i",
                          gsub("ó", "o",
                               gsub("ú", "u",
                                    gsub("ü", "u",
                                         as.character(palabra)))))))))))))))))))))))) %>%
  # eliminamos duplicados
  distinct(palabra, .keep_all = TRUE) %>%
  # Eliminamos palabras con '
  filter(!grepl("'", palabra) & !grepl("ø", palabra))

datos_CREA %>% skim()
Table 1: Data summary
Name Piped data
Number of rows 693402
Number of columns 3
_______________________
Column type frequency:
character 1
numeric 2
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
palabra 0 1 1 30 0 693402 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
frec_abs 0 1 217.22 19637.76 1 1 2.00 9.00 9999518.00 ▇▁▁▁▁
frec_norm 0 1 1.42 128.72 0 0 0.01 0.05 65545.55 ▇▁▁▁▁

Tras este preprocesamiento nuestro corpus se compone aproximadamente de 700 000 palabras/vocablos, de las que tenemos su frecuencia absoluta frec_abs (nº de documentos analizados en los que aparece) y frecuencia normalizada frec_norm (veces que aparece por cada 1000 documentos).

datos_CREA
# A tibble: 693,402 × 3
   palabra frec_abs frec_norm
   <chr>      <dbl>     <dbl>
 1 de       9999518    65546.
 2 la       6277560    41149.
 3 que      4681839    30689.
 4 el       4569652    29953.
 5 en       4234281    27755.
 6 y        4180279    27401.
 7 a        3260939    21375.
 8 los      2618657    17165.
 9 se       2022514    13257.
10 del      1857225    12174.
# … with 693,392 more rows

Además, he calculado los siguientes parámetros de cada una de las palabras (tienes el código colapsado debajo) por si nos son de utilidad:

📝Código

datos_CREA <- datos_CREA %>%
  mutate(# frec. relativa
         frec_relativa = frec_abs / sum(frec_abs),
         # log(frec. absolutas)
         log_frec_abs = log(frec_abs), 
         # log(frec. normalizadas)
         log_frec_rel = log_frec_abs / sum(log_frec_abs),
         # distribución de frec_norm
         int_frec_norm =
           cut(frec_norm,
               breaks = c(-Inf, 0.01, 0.05, 0.1, 0.5, 1:5,
                          10, 20, 40, 60, 80, Inf)),
         # número de letras
         nletras = nchar(palabra))

Análisis numérico

¿Cómo se distribuyen las frecuencias de las palabras? Si nos fijamos en cómo se reparten las palabras y sus repeticiones a lo largo de los más de 150 000 documentos analizados, obtenemos que el 75% de los vocablos que contiene CREA aparecen, como mucho, en 5 de cada 100 000 documentos.

quantile(datos_CREA$frec_norm)
      0%      25%      50%      75%     100% 
    0.00     0.00     0.01     0.05 65545.55 
datos_CREA %>% skim()
Table 2: Data summary
Name Piped data
Number of rows 693402
Number of columns 8
_______________________
Column type frequency:
character 1
factor 1
numeric 6
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
palabra 0 1 1 30 0 693402 0

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
int_frec_norm 0 1 FALSE 15 (-I: 423578, (0.: 99155, (0.: 71980, (0.: 38683

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
frec_abs 0 1 217.22 19637.76 1 1 2.00 9.00 9999518.00 ▇▁▁▁▁
frec_norm 0 1 1.42 128.72 0 0 0.01 0.05 65545.55 ▇▁▁▁▁
frec_relativa 0 1 0.00 0.00 0 0 0.00 0.00 0.07 ▇▁▁▁▁
log_frec_abs 0 1 1.41 1.83 0 0 0.69 2.20 16.12 ▇▁▁▁▁
log_frec_rel 0 1 0.00 0.00 0 0 0.00 0.00 0.00 ▇▁▁▁▁
nletras 0 1 8.97 2.95 1 7 9.00 11.00 30.00 ▂▇▂▁▁

Es importante advertir que el CREA contiene aproximadamente 8 veces más vocablos que palabras hay registradas en la RAE (según la propia RAE). A diferencia de un diccionario, en CREA no solo hay palabras registradas oficialmente en castellano sino que recopila todo un conjunto de vocablos que aparecen en textos, que no siempre tienen porque estar «validadas» en los diccionarios, incluidos americanismos). Por ello, vamos a hacer un filtro inicial, eliminando aquellas palabras muy poco frecuentes, definiendo como poco frecuente toda aquella palabra que aparezca con una frecuencia inferior a 1 de cada 1000 textos analizados o más (aproximadamente 45 000 vocablos).

📝Código

datos_CREA_filtrado <- datos_CREA %>% filter(frec_norm >= 1)
datos_CREA_filtrado
# A tibble: 40,655 × 8
   palabra frec_abs frec_norm frec_relativa log_frec_abs log_frec_rel
   <chr>      <dbl>     <dbl>         <dbl>        <dbl>        <dbl>
 1 de       9999518    65546.        0.0664         16.1    0.0000164
 2 la       6277560    41149.        0.0417         15.7    0.0000160
 3 que      4681839    30689.        0.0311         15.4    0.0000157
 4 el       4569652    29953.        0.0303         15.3    0.0000156
 5 en       4234281    27755.        0.0281         15.3    0.0000156
 6 y        4180279    27401.        0.0278         15.2    0.0000155
 7 a        3260939    21375.        0.0217         15.0    0.0000153
 8 los      2618657    17165.        0.0174         14.8    0.0000151
 9 se       2022514    13257.        0.0134         14.5    0.0000148
10 del      1857225    12174.        0.0123         14.4    0.0000147
# … with 40,645 more rows, and 2 more variables: int_frec_norm <fct>,
#   nletras <int>

Tras dicho filtrado, he hecho una tabla con las 10 000 palabras más repetidas en frecuencia absoluta, y la tabla con las 500 palabras menos repetidas (pero que aparecen en 1 de cada 1000 documentos analizados, o más), por si quieres curiosear algunas de ellas escribiendo en el buscador.

Palabras más repetidas en CREA (las 12 000 primeras)