
Содержание статьи
Эндрю Бейлза
В этой статье вы узнаете, как создать терминальную игру с помощью CSV и нескольких драгоценных камней Ruby! Просмотрите демонстрацию в видео выше и найдите код GitHub.
Этот проект происходит из лекции, которую я прочитал в Ada Developers Academy в Сиэтле. Темой была библиотека Ruby CSV, и я хотел интересный способ проиллюстрировать ее методы и потенциал.
На уроке мы обсуждали, как большинство людей используют такие программы, как Excel, для создания и редактирования CSV-файлов. Они обновляются, нажимая ячейки и изменяя значение. Но для нас встал вопрос: что мы можем сделать с CSV, если подойти к нему как программистов? Используя язык программирования, как Ruby, как можно открывать, читать и манипулировать этими значениями? Могут ли эти упорядоченные строки и столбцы стать базой для программы?
Эта статья охватывает эти вопросы в трех главах:
- Значения, разделенные запятыми
- CSV библиотека Ruby: создание, открытие, добавление, использование заголовков
- Построение игры
Значения, разделенные запятыми
CSV означает значения, разделенные запятыми, и это звучит именно так. Если вы когда-либо открывали один из этих файлов в таком приложении, как Excel, вы видели эти значения, отображенные в электронной таблице.
Однако, если бы вы открыли тот же файл в текстовом редакторе, например Atom или Sublime, вы нашли серию значений, разделенных запятыми. Как вы видите ниже, Excel использует эти исходные значения для воспроизведения удобной таблицы.

Быстрая настройка
Проще всего следовать, если вы загрузите этот репозиторий GitHub. Сделав это, перейдите к этой папке в терминале. Это репо включает все из приведенных ниже примеров, поэтому знайте, что вам нужно будет прокомментировать разделы, которые вы не хотите запускать.
Кроме того, вы захотите установить Awesome Print, украшающий выход терминала:
gem install awesome_print
Библиотека CSV от Ruby
Ruby поставляется с библиотекой CSV, позволяющей нам открывать, читать и манипулировать файлами CSV.
Создание CSV
Давайте начнем с создания нашего собственного CSV. В репозитории Github вы найдёте planets.rb. Этот файл начинается с установки переменных планет равными двумерному массиву (массиву массивов).
В каждом из нас есть атрибуты планеты: id, название, масса и расстояние. Мы назначили имена атрибутов переменной заголовков как другой массив.
require 'csv'require 'awesome_print'
planets = [ [1, "Mercury", 0.055, 0.4], [2, "Venus", 0.815, 0.7], [3, "Earth", 1.0, 1.0], [4, "Mars", 0.107, 1.5]]headers = ["id", "name", "mass", "distance"]CSV.open("planet_data.csv", "w") do |file| file << headers planets.each do |planet| file << planet endend
Выше CSV.open принимает до трех аргументов:
CSV.open(file name, mode, options)
Мы дали ему имя файла (planet_data.csv). Поскольку мы также задали режим w (только для записи), он создает новый файл для нас, даже если он еще не существовал. За это время не было предложено никаких вариантов.
Следующий блок выполняет несколько вещей:
- Он добавляет массив заголовков в файл, который мы создали. Это создает одну строку с четырьмя столбцами, каждая из которых содержит строчную запись имени свойства.
- Мы используем planets.each для итерации по массиву планет (заполненного информацией о его идентификаторе, названии и т.п.) и добавляем каждую запись как отдельную строку.
Если вы запустите этот фрагмент кода, вы увидите, что создан файл CSV:

Режимы
Выше мы использовали w как наш режим для записи нового файла. У вас есть ряд других доступных вариантов в зависимости от поставленной задачи. Важнейшими факторами, которые следует учитывать, является то, хотите ли вы читать и/или писать, а также где в CSV вы хотите начать свою работу.
Например, если вы используете файл для заполнения своего веб-сайта списками, r (только для чтения) будет соответствующим режимом. Если вы хотите добавить новые планеты к вашему CSV, режим «a» (сложение чтения-записи) начнется в конце файла и немедленно позволит вам добавить эти строки.
Вот полный список режимов:
“r” Read-only, starts at beginning of file (default mode).“r+” Read-write, starts at beginning of file.“w” Write-only, truncates existing file to zero length.“w+” Read-write, truncates existing file to zero length.“a” Append write-only, starts at end of file if file exists.“a+” Append read-write, starts at end of file if file exists.“b” Binary file mode.“t” Text file mode.
Добавление
Мы можем добавить новую планету к planet_data.csv следующим образом:
CSV.open("planet_data.csv", "a") do |file| file << [5, "Jupiter", 1234, 3321]end
В приведенном выше списке режимов «a» есть «только для записи» и «начинается с конца файла». Следовательно, информация о Юпитере будет вставлена в конец существующего CSV.
Итерация
Поскольку .open с режимом r возвращает массив массивов, мы можем использовать .each для итерации по строкам. Следующий код напечатает каждую строку CSV в терминале.
CSV.open("planet_data.csv", "r").each do |row| ap rowend
Вы можете сделать еще один шаг, чтобы создать интерполированные предложения!
CSV.open("planet_data.csv", "r").each do |row| ap "#{row[1]} has a mass of #{row[2]} and distance of #{row[3]}."end
Это замечательно, но могло бы быть получше. Нам нужно использовать индексы (1, 2, 3), чтобы получить доступ к данным. Это склонно к ошибкам и, как правило, не доставляет удовольствия. Далее мы увидим, как исправить, передав параметры.
Когда вы добавите параметр, чтобы заголовки были истинными, вы получите новый объект CSV::Table.
csv_with_headers = CSV.open("planet_data.csv", "r", headers: true, header_converters: :symbol)csv_with_headers.each do |row| ap rowend
Читая заголовки и превращая эти заголовки в символы, мы получим уникальный объект: массив хешей. Это означает, что можно повторять каждую строчку, как мы делали раньше, но тогда мы также можем использовать символы в хэше для выделения ключевых данных.
Если мы вернемся к примеру предложения, оно станет следующим:
CSV.open("planet_data.csv", "r", headers: true, header_converters: :symbol).each do |row| ap "#{row[:name]} has a mass of #{row[:mass]} and distance of #{row[:distance]}."end
Это гораздо легче, чем индексы чисел, которые мы использовали раньше!
Когда заголовки имеют значение true, библиотека предоставляет нам объект CSV::Table, который также дает нам доступ к некоторым удобным методам. Ниже .read является синонимом .open в режиме «r»:
csv = CSV.read("planet_data.csv", headers: true, header_converters: :symbol)ap csv # <CSV::Table mode:col_or_row row_count:6>ap csv.headers # Returns an array of headersap csv.by_col[:id] # Array of id column dataap csv.by_col[:name] # Array of name column dataap csv.by_row[0] # Entire row at 0 (or any position)ap csv[:name][3] # Name of the 3rd entry => "Mars"ap csv[3][:name] # 3rd row's name => "Mars"
Игра «Построй Солнечную систему»!
Мы знаем, как открывать и использовать данные в файле CSV с помощью Ruby, поэтому давайте используем эти методы для создания игры солнечной системы.

Настройка
Вам нужно будет установить Catpix и Launchy. Catpix разрешает иллюстрации в терминале, а Launchy позволяет нам управлять окном браузера. В терминале:
gem install catpix gem install launchy
CSV как база данных
Вы можете открыть Solar System.csv в Excel, чтобы визуально получить представление об атрибутах для каждой записи. Когда вы поймете данные, мы используем Ruby, чтобы прочесть файл CSV и назначить его глобальной переменной ($solar_system_data). Это будет служить нашей базе данных.
Когда игра открывается, мы приветствуем пользователя К СОЛНЕЧНОЙ СИСТЕМЕ! и создайте эту базу данных так:
require 'catpix'require 'launchy'$solar_system_data = CSV.read("Solar System.csv", headers: true, header_converters: :symbol)
ap "WELCOME TO THE SOLAR SYSTEM!"
Игра действительно запускается, когда мы вызываем метод explore_planet. Этот метод содержит следующий код:
ap $solar_system_data.by_col[:name]prompt = "Where would you like to start? 0 - #{$solar_system_data.length}"
ap promptinput = gets.chomp
until $selected_planet && /\d/.match(input) ap prompt input = gets.chomp $selected_planet = $solar_system_data[input.to_i]end
ap $selected_planet
Вверху терминал печатает все имена из столбца имя. Затем он просит пользователя выбрать запись от первого (0-индекс) до последнего (длина наших данных). Это хороший момент, чтобы сделать паузу, чтобы рассмотреть следующее:
вопрос: Если мы использовали заголовки, чтобы получить хэш, как может solar_system_data.length == 14?
ответ: Эта таблица CSV:: может смотри как хэш, но на самом деле это массив хешей. Поэтому у него длина, и мы можем перебирать каждый хеш. Чтобы выбрать правильную запись, нам просто нужно преобразовать ввод из строки в целое число (.to_i)
Вы также увидите, что мы использовали оператор до. Это подтверждает выбор – спрашивает ответ, пока пользователь не даст нам действительный номер. После правильного выбора терминал распечатает информацию о планете.
Затем пользователь может выбрать, хочет ли он УЗНАТЬ о планете или УВИДЕТЬ ее:
prompt = "Do you want to LEARN or SEE?"ap prompt
while input = gets.chomp case input.downcase when "learn" Launchy.open($selected_planet[:uri]) return when "see" Catpix::print_image $selected_planet[:image] return else ap prompt endend
По-прежнему оператор while используется, чтобы убедиться, что мы получаем действительную запись. На этот раз он либо использует Launchy, чтобы открыть связанный URI для планеты, либо печатает изображение в терминале с помощью Catpix.
В игре есть еще одна функция. Это сохраняется в способе select_attribute. Мы используем методы CSV, которые мы только что рассмотрели, для возвращения конкретных атрибутов каждый планеты в нашей базе данных.
ap "Which attribute do want to see for each planet (ex: number_of_moons)?"
ap $solar_system_data.headers.to_sattribute = gets.chomp
ap "Here are the #{attribute} findings:"
$solar_system_data.each do |row| ap "#{row[:name]} --> #{attribute}: #{row[attribute.to_sym]}"end
Сначала мы распечатываем все заголовки в виде строк. Это дает пользователю перечень атрибутов для выбора. С помощью пользовательского ответа мы можем указать название планеты вместе с запрашиваемым атрибутом и его значением.
Наконец, они могут ВЫБРАТЬ другой атрибут или начать сначала и ИССЛЕДОВАТЬ отдельные планеты:
prompt = "SELECT another attribute or EXPLORE another planet?"ap prompt
while input = gets.chomp case input.downcase when "select" select_attribute() when "explore" explore_planet() else ap prompt endend
Надеюсь, это поможет прояснить методы CSV и заставит вас создавать свои игры.
Если вы расширяете это или создаете что-нибудь новое, оставьте комментарий. Я бы хотел увидеть, что ты придумаешь!