Использование R Shiny для создания игры Wordle на армянском языке

Когда в 2020 году началась изоляция, Джош Уордл, инженер-программист из Бруклина, решил создать настольную игру для своей партнерши, чтобы она могла убить время. Вскоре пара поделилась игрой со своими друзьями и членами семьи, и к ноябрю 2020 года у игры было 90 пользователей в день. Ситуация изменилась в ноябре, когда они решили сделать игру общедоступной. Без особого маркетинга игра достигла 300 000 пользователей в день к началу декабря и 2 миллиона к середине декабря. Месяц спустя, в январе 2021 года, The New York Times приобрела игру Джоша Уордла Wordle, заплатив семизначную цену. В 2022 году Wordle стал самым гуглимым словом в мире.

Но что такое Wordle? Как он стал таким популярным? Wordle — это игра в слова, в которой вам нужно угадать слово из 5 букв за 6 попыток. Цвета букв ваших догадок обновляются после каждой попытки помочь вам найти слово. Цвета показывают, правильная ли буква (зеленым цветом), есть ли она в слове, но в другом месте (желтым цветом) или неправильная (серым цветом). Например, если вам нужно угадать слово «стол», а первое предположение — «корона», то все буквы слова «корона» будет помечен серым цветом, потому что ни один из них не находится в слове «table». Если ваша вторая догадка «смелая», то буква «e» будет отмечена зеленым, потому что она верна, буквы «a» и «b» будут отмечены желтым, потому что они есть в слове, но расположены по-разному (например, «a» — это вторая буква в «table», но 3-я буква в «brave»), а буквы «r» и «v» будут помечены как серый, так как их нет в слове «table».

Wordle уже некоторое время является частью моего дня, и когда я начал использовать R Shiny в апреле 2022 года, у меня возникла идея сделать армянскую словесную игру с использованием Shiny. В этой статье я собираюсь объяснить, как сделать игру слов с R Shiny, объясняя все шаги от сбора данных до создания приложения. Кроме того, для каждого раздела я предоставлю различные полезные источники, которые я использовал для работы.

Примечание. Я не буду обсуждать здесь все коды, но в конце каждого раздела буду давать ссылки на коды, относящиеся к этому разделу. Я думаю, чтение раздела и проверка кодов (к ним тоже есть комментарии) дадут достаточное понимание того, что я сделал.

Ниже приведены моменты, которые будут освещены в статье:

  1. Веб-парсинг: Получение всех слов на армянском языке
  2. Очистка данных: отфильтруйте только слова из 5 букв и выполните некоторые преобразования.
  3. Основные функции, необходимые для приложения Wordle
  4. Обновление пользовательского интерфейса с помощью CSS
  5. Генерировать случайное целевое слово каждый день
  6. Инструкции и действия при выигрыше/проигрыше
  7. Виртуальная клавиатура и слушатели
  8. Дополнительно: количество попыток

Зачистка веб-сайтов

Первым шагом является получение данных. Мне пришлось найти сайт, на котором есть все армянские слова, и выдрать оттуда данные. Я использовал разные источники, чтобы узнать больше о парсинге веб-страниц с помощью R, но я бы порекомендовал это видео, поскольку оно охватывает важные шаги. Проведя небольшое исследование, я обнаружил, что на https://bararanonline.com/ есть словарь армянских слов, созданный Э. Б. Агаяном, состоящий из более чем 150 тысяч слов. Я написал код, чтобы получить все слова, кроме тех, которые начинаются с և и ր, так как слов, начинающихся с этих букв, немного. В итоге я получил данные, состоящие из 152 309 армянских слов. Коды для парсинга можно найти здесь.

Очистка данных

Соскоблив данные, я начал их чистить и делать пригодными для игры. Этот процесс включал два шага: получение 5-буквенных слов и работа со словами, содержащими букву ու. Проблема с буквой ու заключалась в том, что при наборе буква использует два символа ո и ւ, но это всего одна буква. Следовательно, такие слова, как նուրբ или աշուն, состоят из 5 символов, но состоят из 4 букв. Я видел другое армянское приложение Wordle, в котором это не учитывалось, и в игру также были включены несколько слов из 4 букв, поэтому мне нужно было позаботиться о том, чтобы не совершить ту же ошибку. Позже для буквы ու я решил использовать только символ ւ, чтобы не было путаницы. После очистки данных я получил 7782 пятибуквенных слова. Все коды для очистки данных можно найти здесь.

Однако очистка данных еще не была завершена. На следующем этапе я сел с тетей, чтобы просмотреть все 7,7 тыс. слов одно за другим, проверяя, следует ли их использовать в игре. Причина в том, что многие слова из 5 букв не очень распространены, и большинство людей не знают этих слов. Проверив все слова, мы обнаружили, что только 2329 из них являются общими, поэтому каждый день будет случайным образом выбираться слово из этих 2329 слов для угадывания. Слова, которых нет в списке общеупотребительных слов, не будут выбраны в качестве целевого слова, но если кто-то наберет эти слова, он не получит ошибку, так как эти слова существуют в армянском языке.

Все окончательные модификации и очищенные наборы данных можно найти здесь.

Основные функции, необходимые для приложения Wordle

Наконец, у нас есть готовые данные, и мы можем начать работу над приложением. Я бы порекомендовал посмотреть видеосерию Уинстона Чанга, чтобы узнать, как сделать игру в слова с R Shiny. В этом разделе я расскажу о двух важных функциях, которые выполняют большинство операций в Wordle. Во-первых, это функция сравнить слова.

compare_words <- function(target_str, guess_str) {
  if (nchar(target_str) != nchar(guess_str)) {
    stop("target and guess string must be the same length")
  }
  
  target <- strsplit(target_str, "")[[1]]
  guess <- strsplit(guess_str, "")[[1]]
  remaining <- character(0)
  result <- rep("not-in-word", 5)
  
  for (i in seq_along(target)) {
    if (guess[i] == target[i]) {
      result[i] <- "correct"
    } else {
      remaining <- c(remaining, target[i])
    }
  }
  
  for (i in seq_along(guess)) {
    if (guess[i] != target[i] && guess[i] %in% remaining) {
      result[i] <- "in-word"
      remaining <- remaining[-match(guess[i], remaining)]
    }
  }
  
  result
}

Функция выбирает целевое слово и угаданное слово в качестве входных данных и возвращает список из пяти — для каждой буквы угаданного слова, показывающий, является ли буква «правильной», «в слове» или «не в слове». Следовательно, если целевое слово — «стол», а наше предположение — «храбрый», то мы получим следующий результат:

Второй важной функцией является функция «проверки слов».

check_words <- function(target_str, guess_str) {
  compare_result <- compare_words(target_str, guess_str)
  correct <- FALSE
  if (all(compare_result == "correct")) {
    correct <- TRUE
  }
  
  list(
    guess = guess_str,
    letters = strsplit(guess_str, "")[[1]],
    result = compare_result,
    correct = correct
  )
}

«Проверить слова» просто берет результат сравнения слов и вносит некоторые изменения. На выходе «проверочные слова» — список, состоящий из 4 списков:

  • Угадай — слово, которое мы угадали.
  • Буквы — каждая буква угадываемого слова.
  • Результат — вывод функции «сравнить слова». Сюда можно перенести операции сравнения слов, но для простоты я вынес их отдельно.
  • Правильно — логический элемент, показывающий, совпадают ли догадка и целевое слово.

Все 4 списка используются в разных настройках игры, и эти две функции выполняют все необходимые для приложения операции. Затем я применил функцию формат результата, которая кодирует каждую букву цветом с помощью базового CSS. Все три функции можно найти здесь.

Помимо этих функций, я использовал несколько функций JavaScript, но они используются для других целей: разрешить отправку догадок с помощью кнопки Ввод, добавить слушателей на клавиатуру, добавить кнопку удаления. Функции JS можно найти здесь.

Обновление пользовательского интерфейса с помощью CSS

Несмотря на то, что вышеупомянутые функции сделали все необходимые операции, я не смог бы отобразить нужные мне результаты без CSS. Использовать CSS в R Shiny очень просто, все, что вам нужно сделать, это написать в разделе UI

tags$style(HTML(" ")

и поместите компоненты CSS в скобки. Затем вам нужно написать style = «имя компонента CSS» в тех местах, где вы хотите применить код CSS. Привожу простой пример того, как я хочу отформатировать кнопку «Инструкции». Вначале в HTML(“ ”) я пишу следующий код:

.instructions {font-size: 16px;
               margin-top: 1px;
               margin-left: 1px;
               position: absolute;
               top: 0;
               right: 0;
               }

а позже в ActionButton для инструкций я добавил class = «instructions», чтобы применить дизайн, который я упоминаю в коде CSS. CSS применялся для многих частей игры. Помогло обновить цвета букв на клавиатуре, обновить дизайн разных кнопок, добавить иконки для некоторых кнопок и т.д.

Создавайте случайное целевое слово каждый день

Я также хочу поговорить о различных компонентах игры, над которыми стоит поработать. Первый компонент — убедиться, что случайное слово из списка генерируется каждый день. Это можно сделать, просто используя функцию «образец».

Sys.setenv(TZ='Asia/Yerevan')
set.seed(as.integer(Sys.Date()))
target <- sample(words_common_5, 1)

Обязательно используйте set.seed() перед функцией примера, чтобы получать одно и то же случайное слово каждый раз, когда вы запускаете игру в течение дня. Кроме того, поскольку мое приложение армянское, я изменил часовой пояс, чтобы целевое слово обновлялось по армянскому времени.

Инструкции и действия при выигрыше или проигрыше

В приложении я добавил несколько всплывающих сообщений, чтобы показать инструкции или если игра выиграна/проиграна. Компонент инструкций довольно прост, все, что я сделал, это добавил ActionButton, записал правила и сделал кнопку более крутой, добавив значки.

Немного больше работы потребовалось для всплывающих сообщений при выигрыше или проигрыше игры. Во-первых, мне нужно было определить, что значит выиграть или проиграть. Победа в игре означает нахождение целевого слова. Последний список вывода функции «сравнить слова» показывает, выиграли мы игру или нет. Я написал функцию, которая отключает все кнопки и показывает всплывающее сообщение, если игра выиграна. Несколько иначе обстояло дело, когда игра была проиграна. Я написал функцию для отключения всех кнопок и показа всплывающего сообщения, если количество догадок больше или равно количеству попыток (компонент, который я добавил сам, об этом расскажу позже).

Клавиатура и слушатели

Если вы заметили, в оригинальной игре wordle вы можете печатать с клавиатуры вашего ноутбука или с виртуальной клавиатуры, которая доступна на экране. Цель этого раздела — показать, как сделать эту клавиатуру на экране и обновить ее соответствующими цветами. Это видео очень полезно для того, как сделать виртуальную клавиатуру с помощью HTML, JS и CSS. Я взял логику оттуда и применил ее к Shiny. Сначала я сделал ActionButtons для каждой буквы и рассортировал их по разным строкам. Затем я написал JS-функцию для добавления слушателей к каждой клавише (взяв букву и набрав ее в разделе догадок) и создал дизайн для каждой клавиши с помощью CSS. Наконец, я написал функцию для обновления цветов клавиш, если это необходимо. Последнее нужно сделать в три этапа.

Во-первых, я взял вывод функции «проверить слова» и сохранил использованные буквы и их результаты («правильно», «в слове» или «не в слове») в списке.

Затем я написал функцию, которая обновляет значения в списке после каждой итерации. Например, если целевое слово — «стол», а ваше первое предположение — «смелый», то для буквы «а» вы получите результат «в слове», так как это 2-я буква в «стол», но 3-я буква в слове «храбрый». Если ваше второе предположение — «бекон», то для буквы «а» результат теперь «правильный». Нужно сделать так, чтобы после очередного угадывания результат менялся с «в слове» на «верно», а также обновлялся в списке.

Наконец, я использовал UpdateActionButton с некоторыми другими функциями для обновления CSS ключа, если выполняется условие. Обратите внимание, что код CSS, который я использовал здесь для обновления цветов, немного отличается от того, который я использовал для обновления цветов предположений. Причина в том, что для CSS, который обновляет цвета клавиш, мне тоже нужны слушатели, а для догадок мне не нужны слушатели.

Дополнительно: количество попыток

Это компонент, который я добавил в игру, чтобы при необходимости у людей было больше попыток. Игрок может выбрать от 6 до 10, сколько попыток ему нужно иметь. Убедитесь, что вы написали функцию, которая останавливает игру, если игрок обновляет количество попыток, чтобы оно стало меньшим числом, чем количество догадок, которое у него уже есть.

Заключение

В целом, я рассмотрел шаги, необходимые для создания армянского Wordle с помощью R Shiny. Можно сделать словесную игру, создав несколько основных функций для выполнения всех необходимых операций, создав несколько функций JS для добавления дополнительных функций и обновив пользовательский интерфейс с помощью CSS. Если вы новичок в CSS или JS, как и я, это может показаться тяжелой работой, но поверьте мне, как только вы поймете логику, стоящую за ними, вы сможете легко заставить все работать.

Коды, которые я использовал для создания приложения, находятся здесь.

Наконец, посмотрите мой армянский Wordle здесь.