Today I'm going to be talking about how I wrote acnh_critterpedia, my Ruby CLI application that interfaces with the ACNH API.
The purpose of the application is to be used in conjuction with playing the game Animal Crossing: New Horizons. In the game, one of the things you can do is catch fish, bugs, and sea creatures. The game operates on a real-world clock, so the sun rises and sets and the seasons change the same as in the real world, so the availability of critters will change depending on the month and time of day (and which hemisphere you live in!). A lot of people want to catch everything the game has available and complete their collection, but the game itself doesn't give you a list of everything. Players generally need to use fan-made online tools (like this one!) to look up what they need. Because the ACNH API is so extensive, I knew I would be able to use it build an app that performs functions players would want for all their critter-hunting needs.
I started out by planning out my app as follows:
# Project Structure
## Program Flow
1. Greets User, asks for hemisphere (northern or southern)
2. User provides hemisphere
3. Responds with hemisphere and local time, asks user to pick a selection:
1. View Available Bugs
2. View Available Fish
3. View Available Sea Creatures
4. List Bugs by Month
5. List Fish by Month
6. List Sea Creatures by Month
8. Exit
## Classes
- Critters
- Bugs
- Fish
- Sea Creatures
- CLI
- API
### Notes
https://acnhapi.com/v1a/fish/
https://acnhapi.com/v1a/bugs/
https://acnhapi.com/v1a/sea/
Some things, like the CLI menu options and classes, changed a bit as I developed the app, but I wanted to make sure I had an outline going in so my process had a bit of structure and I didn't get lost trying to figure out what to do next.
The project requirements included having at least 3 classes in the program, which in my case were a CLI class for creating the user interface, handling user input and performing selected tasks, an API class for retrieving data from the API, and a Critter class for storing and displaying critter attributes.
I had originally planned to make a Critter class that Fish, Bugs, and Sea Creatures would inherit from, since they all behave similarly, but I eventually realized they actually behave more or less identically, so if I give the API class the correct part of the URL to search when a search method is run, the Critter class itself does not need to change based on what kind of critter (fish, bug, sea creature) is being asked for. So for now, I decided to just have a Critter class.
In my CLI class, I start out by telling the user the time (which is important for knowing when things are available to catch) and prompting them to enter the hemisphere they are located in. This input is captured in a hemisphere
variable, which is then used throughout the app to search for values in the API that are hemisphere-dependent (namely those having to do with availability by month). The CLI then instantiates the API class with the value of hemisphere
. After that, the user is given options of what they would like to look for:
+-----------------------------------------------------------------------------------+
| Options (pick a number) |
+------------------------+------------------------+---------------------------------+
| 1. View Available Fish | 2. View Available Bugs | 3. View Available Sea Creatures |
+------------------------+------------------------+---------------------------------+
| 4. View Fish by Month | 5. View Bugs by Month | 6. View Sea Creatures by Month |
+------------------------+------------------------+---------------------------------+
| 7. Search Fish by Name | 8. Search Bugs by Name | 9. Search Sea Creatures by Name |
+------------------------+------------------------+---------------------------------+
Type 'about' for more information.
Type 'exit' to close the program.
What would you like to do now? Type 'options' for a list of available commands.
Which is handled with a case statement:
case input
when "options"
list_options
when "about"
about_program
when "1"
available_critters("fish")
when "2"
available_critters("bugs")
when "3"
available_critters("sea")
when "4"
critters_by_month("fish")
when "5"
critters_by_month("bugs")
when "6"
critters_by_month("sea")
when "7"
critters_by_name("fish")
when "8"
critters_by_name("bugs")
when "9"
critters_by_name("sea")
end
Since I have just the one Critter class that will search a different part of the API depending on what critter type it is given, I was able to reuse these methods by just passing in different arguments ("fish", "bugs", or "sea", which are the corresponding sections of the API). These methods show some text to the user and then call on my search methods in the API instance. Here's an example of how that works:
def initialize(hemisphere)
@url = "https://acnhapi.com/v1a"
@hemisphere = hemisphere
end
def search_critter_by_name(name, critter_type)
req_url = "#{url}/#{critter_type}/#{name}"
data = HTTParty.get(req_url)
if data.message == "Not Found"
"error"
else
critter_hash = {
name: data["name"]["name-USen"],
location: data["availability"]["location"],
catch_phrase: data["catch-phrase"],
month_range: data["availability"]["month-#{hemisphere}"],
is_all_year: data["availability"]["isAllYear"],
time_range: data["availability"]["time"],
is_all_day: data["availability"]["isAllDay"]
}
critter = AcnhCritterpedia::Critter.new(critter_hash)
end
end
One thing I ran into that I plan to refactor is how I do my API calls. I was hesitant to store all the data I needed at the beginning because I was worried about app load times, but as a result I end up querying the API every time I do a search, which is not ideal. I also don't instantiate Critter objects at all unless the user asks for "Search [Critter] by name", so some functionality is being under-utilized. I think I will refactor this at some point to search all 3 APIs and create critter objects for everything at the start, which should make doing searches much simpler.
All in all, this was a great first project to work on my Ruby skills with. I learned a lot about planning my project, scoping it out, and working strategically. This gem is available for download, so if you are interested in downloading it to play around with, please do, and let me know what you think!
GitHub repo: https://github.com/merlumina/acnh-critterpedia/
RubyGems page: https://rubygems.org/gems/acnh_critterpedia
Top comments (0)