IMGD 2905 Project 4

League of Legends Analytics

Due date: Saturday, April 21st, 11:59pm (was April 19th)

[League of Legends]


In this project, you will first setup tools for doing game analytics on Riot's League of Legends League. Then, you will use the pipeline to extract data from Riot's game data set, analyze game data through charts and tables, and writeup the results in a report for dissemination.


Top | Part 0 | Part 1 | Part 2 | Part 3 | Writeup | Hints | Submit | Grade

Part 0 - Get Ready

Learn League

Learn a bit about League of Legends. You are more than welcome to play the game. But, given some of the game's complexities, if you prefer, you can read and/or watch videos instead. Some links that may be useful include:

Get Riot API Key

The Riot API is an Application Programmer Interface (API) for retrieving game data for the popular computer game League of Legends.

  1. Sign up for a League of Legends account (free):

https://signup.na.leagueoflegends.com/en/signup/index

Note: There may be up to a 48-hour delay between the time from first making a League of Legends account and getting a Riot API key (the next step). So do this early!

  1. Sign in to the Riot API Dashboard:

https://developer.riotgames.com/signin

Note 1: You will need to keep track of your personal API key after logging in (e.g., 12345678-abcd-9012-efgh-345678901234). These are used in the scripts that extract data from the RIOT API. These should not be shared.

Note 2: The API key expires every 24 hours. You can re-generate it by signing in again (step #2) and going to the "Dashboard".

Install RiotWatcher

RiotWatcher is a thin wrapper on top of the Riot Games API for League of Legends. See:

https://github.com/pseudonym117/Riot-Watcher

To install, open a terminal window / command prompt. Type:

pip install riotwatcher

Test It Out

Put the below script into a file called "hello.py".

#!/usr/bin/python3

#
# Basic "hello, world!" for Riot API.
# v2.0
#

# Bring in imports needed for data processing.
from riotwatcher import RiotWatcher
import json

# Replace below with personal Riot API key.
KEY = 'PERSONAL-RIOT-API-KEY_HERE'

# Using North America region.
REGION = 'na1'

# Getting info on this player.
PLAYER = 'Grig1'

# Get master RiotWatcher object, using Riot developer key.
r_w = RiotWatcher(KEY)

# Get info on summoner.
player = r_w.summoner.by_name(REGION, PLAYER)
print(json.dumps(player, indent=2))

# Get list of Champions.
static_champ_list = r_w.static_data.champions(REGION)

# Print out "Teemo" and "Leona"
print("-----------------");
champ = 'Teemo'
print("Teemo info:");
print(json.dumps(static_champ_list['data'][champ], indent=2))
champ = 'Leona'
print("Leona info:");
print(json.dumps(static_champ_list['data'][champ], indent=2))

Note: You need to replace PERSONAL-RIOT-API-KEY_HERE' with your own API key.

From a terminal/command prompt, run hello.py with:

python hello.py

(Linux/Mac users should use python3, as usual).

If all is working, your output should look similar to:

  {
    "name": "Grig1",
    "id": 21542029,
    "accountId": 34970420,
    "revisionDate": 1523495535000,
    "profileIconId": 983,
    "summonerLevel": 87
  }
  -----------------
  Teemo info:
  {
     "title": "the Swift Scout",
     "id": 17,
     "name": "Teemo",
     "key": "Teemo"
  }
  Leona info:
  {
     "title": "the Radiant Dawn",
     "id": 89,
     "name": "Leona",
     "key": "Leona"
  }

Part 1 - Welcome, Summoner!

Exploration: For a competitive/professional League of Legends player (summoner) of your choice, analyze: a) the number of League matches played over time; and b) the duration of the matches.

Advanced: c) For the same player, pick an in-depth exploration of either a), or b). This could be many additional games over many years (i.e., over the 100 required), correlation of duration with other in-game data, analysis of summoners Champions played, or a related in-depth analysis of your choosing.

Find a competitive/professional player

There are many ways to find professional League players, but a pretty easy way is through a Google search. Or, any friends that have played competitive/ranked League matches can be used. You can even use yourself if you've played a significant number of competitive/ranked League matches.

Note 1: You may not use the player Grig1.

Note 2: You must analyze at least 100 matches, so make sure your player has that many.

Script - Print times played for a player's matches

#!/usr/bin/python3

#
# get-times.py
# version 1.0
#

########################################
## IMPORTS

from riotwatcher import RiotWatcher
import json
import time

########################################
## SETTINGS
KEY = 'PERSONAL-RIOT-API-KEY_HERE'
REGION = 'na1'   # North America as region
PLAYER = 'Grig1' # Player (Summoner) of interest
MATCH_LIMIT = 0  # Number of matches to analyze (0 for no limit)

print("REGION:", REGION)
print("PLAYER:", PLAYER)
print("MATCH_LIMIT:", MATCH_LIMIT)
print("----------------------")

########################################
# SETUP

# Get master RiotWatcher object, using my Riot developer key.
r_w = RiotWatcher(KEY)

# Get player with summoner name.
player = r_w.summoner.by_name(REGION, PLAYER)
id = player['accountId']

########################################
# GET FULL MATCH DATA

# Get player's match list from match data.
match_data = r_w.match.matchlist_by_account(REGION, id)
match_list = match_data['matches']

# Read in full match data for MATCH_LIMIT matches.
full_match_list = []
i = 0
for match in match_list:
  print("Reading full match %d..." % i)
  m = r_w.match.by_id(REGION, match['gameId'])
  full_match_list.append(m)
  i += 1
  if (i == MATCH_LIMIT):
    break
  time.sleep(1)  # Pause to avoid overwhelming Riot API quota.

print("----------------------")

########################################
# TIMES

# Go through each match in list.
i = 0
for match in full_match_list:
  print("Match", i, ":", match['gameDuration'], "seconds")
  i += 1

# Done.
exit()

Script - Print durations of a player's matches

#!/usr/bin/python3

#
# get-durations.py
# version 1.0
#

########################################
## IMPORTS

from riotwatcher import RiotWatcher
import json
import time

########################################
## SETTINGS
KEY = 'PERSONAL-RIOT-API-KEY_HERE'
REGION = 'na1'   # North America as region
PLAYER = 'Grig1' # Player (Summoner) of interest
INDEX = 0        # Start of player matches (will get 100 total)

print("REGION:", REGION)
print("PLAYER:", PLAYER)
print("INDEX:", INDEX)
print("----------------------")

########################################
# SETUP

# Get master RiotWatcher object, using my Riot developer key.
r_w = RiotWatcher(KEY)

# Get player with summoner name.
player = r_w.summoner.by_name(REGION, PLAYER)
id = player['accountId']

# Get player's match list from match data.
match_data = r_w.match.matchlist_by_account(REGION, id, begin_index=INDEX)
match_list = match_data['matches']

# Get time of most recent  match.
# Note: match times are in milliseconds since 1970.
match = match_list[0]
match_time_newest = match['timestamp']

# Print time of each match since first match.
i = 0
for match in match_list:

  print("Match:", INDEX + i)
  
  # Compute how long ago since newest.
  match_time = match['timestamp']
  milliseconds = match_time_newest - match_time
  minutes = milliseconds / (1000 * 60)
  hours = minutes / 60
  days = hours / 24
  years = days / 365

  # Print out times.
  # Note: %d is for integer, %.2f is for real with 2 digits after decimal
  print("  minutes: %d" % minutes) 
  print("  hours: %.2f" % hours)    
  print("  days: %.2f" % days)      

  i += 1


# Done.
exit()

Script - Print all Champions from a player's matches

#!/usr/bin/python3

#
# get-champs.py
# version 1.1
#

########################################
## IMPORTS

from riotwatcher import RiotWatcher
import json
import time

########################################
## SETTINGS
KEY = 'PERSONAL-RIOT-API-KEY_HERE'
REGION = 'na1'   # North America as region
PLAYER = 'Grig1' # Player (Summoner) of interest
MATCH_LIMIT = 2  # Number of matches to analyze (0 for no limit)
INDEX = 0        # Start of matches (will get 100)

print("REGION:", REGION)
print("PLAYER:", PLAYER)
print("INDEX:", INDEX)
print("MATCH_LIMIT:", MATCH_LIMIT)
print("----------------------")

########################################
# SETUP

# Get master RiotWatcher object, using my Riot developer key.
r_w = RiotWatcher(KEY)

# Get player with summoner name.
player = r_w.summoner.by_name(REGION, PLAYER)
id = player['accountId']

########################################
# GET FULL MATCH DATA

# Get player's match list from match data.
match_data = r_w.match.matchlist_by_account(REGION, id, begin_index=INDEX)
match_list = match_data['matches']

# Read in full match data for MATCH_LIMIT matches.
full_match_list = []
i = 0
for match in match_list:
  print("Reading full match %d..." % i)
  m = r_w.match.by_id(REGION, match['gameId'])
  full_match_list.append(m)
  i += 1
  if (i == MATCH_LIMIT):
    break
  time.sleep(1)  # Pause to avoid overwhelming Riot API quota.

print("----------------------")

########################################
# CHAMPS

# Read in static champs list from file.
# Note: instead, this could be done infrequently using
#   r_w.static_data.champions(REGION)
static_champ_list = json.load(open('static-champs.json'))

# Go through each match in list.
i = 0
for match in full_match_list:

  print("Match:", i)

  # Go through each player in match.
  for player in match['participants']:
    id = player['championId']

    # Find and print matching champion name from list.
    for champ in static_champ_list['data']:
      if static_champ_list['data'][champ]['id'] == id:
        print(" ", id, champ)

  i += 1
  
# Done.
exit()

Note: The above script uses the file static-champs.json that you can find in the Hints section.


Part 2 - The Golden Rule

In League of Legends, Gold is available for making Champions more powerful. Champions earn gold regularly over time and also by killing enemy minions, Champions, and neutral monsters. Players use gold to buy items (e.g., weapons and armor) for his/her Champion. However, gold amounts and distributions can vary widely from match to match. What is the role of gold in League of Legends matches?

Exploration: For your chosen summoner, analyze: a) the total amount of gold obtained by all players in each match, and b) the difference between the total gold earned by the winning team minus the losing team.

Hint: The first 5 participants are on Team 1 and the second 5 participants are on Team 2.

Advanced: c) Pick an additional exploration regarding gold. This could be gold for different roles (i.e., Top Lane versus Jungle), gold versus player summoner rank, gold versus duration, gold versus some other in game statistics (examine single-match.json below for details) or some similar aspect of your choosing.

Note: You must analyze at least 100 matches, so make sure your player has that many.

Script - Print gold earned by each Champion for a player's matches

#!/usr/bin/python3

#
# get-gold.py
# version 1.1
#

########################################
## IMPORTS

from riotwatcher import RiotWatcher
import json
import time

########################################
## SETTINGS
KEY = 'PERSONAL-RIOT-API-KEY_HERE'
REGION = 'na1'   # North America as region
PLAYER = 'Grig1' # Player (Summoner) of interest
MATCH_LIMIT = 2  # Number of matches to analyze (0 for no limit)
INDEX = 0        # Start of matches (will get 100)

print("REGION:", REGION)
print("PLAYER:", PLAYER)
print("INDEX:", INDEX)
print("MATCH_LIMIT:", MATCH_LIMIT)
print("----------------------")

########################################
# SETUP

# Get master RiotWatcher object, using my Riot developer key.
r_w = RiotWatcher(KEY)

# Get player with summoner name.
player = r_w.summoner.by_name(REGION, PLAYER)
id = player['accountId']

########################################
# GET FULL MATCH DATA

# Get player's match list from match data.
match_data = r_w.match.matchlist_by_account(REGION, id, begin_index=INDEX)
match_list = match_data['matches']

# Read in full match data for MATCH_LIMIT matches.
full_match_list = []
i = 0
for match in match_list:
  print("Reading full match %d..." % i)
  m = r_w.match.by_id(REGION, match['gameId'])
  full_match_list.append(m)
  i += 1
  if (i == MATCH_LIMIT):
    break
  time.sleep(1)  # Pause to avoid overwhelming Riot API quota.

print("----------------------")

########################################
# GOLD

# Go through each match in list.
i = 0
for match in full_match_list:

  print("Match:", i)

  # Go through each player in match.
  p = 0
  for player in match['participants']:
    gold = player['stats']['goldEarned']
    print("  player", p, ":", gold)
    p += 1

  # Print winner/loser
  print("Team 0:", match['teams'][0]['win'])
  print("Team 1:", match['teams'][1]['win'])

  i += 1
  
# Done.
exit()

Part 3 - Puck Hunt

Play

Participate in the Puck Hunt user study.

Visit:

http://www.tinyurl.com/lagstudy

Grab a timeslot and participate in the study.

Data

Data is batched about once per day and posted on the project page. Here are the cumulative data sets:

Download the latest data set.

Analyze

Prepare to analyze data. The fields are:

  1. Lag - amount of delay added to all mouse actions (in milliseconds)
  2. MinTime - minimum time between target moves (in milliseconds)
  3. Angle - range for target angle change (in degrees)
  4. Rating - user rating (1 - low to 5 - high)

For analysis:

  1. Analyze the Rating versus Lag for all the data (this should be the survey-all.txt file contained in the data set).

  2. Analyze one additional aspect of the data set. Possibilities include:

Note: the data is tab separated instead of comma separated. This can be handled by Excel in a couple of ways: a) Copy and paste the data from text editor (e.g., Wordpad) into Excel, or b) in Excel, use "Data" -> "Get Data" -> "From File" -> "Text/CSV" and use a "tab" as the delimiter.


Hints

An easy way to write ouptut to a file is to "redirect it" from the command line using the '>' (greater than) operator. For example, running:

python parse.py > myfile.txt

will record all the output that normally would go to the terminal screen into the file myfile.txt. Note that any previous content in myfile.txt will be over-written when this is run.

For match data, the Riot APIs return at most 100 matches. If you want to analyze more than 100 matches, you will need to make multiple calls to matchlist_by_account(), changing the begin_index each time, until the number of matches returned is less than 100.

For all Riot API queries, there is a limit to how fast requests can be made. Doing so too rapidly will have the query rejected and an error returned. To prevent this, there should be a pause between requests (the example scripts do this). In Python, this can be done by:

  import time
  ...
  time.sleep(1)  # sleep for 1 second

For reference, here are some json-formatted files may be helpful:


Submission

The assignment is to be submitted electronically via the Instruct Assist Website by 11:59pm on the day due.

The submission is a report in PDF, named proj4_lastname.pdf.

To submit your assignment, log into the Instruct Assist website:

https://ia.wpi.edu/imgd2905/

Use your WPI username and password for access. Visit:

Tools --> File Submission

Select "Project 4" from the dropdown and then "Browse" and select the assignment file (i.e., proj4_lastname.pdf).

Make sure to hit "Upload File" after selecting it!

If successful, there should be a line similar to:

Creator    Upload Time             File Name        Size    Status   Removal
Claypool 2018-04-17 12:19:17  proj4_claypool.pdf  1203 KB  On Time   Delete

Grading

All accomplishments are shown through the report.

Breakdown

Part 1 (Summoner) - 40% :
10% general
10% duration of matches
10% most played Champions
10% in-depth

Part 2 (Gold) - 40% :
10% general
10% total gold
10% gold for win vs. loss
10% additional

Part 3 (Puck Hunt) - 20% :
10% participating in study
5% rating versus lag
5% additional analysis

Rubric

100-90. The submission clearly exceeds requirements. All parts of the project have been completed or nearly completed. The report is clearly organized and well-written, charts and tables are clearly labeled and described and messages provided about each part of the analysis.

89-80. The submission meets requirements. The first 3 parts of the project have been completed or nearly completed, but perhaps not completely. The report is organized and well-written, charts and tables are labeled and described and messages provided about most of the analysis.

79-70. The submission barely meets requirements. The first 2 parts of the project have been completed or nearly completed, but perhaps not part 3. The report is semi-organized and semi-well-written, charts and tables are somewhat labeled and described, but parts may be missing. Messages are not always clearly provided for the analysis.

69-60. The project fails to meet requirements in some places. Not all parts of the project are completed. The report is not well-organized nor well-written, charts and tables are not labeled or may be missing. Messages are not always provided for the analysis.

59-0. The project does not meet requirements. No part of the project has been completed. The report is not well-organized nor well-written, charts and tables are not labeled and/or are missing. Messages are not consistently provided for the analysis.

Postmortem Feedback on Graded Projects

The comments below are in response to graded projects. They are not provided in any particular order.

Hannah J.
Allison S.


Top | Part 0 | Part 1 | Part 2 | Part 3 | Writeup | Hints | Submit | Grade

Return to the IMGD 2905 home page

Questions: imgd2905 question-answer forum