Project 1 - RiftOdds. So you can rage at your teammates with some statistical certainty.

Riot Games API Integration w/ Ruby 2.1.3 and Rails 4.2.3

RiftOdds Repo

I’ve always wanted to know how to integrate a simple API to a rails app. So when I found out that Riot has a developers’ portal, I looked into it and made a simple app.

For those of who’ve never played League of Legends, here is a quick run-down. Five players (aka summoner) on a team. Each summoner chooses a Champion. Two teams are matched against each other. First team to completely obliterate the other team wins.

Now that we understand how the game works, let’s make a rails app. To keep the math simple and not abuse the API call limit, this app will simply do the following: Given two summoners’ names and and two champions’ names, return their win-rate of their respective champions. Sound simple enough, right?

Obtain a Riot API key and Access the Docs.

First thing first, head over to League of Legends website and create an account if you do not have one already. Next, head over to LoL Developer Portal, log in, and get your API key. This is also the site that you might constantly revisit to read the API Documentation, so it might be helpful to bookmark it.

Gotta Set Up Somewhere

To get started, simply create a new rails project. Let’s call it riftodds.

rails new riftodds

Let’s update up our Gemfile before we do anything. Now, I personally am a huge fan of HAML and better_errors, so in the application below I will be using the HAML syntax as oppose to the erb syntax. If you are not familiar with HAML, take a quick look at the HAML Documentation. I promise it will be quick to learn and very rewarding. PS: Most people put better_errors under group :development. I forgot to do so, but feel free to do it if you wish.

gem "better_errors"
gem 'haml', '~> 4.0.6'

Covering Some Basics

Now, let’s begin by thinking what we need to accomplish here. First, we need to add some sort of player column to the database so that we can get the information we want from the two players. The two players name will both be type string. Let’s create that model first.

rails g model Players p1:string p2:string

But wait, don’t we need the champions names too? It appears that I forgot to add that field in our Players model. No fear, Let’s just create a simple migration to accommodate my mistake. Both Champions will have name type string.

rails g migration add_champions_to_players c1:string c2:string

Great, run rake db:migrate and now there should be the following files under the db/migrate directory

20150715123456_create_players.rb
20150716987654_add_champions_to_players.rb

Now that we got the model, we need a corresponding controller to interact with the model. Let’s create a Players controller now.

rails g controller Players

Before we define any action, let’s think about what we really need. Since all we need is for the user to input four values to a form, and then display the corresponding calculations, I took the following approach: Render the form on the index page, and then display the results on the show page. With this method, we only need the index, create, and show method.

players_controller.rb

class PlayersController < ApplicationController

	def index
	end

	def create
	  @player = Player.new(players_params)
	  @player.save
	  redirect_to @player
	end

	def show
	  @player = Player.find(params[:id])
	end

	private

	def players_params
	  params.require(:player).permit(:p1, :p2, :c1, :c2)
	end

end

while we’re at it, let’s define the players resources and set the root page to the index of the application in routes.rb .

Rails.application.routes.draw do
    resources :players
    root 'players#index'
end

And don’t forget to define the corresponding view pages too

views/players/index.html.haml

= form_for :player, url: players_path do |f|
  = f.label :p1
  %br/
  = f.text_field :p1
  %br/
  %br/
  = f.label :p2
  %br/
  = f.text_field :p2
  %br/
  %br/


  = f.label :c1
  %br/
  = f.text_field :c1
  %br/
  %br/
  = f.label :c2
  %br/
  = f.text_field :c2
  %br/
  %br/


  = f.submit

Now open up the rails server point to localhost:3000. You should see the form. But wait a second, the form is not descriptive at all. How are the users suppose to know what p1, p2, c1, and c2 represents? Let’s fix that by changing the labels on the form as described here

views/players/index.html.haml

= form_for :player, url: players_path do |f|
  = f.label :p1, "First Summoner's Name " 
  %br/
  = f.text_field :p1
  %br/
  %br/
  = f.label :p2, "Second Summoner's Name"
  %br/
  = f.text_field :p2
  %br/
  %br/


  = f.label :c1, "Summoner One's Champion"
  %br/
  = f.text_field :c1
  %br/
  %br/
  = f.label :c2, "Summoner Two's Champion"
  %br/
  = f.text_field :c2
  %br/
  %br/


  = f.submit

Great. Now the form looks much informative. But let’s not forget about the show page. For now, let’s simply display the both summoner’s names and the champions names.

views/players/show.html.haml

%p
  %b= @player.p1
  %b= @player.c1
%p
  %b= @player.p2
  %b= @player.c2

Awesome. Head to the index page, input something, and take a look at the show page. It doesn’t look like much, but this is all the information we need to play around with Riot API and get what we ultimately want - the win-rate of each summoner and his respective champion.

Understanding Riot API

Finally, the good parts. If you forget the developer link for whatever, here it is again. First thing first, let’s have a basic understanding of the API documentation first. One thing to keep in mind is the API rate limits. In this API case, it is at most 10 requests every 10 seconds, up to 500 requests every 10 minutes. This simply means that some apps that we might want to create with the Riot API may not be achievable because of the rate limits.

Looking at the getting started page, it appears that all the API calls are within the following format: a base URL https://na.api.pvp.net/ , follow by the targeted API method call, follow by your API key. (Ex: https://na.api.pvp.net/api/lol/na/v1.4/summoner/by-name/RiotSchmick?api_key=SOME_KEY)

Because there are no ruby gems for the Riot API, we need to manually integrate the URL call whenever we want to call upon the Riot API.

Now that we understand the basics, let’s get started.

Configuring Riot API

Before we do anything with the Riot API, let’s keep your API key save. To achieve this in Rails, I typically use the Figaro Gem. Figaro will allow you to securely store your API keys (or any other sensitive materials) and remove them from your .gitingore if you choose to push to Git. Let’s configure Figaro now.

Gemfile

gem "figaro"

bundle install, and then call the bundle exec figaro install to install Figaro.

This will create a config/application.yml that you can define your sensitive materials. In this case, we need to store the riot_api_key. Before we store it in application.yml, notice one thing. Although all the API calls do end with your API key, it doesn’t just require you to simply append your key at the end of the URL. Instead, you are require to set your API key like so ?api_key=YOURKEY . This suggests that rather than adding every URL with the ?api_key= string, we might as well as simply put that entire string as part of the API key when storing it.

config/application.yml

riot_api_key = "?api_key=1234567-8900987-654321"

and now you can use the following syntax anytime you need to call upon your riot_api_key

ENV["riot_api_key"]

If you encounter any sort of error from this point on, try restarting the servers first. For some reason I think you need to restart the server whenever the riotapi.rb file is altered in any way. If that does not fix it, feel free to leave a comment below!

Implementing Riot API

Before this application, I have no idea how to implement an API at all. With that said, here are some of the most helpful resources on StackOverflow that I read when I attempted the integration. How to Organize External API Calls and How to Use External API and Another One on How to Use External API

After some research, it appears that API call without a gem belongs to the lib/ directory, as the lib/ directory is use to store code that could stand on its own (like a gem) and does not belong to the Model, the View, or the Controller. The Riot API fits this description because there could potentially be a gem for it, but there isn’t one. API call also does not relate to any of the MVC, so we’ll put it in gem.

Before we can call the API from Riot’s server, we need some way to establish the connection. For this purpose, we’ll use the HTTParty Gem. This gem allows the application to establish a connection with a third-party API, and parse it into JSON format that we can mess around with.

Include the HTTParty Gem in your GEMFILE and bundle install

gem "httparty"

Now, let’s create a riotapi.rb within the lib/ directory.

In order to use the HTTParty gem, we must require it within the file. If you’re wondering why it’s not include, here is a great response to require vs include.

We then simply create a class. Let’s call it RiotApi. Since we are dealing with the Riot API call within this file, we sure need to incorporate the API URL somehow. Following the breakdown we mentioned earlier, we can establish two variables, one for the BASE_URL an one for the API_KEY and assign them the appropriate values. We then need to define a method to parse the information we get into JSON format. Using this post as a guide, we can, for now, derive the following RiotApi class

lib/riotapi.rb

class RiotApi

  BASE_URL = "https://na.api.pvp.net"
  API_KEY = ENV["riot_api_key"]

  def parse_json(url)
    response = HTTParty.get(url)
    json = JSON.parse(response.body)
    json
  end

end

parse_json method will simply parse any url given into JSON format.

Gathering Summoner Information

Now that we can parse any URL into JSON format, we can start targeting the information that we actually need. Remember, we need, to some extent, information on the summoners and their respective champions. Looking through the API Doc, we can see that stats-v.13 might provide us with that we need. Under /api/lol/{region}/v1.3/stats/by-summoner/{summonerId}/ranked, we can see that it does return a champion value, and within the champion value is a list of stats for that champion. In that list, there exists totalSessionsWon, totalSessionsPlayed, and totalSessionsLost. Which is exactly what is required to calculate the win-rate.

However, upon closer inspection, it appears that the API call requires a region and a summonerId. This makes sense, As the statistics might be separated by region, and each player’s stat is recorded individually under their own unique summonerId. For our intents and purposes, we can assume that all region simply points to North America, or simply na in this case. Therefore, our first issue now is to figure out how to obtain the summoner id when given a summoner name.

It wasn’t hard to figure out how to solve the problem. Looking at the Riot API, it appears that under Summoner-v1.4, you can call the API to return information on a certain summoner given the name, including the summoner ID (api/lol/{region}/v1.4/summoner/by-name/{summonerNames}). Given that our application asks for the summonerNames, we can utilize this method call. But first, we need to create a function that parse it into JSON format so we can work with it.

lib/riotapi.rb

class RiotApi

  BASE_URL = "https://na.api.pvp.net"
  API_KEY = ENV["riot_api_key"]

  def get_summoner_id(name)
    summoner_url = "#{BASE_URL}/api/lol/na/v1.4/summoner/by-name/#{name}#{API_KEY}" 
    parse_json(summoner_url)
  end

  def parse_json(url)
    response = HTTParty.get(url)
    json = JSON.parse(response.body)
    json
  end

end

Given a name, the get_summoner_id method will return all summoner information on that name, including the summoner API. To see that this function does work, let’s play around with it by setting up some methods in our model and call it within our view. In order for the play model to recognize the riotapi file within lib, we need to require it in our model.

One thing to note is that when using string interpolation as demonstrated above, double quotes around the string is mandatory. If the above code is coded as summoner_url = '#{BASE_URL}/api/lol/na/v1.4/summoner/by-name/#{name}#{API_KEY}' , it would return an error.

/app/model/Player.rb

class Player < ActiveRecord::Base
  require 'riotapi'

  # returns the player id
  def self.return_id(name)
    api = RiotApi.new
    info = api.get_summoner_id(name)
    info
  end
end

In the return_id method above, we’ve simply initiated a new instance of the RiotApi, call the get_summoner_id function within that API, and return the information obtained. Let’s check to see if this works.

In our show page, we’ll display that information and see what kind of info we get.

/app/views/players/show.html.haml

%p
  %b= @player.p1
  = Player.return_id(@player.p1)

This will show the players name, and all the information gathered from the return_id method.

Awesome, now that we got the required setups up and running, lets test it out with the form on our index page. One of the more well-known player in League goes by the summoner name uzi. Let’s put that name as our player 1 in the form. For the other three fields (summoner 2, champion 1, and champion 2), input doublelift as summoner 2, janna as champion 1, and jinx as champion 2. By the way, these four values will be our test values for this application.

Upon saving the form, the show page should return the following

uzi {“uzi”=>{“id”=>40671078, “name”=>”Uzi”, “profileIconId”=>753, “summonerLevel”=>30, “revisionDate”=>1437445602000}

Let’s break down this information. When we searched for “uzi”, the Riot API says that the summoner name Uzi has an ID of 40671078, In-game-name of Uzi, Profile Icon Id is 753, Summoner Level is Level 30, and the last revision Date. From this JSON info, all we want right now is the id.

Since the above information is stored as a hash, we can get to the id by calling [“uzi”][“id”]. Remember though, this information is returned to us from the return_id method within the Model. Furthermore, we already have the first key (“uzi”), as it was not only our first form field, but also the method parameter. Therefore, all we really need is attach [name]["id"] by updating the return_id method within the model.

/app/models/player.rb

# returns the player id
def self.return_id(name)
  api = RiotApi.new
  info = api.get_summoner_id(name)
  info[name]["id"]
end

Now when the show page is refreshed, this should be what be displayed

uzi 40671078

Sweet, we’re almost done with obtaining the Summoner portion of the app. One last thing. Notice how the API only accepts lower-cased summoner’s name as a key (“uzi” as opposed to “Uzi”), it would be a headache if a user inputs Uppercase name. Let’s do a quick befores_save method in our model that will down-case all player names before saving them to the Player database.

/app/models/player.rb

class Player < ActiveRecord::Base
  require 'riotapi'

  before_save :downcase_players

  # returns the player id
  def self.return_id(name)
    api = RiotApi.new
    info = api.get_summoner_id(name)
    info
  end

  private

  #downcase the players name before saving
  def downcase_players
    self.p1.downcase!
    self.p2.downcase!
  end
end

Awesome, now we know how to obtain the Player ID through the Riot API! Although it is not what we want yet, it is definitely a huge leap forward.

Gathering Champions Information

Now that we have a way to return the summoner ID, it’s time to put that into use so we can return the statistics of a summoner. Looking at stats-v1.3 and the required URL call, we see that we can now create a method within our RiotApi class to return the statistics. Let’s do that now by creating a get_full_stats method

lib/riotapi.rb

require 'httparty'

class RiotApi

  BASE_URL = "https://na.api.pvp.net"
  API_KEY = ENV["riot_api_key"]

  def get_summoner_id(name)
    summoner_url = "#{BASE_URL}/api/lol/na/v1.4/summoner/by-name/#{name}#{API_KEY}" 
    parse_json(summoner_url)
  end

  def get_full_stats(summoner_id)
    stat_url = "#{BASE_URL}/api/lol/na/v1.3/stats/by-summoner/#{summoner_id}/ranked#{API_KEY}" 
    parse_json(stat_url)
  end

  ...

end

The get_full_stats method will take a summoner ID and return the statistics from the Riot API and parse it. Now we need a method for the Players to call get_full_stats, so let’s define a return_summoner_champion_list method in our Player model.

app/models/player.rb

class Player < ActiveRecord::Base

  require 'riotapi'


  before_save :downcase_players

  # returns the player id
  def self.return_id(name)
    api = RiotApi.new
    info = api.get_summoner_id(name)
    info[name]["id"]
  end

  # return a list of champions a given summoner has played in ranked
  def self.return_summoner_champion_list(summoner_name)
    id = return_id(summoner_name)
    api = RiotApi.new
    full_stat = api.get_full_stats(id)
    full_stat
  end
end

return_summoner_champion_list will take a summoner name, get the id using the previously defined return_id method, and return the full statistics of that summoner. Let’s see this method in action in our view.

app/views/players/show.html.haml

%p
  %b= @player.p1
  = Player.return_summoner_champion_list(@player.p1)

Let’s return to our index page, input the same form values again (uzi, doublelift, janna, jinx), and save it. On the show page, you should see something very similar to the following (I’ll cut it down due to the large amount of data the API is giving back)

uzi {“summonerId”=>40671078, “modifyDate”=>1437355043000, “champions”=>[{“id”=>40, “stats”=>{“totalSessionsPlayed”=>6, “totalSessionsLost”=>4, “totalSessionsWon”=>2, “totalChampionKills”=>2, “totalDamageDealt”=>43266, “totalDamageTaken”=>75566, “mostChampionKillsPerSession”=>1, “totalMinionKills”=>47, “totalDoubleKills”=>0, “totalTripleKills”=>0, “totalQuadraKills”=>0, “totalPentaKills”=>0, “totalUnrealKills”=>0, “totalDeathsPerSession”=>27, “totalGoldEarned”=>37897, “mostSpellsCast”=>0, “totalTurretsKilled”=>0, “totalPhysicalDamageDealt”=>20250, “totalMagicDamageDealt”=>23014, “totalFirstBlood”=>0, “totalAssists”=>62, “maxChampionsKilled”=>1, “maxNumDeaths”=>11}}, {“id”=>42,…

Looking at the data we received, we can indeed see that there is an array of champion hashes, which is exactly what we want. Within the champions array, each element is defined by a champion ID, and the statistics regarding that champion ID. From the data above, we can see that the summoner Uzi has played with a champion with an ID of 40. With that champion, Uzi has played a total 6 games (totalSessionsPlayed), won 2 of them (totalSessionsWon) and lost 4 (totalSessionsLost).

Because we are not interested in any other statistics other than the champions array, we can tell return_summoner_champion_list to only return the champions data from all the statistics by adding ["champions"] to the return_summoner_champion_list call.

/app/model/player.rb

class Player < ActiveRecord::Base

  require 'riotapi'


  before_save :downcase_players

  # returns the player id
  def self.return_id(name)
    api = RiotApi.new
    info = api.get_summoner_id(name)
    info[name]["id"]
  end

  # return a list of champions a given summoner has played in ranked
  def self.return_summoner_champion_list(summoner_name)
    id = return_id(summoner_name)
    api = RiotApi.new
    full_stat = api.get_full_stats(id)
    full_stat["champions"]
  end
end

If you refresh the show page, you should see that all the unrelated statistics are now gone, and what remain is an array of champion ids and their stats.

Finding Champion through Champion ID

Another problem (though familiar) arises. How do we match the champion ID to its corresponding champion name? Let’s take a look at the Riot API Doc again. Looking at lol-static-data-v1.2, under /api/lol/static-data/{region}/v1.2/champion , we see that under data, it returns a bunch of champion informations, including name and id. One problem though, what exactly is the Map[string, ChampionDto], the return type of data? Let’s find that out first.

We’ll simply go through the same processes as above, adding a get_champion method to the RiotApi class, a get_champion_id in our player model, and display it in the show page.

lib/riotapi.rb

require 'httparty'

class RiotApi

  BASE_URL = "https://na.api.pvp.net"
  API_KEY = ENV["riot_api_key"]

  def get_summoner_id(name)
    summoner_url = "#{BASE_URL}/api/lol/na/v1.4/summoner/by-name/#{name}#{API_KEY}" 
    parse_json(summoner_url)
  end

  def get_full_stats(summoner_id)
    stat_url = "#{BASE_URL}/api/lol/na/v1.3/stats/by-summoner/#{summoner_id}/ranked#{API_KEY}" 
    parse_json(stat_url)
  end

  def get_champion
    champion_url = "#{BASE_URL}/api/lol/static-data/na/v1.2/champion#{API_KEY}"
    parse_json(champion_url)
  end

  ....
end

app/models/player.rb

class Player < ActiveRecord::Base

  require 'riotapi'


  before_save :downcase_players

  # returns the player id
  def self.return_id(name)
    api = RiotApi.new
    info = api.get_summoner_id(name)
    info[name]["id"]
  end

  # return a list of champions a given summoner has played in ranked
  def self.return_summoner_champion_list(summoner_name)
    id = return_id(summoner_name)
    api = RiotApi.new
    full_stat = api.get_full_stats(id)
    full_stat["champions"]
  end

  # get the champion ID given the champion name
  def self.get_champion_id(champ_name)
    api = RiotApi.new
    champs = api.get_champion
    champs["data"]
  end
end

app/views/show.html.haml

%p
  %b= @player.c1
  = Player.get_champion_id(@player.c1)

Going back to the index page, inputting the same values (uzi, doublelift, janna, jinx), you should see something very similar to the following

Janna {“Thresh”=>{“id”=>412, “key”=>”Thresh”, “name”=>”Thresh”, “title”=>”the Chain Warden”}, “Aatrox”=>{“id”=>266, “key”=>”Aatrox”, “name”=>”Aatrox”, “title”=>”the Darkin Blade”}, “Tryndamere”=>{“id”=>23, “key”=>”Tryndamere”, “name”=>”Tryndamere”, “title”=>”the Barbarian King”}, “Gragas”=>{“id”=>79,….

Great, it appears that data will return EVERY champions information, starting with their name as the key and info as the values. From the information above, it appears that if we to know what Champion Thresh’s ID is, we can simply call [“data”][“Thresh”][“id”], and it’ll return to us 412. Let’s use that knowledge to modify our API call once again.

app/models/player.rb

class Player < ActiveRecord::Base

  require 'riotapi'


  before_save :downcase_players

  # returns the player id
  def self.return_id(name)
    #some code...
  end

  # return a list of champions a given summoner has played in ranked
  def self.return_summoner_champion_list(summoner_name)
    # some code...
  end

  # get the champion ID given the champion name
  def self.get_champion_id(champ_name)
    api = RiotApi.new
    champs = api.get_champion
    champs["data"][champ_name]["id"]
  end
end

champs["data"][champ_name]["id"] will return us to whatever the ID of whatever champion name parameter we feed it.

One more thing though. In the information returned by data, it appears that all the champion keys start with an upper case letter (like a real name), and if it’s a multi-word name, Each word starts with an upper case letter, but the space is stripped between them (for example, Champion “Lee Sin” appears as “LeeSin” when used as the key value for data). Let’s write a quick before_save method in our model to prevent the saving of any incorrectly formatted names in our database.

app/models/player.rb

class Player < ActiveRecord::Base

  require 'riotapi'


  before_save :downcase_players
  before_save :mod_champ_save

  ....

  private 

  #downcase the players name before saving
  def downcase_players
    # some code...
  end

  #modifies champion name before saving to the database
  def mod_champ_save
    #split the word first. capitalize each individual word. Join them back with no spaces in between. 
    self.c1 = c1.split.map(&:capitalize).join('')
    self.c2 = c2.split.map(&:capitalize).join('')
  end
end

what mod_champ_save is doing is the following: Because it is a pretty accurate assumption to assume that the user will input two-words names with a space in between, we need to split them up first. First, the split method will split the words into their own individual word. If it’s a single word, it doesn’t do anything. Next, map will map each word and capitalize the first letter. Finally, join will join all words back together without any spaces in between. This is where I got the solution from

Now if you input the same information again through the index page, you should see Janna 40 on the show page. 40 is indeed Janna’s champion ID, so that’s awesome. Here is a nice reference to see which champion has what ID number.

Great, now that we have all the information we need, it’s time to do some win-rate calculations.

Calculating Win-Rate

Let me give you the full method first, and then I’ll break it down for you. The following determine_champion_probability method will take two parameters, a summoner’s name and a champion’s name.

app/models/player.rb

class Player < ActiveRecord::Base

  require 'riotapi'


  before_save :downcase_players
  before_save :mod_champ_save

  # returns the player id
  def self.return_id(name)
    #some code...
  end

  # return a list of champions a given summoner has played in ranked
  def self.return_summoner_champion_list(summoner_name)
    # some code...
  end

  # get the champion ID given the champion name
  def self.get_champion_id(champ_name)
    # some code...
  end

  # see if a summoner has played with a given champion in ranked
  def self.determine_champion_probability(summoner_name, champ_name)

    champion_ids = return_summoner_champion_list(summoner_name).collect{|player| player["id"]}
    desired_champ_id = get_champion_id(champ_name)
    winrate = 0.0

    if champion_ids.include?(desired_champ_id)
      # get the desired champion array location
      hash = Hash[champion_ids.map.with_index.to_a]
      champ_array_location = hash[desired_champ_id]

      # calculate winrate
      player_stat = return_summoner_champion_list(summoner_name)[champ_array_location]["stats"]
      total_played = player_stat["totalSessionsPlayed"]
      total_won = player_stat["totalSessionsWon"]

      winrate = (total_won.to_f / total_played.to_f)
      winrate.round(2) * 100
    else
      winrate = 0.5 * 100
    end
  end

  private 

  #downcase the players name before saving
  def downcase_players
    # some code...
  end

  #modifies champion name before saving to the database
  def mod_champ_save
    # some code...
  end
end

Initially, we declare a champ_ids variable. In that variable, we call summoner_champion_list on a given summoner, return the the list of all the champions (and all the champion information) that summoner has played. We then pass that to .collect{|player| player["id"]}, which returns ONLY the ID of those champions. The result is that champ_ids now has an array of champions ID given a summoner name. Now, some of you may be wonder why I’m using .collect as opposed to .each, so here is a quick rundown on why .collect works in this case but not .each.

Next, since we already have the champion name, all we need to do is get the champion ID. desired_champ_id stores that information. We also set the initial winrate to 0.0.

Now, the winrate can only exist if the champion we are looking for (desired_champ_id) is within the array of champion_ids. The if statement will check that for us. If the champion is not within the array of champion_ids, we will simply grant them, and assume, a lenient 50% winrate (though I believe the actual statistics would point to around 30% for all new champions played upon release).

If the champion is indeed within the array, we need a way to find it within the actual return_summoner_champion_list. Now, if you remember, return_summoner_champion_list seems to return the list of champions in a random fashion (or not, I just didn’t quite figure it out). This means that either we can loop through the list again trying to match desired_champ_id to one of the element within the array, or we can use the code we already have.

When I double checked, it appears that the array of champion_ids do preserve the index location of each IDs when compared to the list taken from return_summoner_champion_list. This means that if we can find a way to locate the index, we can get the information we want immediately without all the hassles of looping and searching.

After looking quite a few SO posts, I stumble upon this post. Following the solution, I declared a hash = Hash[champion_ids.map.with_index.to_a] and saved the desired champion index with champ_array_location = hash[desired_champ_id]. This variable stores the index location of the champion we are looking for.

All I have to do then is to simply gather the information and do a simple calculation. player_stat = return_summoner_champion_list(player_name)[champ_array_location]["stats"] returns the summoner statistics with respect to the champion we are interested in. total_played = player_stat["totalSessionsPlayed"] and total_won = player_stat["totalSessionsWon"] provided the values we need to calculate the winrate. winrate = (total_won.to_f / total_played.to_f) calculates the actual winrate (need to cast it to a floating value), and finally multiply winrate by 100 to get the percentage value.

To see all this in action, let’s define our show page.

app/views/players/show.html.haml

%p
  In any given ranked game, the probability of 
  %b= @player.p1
  winning with
  %b= @player.c1
  is
  = Player.determine_champion_probability(@player.p1, @player.c1)
  &#37

%p
  In any given ranked game, the probability of 
  %b= @player.p2
  winning with
  %b= @player.c2
  is
  = Player.determine_champion_probability(@player.p2, @player.c2)
  &#37

The &#37 provides the % symbol

There it is! You’ve successfully messed around with the Riot API and created something with it.

Finishing Touches

They picture are worth a thousand words, so let’s add some pictures. Let’s add some champion icons to the application. From the Riot Developer Portal regarding static data, it appears that we can render the icons simply by calling the URL. Let’s add some image_tag to the show page.

And while we’re at it, why not add the option for the users to search again after the initial Pair?

app/views/players/_form.html.haml

= form_for :player, url: players_path do |f|
  = f.label :p1, "First Summoner's Name "
  %br/
  = f.text_field :p1
  %br/
  %br/
  = f.label :p2, "Second Summoner's Name"
  %br/
  = f.text_field :p2
  %br/
  %br/


  = f.label :c1, "Summoner One's Champion"
  %br/
  = f.text_field :c1
  %br/
  %br/
  = f.label :c2, "Summoner Two's Champion"
  %br/
  = f.text_field :c2
  %br/
  %br/


= f.submit

app/views/players/index.html.haml

= render 'form'

app/views/players/show.html.haml

%p
  In any given ranked game, the probability of 
  %b= @player.p1
  winning with
  = image_tag "http://ddragon.leagueoflegends.com/cdn/5.13.1/img/champion/#{@player.c1}.png"
  %b= @player.c1
  is
  = Player.determine_champion_probability(@player.p1, @player.c1)
  &#37

%p
  In any given ranked game, the probability of 
  %b= @player.p2
  winning with
  = image_tag "http://ddragon.leagueoflegends.com/cdn/5.13.1/img/champion/#{@player.c2}.png"
  %b= @player.c2
  is
  = Player.determine_champion_probability(@player.p2, @player.c2)
  &#37

  %h2 Want to Search Another Pair?
  = render 'form'

And that’s it!

Thanks for sticking with me till the end. I hope you’ve enjoyed it and, most importantly, learned something along the way.

If you have any comments or questions, please don’t hesitate to post them below.

Some Self Reflection

When I read back on what I’ve written, I honestly could not believe that I wrote an entire tutorial. In regards to the application itself, there were some tweaks that I thought about adding, but simply didn’t.

First would probably be some CSS styling. Second would probably be writing some tests. And third would be some error checking. I did not add in any of them listed above because when I first started, I really wanted to focus only on rails and the topics at hand. Many times when I go through other tutorials, I tend to skip the styling and front-end stuff because I didn’t mind not having them for this app.

With that said, there are probably bugs and errors within the app that I did not encounter. If, given the example input fields (uni, doublelift, janna, jinx), you encounter some sort of error, please let me know so I can work them out and give an update. If you encounter errors that occur from other fields of input, please let me know too! But the foundation of the app should be working. After all, the point of this tutorial is to work with the Riot API.

See you next time.