Tuesday, February 6, 2018

100 Thieves vs TSM post game analysis - Week 3 NA LCS

Want to get better at strategy analysis so going to try to do one of these a week for a while. Figured I'd start with a 100 Thieves game since we just bought them. Also Hauntzer vs Ssumday is a powerhouse top lane matchup!

Here's the game VOD if you'd like to follow along at home:


Post game stats:
https://matchhistory.na.leagueoflegends.com/en/#match-details/TRLH1/1002450079?gameHash=83d7ba716294292c&tab=overview

The Draft:

First Bans - Nothing too crazy. 100 Thieves targets Bjergsen's pool with Azir/Galio, and takes away Hauntzer's Camille. TSM likes these champs that can hold people in place for key abilities. Think Camille ultimate into Gallio ultimate with Azir Shurima-shuffling champs into that wombo.

TSM takes away Ryu's Ryze (he's been playing really well on it), Zoe's just annoying to play against with the long range cc + poke. Not sure what the Corki ban is from? Haven't seen Ryu or Cody Sun play that champ yet. Might be showing that TSM wants something like a Kassadin and doesn't want to get bullied in lane by a ranged champ early?

First Picks - 100T locks in Kog'maw for ADC first pick. High priority pick since this champ is arguably the best ADC right now, and does WAY too much damage with 2+ items. Kog'maw means 100T want a long game and lots of farming early.

TSM counters with Sejuani (probably jungle) and Taliyah (probably mid). Champs with pick and burst potential to isolate and quickly kill a fed Kog'maw. This leaves champs like Lulu or Karma up which Kog'Maw loves for the shields/sustain/crowd-control and extra stats from their support itemization.

Second Picks - 100T locks in Jarvan IV in the jungle and Alistar in the support role. They have a good frontline for Kog'Maw that can make picks or and control the area around Kog'Maw. Alistar pick is a melee champ and they may be trying to limit the melee support pool of TSM since you usually want 2-3 melee champs to stack her Permafrost stun. No shields for Kog'Maw, which is interesting, but maybe they run something like Lulu mid/top?

TSM locks in Gangplank (probably top). Melee champ for Sejuani, and another damage threat. Both Kog'Maw and Gangplank have late game power spikes after a lot of farm, so TSM probably trying to equal damage curves. If Jarvan IV ults a target, Gangplank can drop his ultimate on the same area to stop 100T from following up. This will also pull Meteos's J4 top early/mid game to stop Gangplank from farming, leaving Kog'Maw susceptible to early dives bot.

Second Bans - TSM still has to pick their botlane, so 100T uses their bans to target those picks. Braum is one of the best supports to play with Sejuani (melee + extra crowd control), and Tristana can jump out of Jarvan IV ult with her E, and can make picks with her Ult potentially repositioning Kog'Maw into unfavorable territory. Smart bans.

100T is yet to pick their solo lanes. Looks like TSM target bans Ssumday's ranged top laners so that he'll have a hard time bullying Gangplank in lane and stopping him from farming. The Gnar ban is especially smart since it denies 100T the Gnarvan combo (J4 ultimate into Gnar ultimate AOE stun).

Third Picks - TSM locks in Ornn for support. Melee champ that synergizes well with Sejuani's stun passive, and has abilities that elongate crowd control durations. Also, Braum is banned, which is usually a good Ornn counter (Ornn ults, Braum uses his shield to block/cancel the initial Ornn Ram). Granted 100T has already locked Alistar, but still!

100T locks in Twisted Fate (probably mid) and Vladmir (probably top). They are heavy on the magic damage (Kog'Maw does a lot of magic damage for an ADC), and don't have a good front line for keeping Kog'Maw safe. Aphromoo's Alistar is going to have to put in some WORK to keep his carry safe. 100T is probably opting for a 1 3 1 type comp, with Vladimir and Twisted Fate split pushing. Vlad can join fights with Teleport, TF can use his ultimate to join fights or make picks off of bad rotations. Vladimir can sustain through Gangplank's harass and can use his pool to dodge a lot of scary abilities (Sejuani/Ornn stuns). Vladmir's ultimate will also amp Kog'Maw's damage in team fights if they can hit the same targets.

Final Pick - TSM locks Varus for ADC. Their team comp is looking to initiate with Orrn Ultimate, into Varus ultimate, into Sejuani crowd control, with a Gangplank ultimate and Taliyah damage on top of everything. They have pick potential with all champs (Gangplank can always just press R to help) and can deal with split pushes or a 5v5. I really like TSM's comp and think they're better off.

100T is looking to 1 3 1 and catch TSM in bad rotations to take objectives. Once Kog'Maw gets a few items, they may look to team fight, but TSM's 5v5 is better and should be able to zone Kog'Maw out from doing damage. 100T is looking to get strong early (probably through midlane) and roam to take early towers with TF, opening up the map for their split push. This doesn't give Kog'Maw time to farm safely, but TSM's team comp is just better, so they're looking to end quickly.

Early Game:


Start - Things to notice! TSM taking teleport and cleanse on their solo lanes vs 100T 2x ignites. 100T wants to win early! Botlane both supports have ignite. This is going to be an aggressive early game! Both teams fan out and guard their respective jungle entrances. No cheesy 5man invades, or risks for deep vision. Laning is where the aggression will come out. TSM knows they win if the game stalls out, so the impetus is on 100T to make something happen.

Jungle Starts - Both 100T and TSM start their bottom buff. Notice that they're not doing wolves or raptors for the extra experience (kill big monster, leave littles, clear after another big monster is cleared for 50 extra exp). This means both junglers are looking to hit level 3 fast, and looking to control top/mid. J4 is much stronger early game compared to Sejuani, and can do cheesy things like ganking level 2 with red buff mid/bot lane for some unexpected aggression. Sejuani is looking to farm and get level 6 (when she becomes stronger than J4), and match or counter gank until then.

3:25 - Not a fan of Meteos's clear pattern. He doesn't use his powerspike at level 2, or 3. And is behind Sejuani by a camp and lacking control (look at the scuttles and wards on the bottom half of the jungle). Gangplank's lane is ungankable and he is able to farm (what he wants to do), and botlane is perfect for Kog'Maw farming. Looks like Meteos is content to powerfarm jungle and wait for TF to hit 6 for ganks? One thing that's nice is that TSM doesn't have a good idea where Meteos is on the map due to his weird clear and not being spotted out by wards. But again, this strategy isn't the greatest since TSM's comp will outscale 100T, so doesn't fit the "win early" strategy.

No aggression yet, TSM with slight creep score advantages in every lane + jungle.

3:53 - AYYYYYY I was wrong! Very clean play by Aphromoo to setup first blood and blow Varus Heal. Surprised that worked, but TSM did not respect the engage. Alistar can cancel his knock up animation with flash, so you pretty much need to predict that engage to dodge on champs with no crowd control or dodges to interrupt. Notice that Kog'Maw used his heal offensively to speed up both Alistar and himself. Think this was overkill and unnecessary, but still, nice kill, good gank, and gold onto Kog'Maw plus the extra waves of creeps to farm!

5:43 - Twisted Fate hits 6. This is when 100T's team comp should start making plays across the map. Expect Ryu to shove waves and roam to side lanes with his ults. At 6mins, the stopwatch rune activates and everyone but Sejuani gets a free Zhonya's active. So dives will be tricky. That rune can be used offensively to juggle tower aggro, or defensively to deny dives or that last tick of ignite. Notice the double control wards on the bottom side of the map. They know TF wants to roam bot and are putting those there to spot him out!

TSM is ahead in gold for all lanes except botlane. So time for 100T to start making plays before they get outscaled.


9:23 - Nothing happens for a while. No ganks, no roams with TF ultimate, just farming. TSM makes a good play on the Ocean dragon when the 100T botlane backs. Mike Yeung blows Kog'Maw flash with Sejuani ultimate, but then gets picked off by an Alistar/J4/TF wombo when overextended trying to clear a ward. Nothing in the area objectivewise for 100T to capitalize on. 100T are up a slight amount of gold from the early kills bot. Farming continues, which again, is what TSM wants since their team composition is better. Interesting that TSM did not go for ganks mid against TF. Usually champs with no dash or escape are an easy burst combo for Taliyah. Maybe a missed opportunity to gank the global?

13:57 - Good wards on the TSM blue jungle reveal Sejuani backing, which lets J4 pick up a free Rift Herald. He's seen from a river ward, but TF uses ultimate to scare off enemy team, and Ssumday teleports in (spellbook rune changed from ignite) for extra muscle to scare off Sejuani's steal attempt.

15:25 - Four man roam bot scares away Alistar/Kog'Maw, allowing TSM to pick up the first cement bonus from killing the first tower of the game. Meteos sees this happening and summons the rift herald top to counter with a tower and a half worth of damage. Hauntzer's Gangplank backs off respecting Vlad+J4's kill potential. TSM has 100T's red jungle lit up with wards, and is likely looking to control the 2nd Ocean dragon after everyone recalls to spend their gold.

16:47 - TSM's vision control pays off and they're reward with an uncontested Ocean dragon (their second of the game). All TSM carries are happy they can spam abilities on cooldown. A Mountain Dragon is spawning next in 7mins. Both teams will likely prioritize this objective.

17:47 - Ryu's TF makes a nice pick on Zven's Varus bot, blowing both Varus summoner spells. Ssumday makes a rare mechanical error flashing after Varus (Didn't lead with his ult), and dies to the remaining members of TSM collapsing around him. This is a mistake from 100T who need to take the blown summoners and back off instead of forcing things (but the playmaking is understandable since they're up against the clock to end the game early). Cody Sum's Kog'Maw overextends toplane and is picked off by the Hantzer's Gangplank, blowing Kog'Maw stopwatch and heal. Both teams making some positional mistakes.



20:00 - Nothing fancy happens. Remaining side lane outer towers are traded. J4 burns Gangplank's flash with his ultimate. Gold is in TSM's favor by ~1k plus the 2x Ocean Dragons. TSM is itemizing heavily into magic resist items, and is in a good place, scaling into their superior team comp mid-late game.

100T begins their 1 3 1 with Vladmir and TF in the sidelanes, matched by Taliyah and Gangplank respectively. Really like those TSM picks since Taliyah can use her ultimate to follow TF, and Gangplank can click R to help if his teammates get engaged on. Hauntzer probably could have itemized into an executioner's calling (800g item) for the grevious wounds passive to negate Vladmir's sustain. Instead he opted to finish core items and farm waves back and forth with Ssumday (who just heals off any chip damage).

Both teams have vision around Baron Nashor. TSM will likely wait until the Earth Dragon spawns, and force 100T to choose between objectives. Both teams have global ultimates, teleports, and can move across the map very quickly.

24:07 - TSM controls the dragon pit, and secures the Earth Dragon as 100T watches helplessly. Next Dragon will be a 3rd Ocean.

TSM's team composition is starting to look far superior and 100T can't engage 5v5 without suffering heavy casualties. Hard for 100T to split since TSM's champs can easily match the pressure, and still contribute in team fights with global ultimates. Next objective is likely Baron or mid outer tower for TSM who is in complete control. 100T is looking for TSM to make a positional mistake, and pick someone off, or get an AMAZING team fight where Kog'Maw survives long enough to kill everyone on TSM. Which will be tough with their team comp.

26:17 - Here's an example of the team comp difference. TSM executes this very well. 100T cannot win a sustained all in, and must rely on picking off members of TSM that aren't grouped. 100T is now down a teleport, which TSM can abuse to gain control over the Baron pit.

28:00 - TSM patiently and methodically pushes down the 100T mid outer turret. Here the 2x Ocean Dragons are starting to really hurt 100T. TSM can continue trading, and then regain health and mana after a few seconds, while 100T are slowly getting whittle down. Sejuani continues to fish for Kog'Maw with her ult, looking to blow summoner spells or his QSS. TSM is building this pressure while their top wave crashes into the 100T inner tower. A 100T botlane wave is slow pushing which will need to be cleared by TSM in a minute or two (worst case Gangplank can always use his ultimate on it). The Baron dance is about to begin, with TSM in a superior position.



30:18 - 100T blows Varus flash, and then looks to bait a fight at Baron. They are down 3.7k gold, 4 dragons, and their team composition is far inferior to TSM. They're looking to get Baron and force a powerplay before TSM completely outscales them. Remember, TSM has Ornn items as well, so they WILL be stronger if the game goes full builds.

Meteos throws the game at ~31mins. Not sure what he was looking to do with the engage minus follow up from Kog'Maw and Ssumday putting pressure bot. Wasn't looking good for 100T anyway, but this did not help.

34:22 - TSM is in complete control and takes two towers, an inhibitor, and the two nexus towers after Meteos gets caught out of position. Ryu takes the TSM outer mid tower as a parting gift, and Kog'Maw is able to get a few kills, but now it is only a matter of time before 100T loses. TSM can wait for super minions to build pressure bottom, and control the Elder Dragon with their 4-0 Dragon lead, then end the game with one final push.

35:52 - TSM sets up for the Elder Dragon. Notice their top wave. It is setup to slow push and build pressure exactly when the Elder Dragon spawns. That pressure, plus the super minions pushing in the bottom lane, should allow TSM to ward up the 100T Red jungle, and control the Elder Dragon. Very well played by TSM.

38:29 - TSM controls the Elder Dragon and heads to Baron. Good patience by TSM to wait for an engage until the big wave top was taking an outer turret. This spread 100T too thin, and allowed for a TSM pick and control of the Elder Dragon. Now an easy rotation to Baron, and end the game from the bot lane for TSM.

41:05 - GGWP TSM wins.

Post Mortem:


What could 100 Thieves done better?

1. They got severely outdrafted. This is the main reason 100T lost. Kog'Maw is a huge powerpick, but needs a frontline or shielding supports to allow him to do the damage. 100T had J4 and Alistar to frontline, but TSM had too much crowd control and zoning abilities for Kog'Maw to get close enough to do damage before his frontline melted. TSM had answers to the 100T 1 3 1 split push comp, and a better 5v5 teamfight. Plus, 100T was mostly magic damage with a tank Jarvan IV out of the jungle. Made it easy for TSM to itemize magic resist items to make team fights and picks tougher to pull off. Ugly draft for 100T, very well played by TSM!

2. 100 Thieves needed to make more happen early to force a win before their team comp got outscaled. The early Alistar -> J4 gank bot was beautiful! They needed to do that more often and punish TSM every time a flash was down. Very surprised Meteos didn't camp the Taliyah mid since J4+TF have more damage than Taliyah + Sejuani. TF gold card is a free J4 E-Q combo, with his ultimate following the Taliyah flash. Maybe scared because of her cleanse? But you can't cleanse the J4 ultimate...

Ssumday could have played something with more damage top to put more pressure on the Gangplank, but TSM banned his two practiced damage dealers. Vladimir auto pushes the lane with his area of effect abilities, so easy for Gangplank to farm safely under tower making it tough for Meteos to make plays.

3. Meteos needs to stop doing bad engages late game. This is a bad habit that repeats itself in most 100T games. Meteos will engage without his team, or without waiting for waves to create enough pressure. He's been lucky a few times where his team has still gotten the W, but it completely threw the game to TSM in this game.


4. Better Dragon Control. Giving up the early dragons for free, made TSM's carries and siege a nightmare to deal with later. It also made Elder Dragon an extremely important objective since TSM was able to stack 4 Dragons early.

5. Better wave management. 100T could have used minion wave mechanics to create pressure and force TSM to split up, giving them an advantage. Granted Gangplank or Taliyah can both use their ultimates to get back into the team fight when they go to fix the waves. Again, the draft was brutal for 100T, if they fix that, a lot of these problems go away.


This was a fun game to watch and I've enjoyed watching 100T get better. Fun watching a new team hold their own against these legacy power houses. Still very early in the season and a lot of time to improve and fix mistakes!

Let me know if this is interesting on Twitter or Twitch! Will try to do more of these if people are into them. Thanks for reading!



























Friday, June 30, 2017

Code For Twitch Chat Bot

Stole Modified some fantastic code I found on the internet to build a python bot to deal with some unsavory characters on my twitch channel. Some scumbag was making rape threats against my family members, so me or my mods would ban them. Problem was, they would just fire up new twitch accounts and continue the barrage. So, being lazy, I built a bot that can be turned on/off, that would look for brand new accounts, and automatically time them out of chat for exactly one day. That way, they would still watch my channel and boost my twitch numbers, but they would do so in glorious silence.

Also my bot is called DavidSPumpkinsBot and will type "Any questions???" as it does the things.
(It's the little stuff, right?)

https://www.youtube.com/watch?v=rS00xWnqwvI

Posting the Python code here (minus my authentication codes, for obvious reasons), so my friend Das Ranger can build his own! Enjoy!

Python code:

#! TwitchBotBusterCode
# -*- coding: utf-8 -*-

from __future__ import print_function
import socket
import time
import re
import requests
import json

# The twitch user which will be acting as your bot
chat_user = 'davidspumpkinsbot'

# The oauth password for the twitch user which will be operating
# Obtain this value by visiting this page while logged in as your bot user: http://www.twitchapps.com/tmi/
#example:
chat_pass = '<PUT YOUR CHANNELS CODE HERE>'

# The channel the bot will be operating in (your channel's name)
chat_chan = 'gundaymonday'

# Punishment method; change to timeout (or any word other than ban) to make it timeout instead of ban
punishment = 'timeout'

# If using timeout punishment, duration of the timeout
timeout_duration = 3600

# Twitch IRC server info: the host and port should not need to be changed
chat_host = "irc.chat.twitch.tv"
chat_port = 6667

# List of commands which use regular expressions. Only change the left side and make sure to leave the ^
commands = {
    '^!startbans': 'start_banning',
    '^!blacklist': 'blacklist_user',
    '^!bld': 'blacklist_date',
    '^!unlist' : 'unlist_date',
    '^!whitelist': 'whitelist_user',
    '^!stopbans': 'stop_banning',
    }

# Initial IRC socket connection and authentication
s = socket.socket()
s.connect((chat_host, chat_port))
s.send("PASS {}\r\n".format(chat_pass).encode("utf-8"))
s.send("NICK {}\r\n".format(chat_user).encode("utf-8"))
s.send("CAP REQ :twitch.tv/membership\r\n".encode("utf-8"))
s.send("JOIN #{}\r\n".format(chat_chan).encode("utf-8"))

mitigation_active = 0


# Function for threaded asynchronous functions decorator @async
def async(func):
    from functools import wraps
 
    @wraps(func)
    def async_func(*args, **kwargs):
        from threading import Thread
        f = Thread(target = func, args = args, kwargs = kwargs)
        f.start()
        return
    return async_func

 
# Sends messages to chat
def chat(sock, msg):
    sock.send(bytes('PRIVMSG #%s :%s\r\n' % (chat_chan, msg), 'UTF-8'))

 
# Permabans users
def ban(sock, user):
    chat(sock, "/ban {}".format(user))

# Unbans users
def unban(sock, user):
    chat(sock, "/unban {}".format(user))

 
# Times users out
def timeout(sock, user, secs):
    chat(sock, "/timeout {} {}".format(user, secs))

# Punishes chatter with specified method
def punish(chatter):
    if punishment == 'ban':
        ban(s, chatter)
        banned_users.append(chatter)
        print('Banning {}'.format(chatter))
 
    else:
        timeout(s, chatter, timeout_duration)
        print('Timing Out {}'.format(chatter))

# Gets list of chatters and updates admin list
def get_chatters():
    # Loop ends when a value is returned
    global admin_list
    while 1:
        chatter_list = ()
        admins = ()
        # Uses try because sometimes the connection times out
        try:
            r = requests.get('https://tmi.twitch.tv/group/user/{}/chatters'.format(chat_chan))
        except:
            time.sleep(1)
            continue
     
        if r.status_code == 200:
            r = json.loads(r.text)
            # Builds the list of chatters
            for chatter in r['chatters']['viewers']:
                chatter_list += (chatter,)
         
            # Dynamic moderators list, kinda hackish but if you unmod someone they will be removed on the next loop
            for moderator in r['chatters']['moderators']:
                admins += (moderator,)
         
            admin_list = admins
            return chatter_list
        time.sleep(1)

     
# Function for returning the user's creation date
def creation_date(user):
    # Loop ends when a value is returned
    while 1:
        # Uses try in case of request timeout
        try:
            r = requests.get('https://api.twitch.tv/kraken/users/{}'.format(user))
        except:
            time.sleep(1)
            continue

        if r.status_code == 200:
            # Captures only YYYY-MM-DD
            date = re.match(
                '([\d]{4}-[\d]{2}-[\d]{2})',
                json.loads(r.text)['created_at']
            )
            return date.group(1)
 

# Thread for watching and banning chatters
@async
def watch_chatters():
    global chatter_list
 
    while 1:
        # Gets the list of chatters in the room, needs to run while mitigation is off
        chatter_list = get_chatters()
     
        if mitigation_active:
            for chatter in chatter_list:
                if chatter in banned_users:
                    continue
             
                if chatter in whitelisted_users:
                    continue
                 
                if creation_date(chatter) in banned_dates:
                    if punishment == 'ban':
                        ban(s, chatter)
                        banned_users.append(chatter)
                    else:
                        timeout(s, chatter, 3600)

                    print('Current number of banned users: {!s}'.format(len(banned_users)))
        time.sleep(15)
 

def process_chat(chat_object):
    user = chat_object.group(1)
    msg = chat_object.group(2)
    # Initialize variables
    global banned_users,whitelisted_users,mitigation_active,banned_dates
    banned_users = []
    whitelisted_users = []

    banned_dates = []

    #Processes possible commands from the command dictionary
    for command,action in commands.items():
        if re.search(command, msg):
            #manual blacklisting of dates, uses GMT always
            if action == 'blacklist_date' and user in admin_list:
                input = re.search('^'+command+'\s+([\d]{4}-[\d]{2}-[\d]{2})', msg)
                if input:
                    #adds date to the banned list
                    banned_dates.append(input.group(1))
                else:
                    chat(s, 'Error: invalid format. Use !bld YYYY-MM-DD')
                break
         
            # Manual unlisting of a date, can also !stopbans to clear the list
            elif action == 'unlist_date' and user in admin_list:
                input = re.search('^'+command+'\s+([\d]{4}-[\d]{2}-[\d]{2})', msg)
                if input:
                    banned_dates.remove(input.group(1))
                else:
                    chat(s, 'Error: invalid format. !unlist YYYY-MM-DD')
                break

            #Automatic lookup of a user's creation date blacklisting it.
            elif action == 'blacklist_user' and user in admin_list:
                target = re.search('^'+command+'\s*@?([\w\-]+)', msg)
                if target:
                    #Makes sure the user is actually in the room to prevent erroneous bans.
                    if target.group(1).lower() in chatter_list:
                        user_date = creation_date(target.group(1).lower())
                        chat(s, 'Blacklisted: {}'.format(user_date))
                        banned_dates.append(user_date)
                    else:
                        chat(s, 'User {} not found in the room. Check spelling or try again in 15 seconds.'.format(target.group(1)))
                break

            #Stops bans and clears blacklist
            elif action == 'stop_banning' and user in admin_list:
                mitigation_active = 0
                banned_dates = []
                chat(s, 'Turning off chat mitigation.')
                break

            #Starts banning users
            elif action == 'start_banning' and user in admin_list:
                mitigation_active = 1
                chat(s, 'Turning on chat mitigation ═══█❚')
                break

            #unbans and prevents future banning of the user
            elif action == 'whitelist_user' and user in admin_list:
                target = re.search('^'+command+'\s*@?([\w\-]+)', msg)
                if target:
                    chat(s, 'Whitelisting user: {}'.format(target.group(1)))
                    whitelisted_users.append(target.group(1).lower())
                    unban(s, target.group(1).lower())
                break
 
 
# Thread for reading chat and watching for user commands
@async
def read_chat():  
    # Starts infinite loop listening to the IRC server
    while True:
        response = s.recv(1024).decode("utf-8")
     
        #PONG replies to keep the connection alive
        if response == "PING :tmi.twitch.tv\r\n":
            s.send("PONG :tmi.twitch.tv\r\n".encode("utf-8"))
            continue

        #separates user and message.
        chat_object = re.search(r'^:(\w+)![^:]+:(.*)$', response)
        if chat_object:
            process_chat(chat_object)
                         
     
if __name__ == '__main__':
    chat(s, 'Any questions???')
    read_chat()
    watch_chatters()

Thursday, November 12, 2015

Nukulibrium Art Assets



Concept art for a potential game I'm working on. Sketches by Jody Osentoski, tech tree by Joe Hopkins.

I'm imagining the intro/hype video to have the Death God throw a necrotic looking spear, only to have the Life Goddess wave her hand and turn it into a spray of flowers and a dove. The dove then flies up only to be struck by lightening and turned into a drumstick, which the God of Fried Chicken then catches and chows down on. Zoom out to game title, tap anywhere to begin playing.

Waximus, the God of Death - Rough Character Sketch


Waynia, the Goddes of Life - Rough Character Sketch

Drumstick, the God of Fried Chicken - Rough Character Sketch

Another idea for Drumstick.



Example objective tree of things the gods would need to help humanity build to colonize Mars.

Tuesday, May 19, 2015

Bad Experiences by Design


Bad design, well, sucks.

We live and play in a designed world. The screen you're reading, the seat you're sitting in, the electricity you're consuming is all there, for better or worse, by someone's design.

I can count the number of times these have been used on one hand.

Which is why it's important to study design, both the good and the bad, to understand how to build better systems. Thinking through systemic design is important. Without foresight, you can leave your users frustrated and extremely vocal about the system's shortcomings.


So how do you avoid a design problem like this? Well, a simple trick is to put yourself into the shoes of a future user. If they knew nothing about the design, would they understand what to do at a glance?

Uh...

Another tactic is to test drive your design before it hits the big stage. Jesse Schell recommends using kindergartners. If they get your design, then everyone else will probably understand too. They're also pretty brutal with their feedback and hold nothing back. You'll recognize a problem very quickly when a 5 year old is tearing your design a new one.

Any door with an instruction manual is a design failure.

Design is like a joke, it's not very good if you have to explain it. How many times have you pulled on a door with a "Push" sign above the pull handle? This design flaw is EVERYWHERE! There are books with entire chapters dedicated to badly designed door handles.

It's pretty clear you have to push this, hence no "PUSH" sign.

Riot Games recently made a frustrating design decision with their League Point (LP) system. When you win a match of League of Legends, you gain LP. When you lose a match, you lose LP. Win enough, and you are promoted to a new shiny tier with a new shiny badge. Lose enough, and the opposite is true.



Sounds great right? A clear indicator of how well you're doing and if you're getting better or worse at the game! Well, sometimes.

Riot decided to add in an extra tier just above diamond (where the top 1-2% of the playerbase plays) called Master Tier. When they did this, they made it so players newly entering the Diamond tier could not gain or lose LP in a regular fashion. Essentially, you would win a game, get +10 LP, lose a game, get -30 LP. So a win and a loss was one step forward 3 steps back. Check out what it did to the Diamond player distribution:

Click the picture to make it bigger. The player count in Diamond V is the interesting part.

Currently ~73% of Diamond Players are stuck in Diamond V. This is significantly higher than any other tier's V distribution (For example, it's only ~42% for the Gold Tier).

This has left a lot of the top players frustrated. The system that's supposed to tell you if you're improving or playing worse is automatically set to tell you that you do not belong (regardless of how you're playing). The reddit rage is pretty hilarious to read.

That frustration is translating into gameplay. The top 1-2% of the playerbase is now trapped in the most toxic part of the player distribution. You have a mix of the frustrated players desperately trying to get to Diamond mixed in with the chill players that got Diamond, but don't care anymore and are learning new champions. Add to this a dash of tryhard rage stuck in a one step forward, three steps back cycle, and you get a play experience where every match is pretty angry. That anger is sucking the fun out of the game.



A system designed to give feedback on performance, now has a choke point that does the opposite. This has created a ton of frustration which is translating into toxic gameplay (people raging at one another, intentionally throwing games, quitting the games early). Riot Lyte recently gave a talk at GDC showing that this kind of toxicity makes a player 320% more likely to stop playing a game.

http://www.gdcvault.com/play/1022160/More-Science-Behind-Shaping-Player
They probably want to fix this since their most diehard players are getting hit with this systemic toxicity. Lyte also has the solution in his talk when he says that "Clear feedback is everything." This would all be fixed if the LP system did it's job, and told players how they were performing. Fix that, fix the toxicity, everyone's happy. Hopefully Riot realizes this soon and saves the day!

Don't be a victim of bad design.
So when you're designing the next big thing, take a step back and look at it with fresh eyes.

Is it easy to understand?
Is it working as intended?
Can a 5 year old get it?

A little foresight goes a long way.

Tuesday, January 20, 2015

Sejuani Mathcrafting for Patch 5.1 (League of Legends)

Tomorrow Season 5 of League of Legends begins. This means that the ranked ladders will reset and and everyone will need to climb back up to the top! Generally Riot will do something like:

(Your preseason MMR + 1200) / 2 = Your new Season 5 MMR

Worldwide League of Legends player distribution.
Compliments of http://loldb.gameguyz.com/statistics/league

1200 MMR is the average league of legends player. So Riot will do a soft reset pulling everyone towards the average. This means that I get to be matched with and against lower MMR players, who will probably make more mistakes than what I'm used to. To compensate (and win games doing it), I did a little math to figure out a solid build/strategy for early game dominance.

(One of the top ADC players playing against the very bottom of the playerbase)

Against less skilled players, games are often decided in the first 20 mins of the game. If you can apply enough pressure or demoralize the enemy team, they'll often surrender at this point. My favorite champion is strongest late game (~30-40 mins), so I need to adapt my playstyle/build for the start of the new season. Here's my plan (math included) for doing this.

How to win the early game

Have you ever heard of a player named Ryan Choi? He is one of the best Rengar players in the world. The reason I mention Ryan, is that he made popular an interesting strategy to mitigate early game statistical weaknesses on his favorite champion. He stacks Doran's items (items that are cheap, give very good stats per gold spent, but don't build into stronger items) until he has enough gold to afford his more expensive late game build. Here's an example of how it works:


My favorite champion is Sejuani. She is a late game monster tank that becomes pretty damn unstoppable as the game goes on. But like I already mentioned, most games are decided in the first 20 mins, so I need to tweak my starting item build path (much like what Ryan Choi does) to compensate for my weak early game. Here's the item that's going to win me games tomorrow:


Here's why. Sejuani's damage scales with her maximum health, and ability power. It usually takes a while for her to buy enough items to be really scary though. Early game, she has trouble staying out on the map due to running low on mana (fuel for her abilities that do damage). Stacking Doran's rings gives her extra damage, and extra mana to compensate for her early game statistical weaknesses. Here's some math comparing what we could buy when we recall ~7 mins into the game ~level 5 with ~1600 gold (We should already have our upgraded jungle item).

What would you want to buy with ~1600g, to be strongest in the next 5 mins of the game?
The idea here, is to get our upgraded machete as quickly as possible since it gives us 30g per large monster kill and a bunch of extra damage/sustain. On our next buy, instead of buying REAL items, you grab 3-4 doran's rings. BECAUSE THEY GIVE US MORE STATS than real items at this point in the game!!! We will have more damage, and due to the mana regen and passive (free mana per unit kill), we should never run low on mana in the jungle. We'll also be using some % max health seals/quints to make the health worth a little more as well (we already talked about why this is smart in a past post). If we buy 4, we're spending 1600g. We can sell these back for 640g, so we're really only down 960g. If we can farm a few extra camps, get a kill or two, or win a tower/dragon after this buy, the Doran's rings pay for themselves.

(The power of Doran in a championship game)

As the game progresses, we'll sell off our Doran's rings to make room for better items. Hopefully we will have already made a bigger early game impact by the time that happens. The season resets tomorrow (1/21/2015), so feel free to tune into my stream on Twitch if you'd like to watch and see for yourself:

Watch live video from GundayMonday on www.twitch.tv

Thursday, January 15, 2015

Resolutions

New year, new goals.

Over the past year I focused a lot on my job at Quicken Loans. Would spend weekends building my coding chops (I am now a SQL deity) and learned how to do a lot of cool stuff. But I didn't get a chance to do a lot of the things I used to love. This year I want to fix that.

Got over my fear of public speaking in this band. Also my fear of everything.

Music has always been a big driving force in my life. Nothing is better than a great song. I admire those that create masterworks capable of shaping emotion from sound. My heroes are those that conjure the obscure, the triumphant, and the different kinds of tunes that make feet tap and smiles form.

(My favorite song)

I used to make music every day. Last year, maybe a month or two? And I only finished two songs.


This year, I want to change that. I want to create a full album that I can really be proud of. I want to take my time and really do it right. Spending an immense amount of time focusing on tweaking the little things to make it perfect. Don't even care if anyone else likes or listens, this one's for me.

So that's resolution #1, make an entire album of music that I can be proud of.

Next resolution's a little nerdier. I'm a pretty good League of Legends player. I'm the best Sejuani (Pig riding champion) in North America currently, and am in the top 10k players out of the entire continent (Just hit D3 ranking last night!). That's pretty good for a game with a multi-million playerbase. I want to do better though.



Riot recently created a new Master Tier to hold the absolute elite of the competitive player base. I want to break into it. I want to do it my way too. Most of the players up there use a specific playstyle that specializes in killing other players (they carry bad teammates this way). I want to break into the top tier by playing tanks and supports (champs that specialize in making teammates better). I'm already pretty close, and I think it's doable, but there aren't many tank/support players currently in the top 0.01% of the player base. I want to be one of the first.

Resolution #2 is to break into Master Tier for my favorite game while still having fun and playing it my way.

3rd and final resolution coming up (Wanted to keep it doable!).

Watch live video from GundayMonday on www.twitch.tv
Recently built my own computer from scratch, and started streaming on Twitch. This is honestly one of the most fun things I've ever done. After ~2 months, I have some quality individuals hanging out in my chat watching me play. Which is super weird! Complete strangers are looking at me while I play video games and sometimes posting penis emoji (seriously, what do you think will happen if you click that link?) all over my stream! But it's awesome and SO MUCH FUN.

I want to get better at streaming. I am going to arbitrarily validate my success via my follower count. Right now I have 58. I would like to shoot for 1000 by the end of the year. I think that's doable if I keep at it, but definitely a tough goal to have (wouldn't be fun if it were easy). Plus, always nice to make new internet pals!

So there you go. Resolutions for the year. Wish me luck!

Monday, December 29, 2014

How To Deal With Toxicity in League of Legends


What is Toxicity?

Toxicity is any in-game behavior that negatively affects other players. That means your average toxic player could be doing anything from intentionally throwing the game, to simply being a jerk in chat. Here are a few quick examples from the League of Legends tribunal, as read by Stephen Hawking:



http://forums.na.leagueoflegends.com/board/showthread.php?p=37108896#37108896
The phrase "toxic" was carefully chosen, since players exposed to toxic behavior, will often become toxic themselves. This can lead to a huge positive feedback loop (for negative activity) where the toxicity flame can literally (and figuratively) rage out of control. That is, unless you stop it early.


More often than not, the toxic player is simply upset and not trying to be a complete scumbag. In this post, we'll go into detail about how to defuse toxic situations and optimally work with difficult teammates. Now trolls do exist, and we won't always be able to stop everyone, but more often than not these tactics work. The same methods we talk about are used by hostage negotiators and behavioral psychologists, so feel free to use them outside of video games (I know I do every day!).

Why you should care about this

Dealing with toxicity is a pretty common problem that most players feel powerless about. This is the wrong way to think about it. If Riot can influence how players behave by changing the color of a font, then we can definitely change the behavior of our teammates with our words and actions.

I'm not playing games with you. You're playing games with ME.

Here's a thought experiment showing why this is useful. Riot recently mentioned that 95% of active players in 2014 never received a punishment (for behavior) of any kind. Which is great! Most players you play with aren't scumbags! But if you do some math...

95% of players never punished for toxicity.
That means 5% of players were punished for toxicity.
5% means 1/20 players were toxic in 2014.
With 10 players per game of league of legends, then 50% of your games have a toxic player.
With 5 players per team, then 25% of the time the toxic player is on your team!


We're going to run into difficult players in 1/4 of our games. I'd be willing to bet that the 1/20 ratio holds up when applied to real life as well. Definitely worth knowing how to deal with toxic people since they're all around us.

Okay, so how do we deal with toxicity?

Most of this I'm going to paraphrase from Eric Barker's very excellent blog (which I highly recommend). I've applied his ideas to a team setting with a League of Legends focus. Here's how you deal with a toxic player.


Step 1: Keep calm. 

One person is freaking out and going toxic. Two people freaking out is even worse. Dr. Albert J Bernstein refers to the toxic state as "dinosaur brain", which is very fitting:

…the basic idea is that in many situations, you’re reacting with instincts programmed into your dinosaur brain, rather than thinking through a situation. If you’re in your dinosaur brain, you’re going to play out a 6 million-year-old program, and nothing good is going to happen. In that case, the dinosaur brain of the other person is going to understand that they are being attacked, and then you’re responding with fighting back or running away, and either one is going to escalate the situation into what I like to call the “Godzilla meets Rodan” effect. There’s a lot of screaming and yelling, and buildings fall down, but not much is accomplished.

Never panic, always keep a level head and think through the situation.


Step 2: Ignore the toxicity (don't get baited).

Dr. Bernstein recommends treating the toxic individual like a child. When they're throwing a tantrum, do you argue with them about why they're being a baby? Of course not! Dismiss their actions and focus on the underlying problem. Do not drop to their level. Ignore the toxicity, and zero in on what's causing it.


http://aguywithatablet.deviantart.com/

Step 3: Slow down the situation and make them think.

We want the toxic player to slow down and start thinking. We need to say things like:
"Please help us understand, we'd like to help."
"Why is this happening and how can we fix it?"
"What can we do to help?"

Notice that all of these can't be responded to with a simple yes/no one-word answer. We're trying to switch the toxic player from dinosaur brain mode to thinking mode. This will force them to start answering open ended questions that require a lot of typing. We want to avoid any criticisms that could put them on the defensive. So stay away from "You" and focus on "We" statements. This makes the toxic player feel like they're still part of the team, and stops them from feeling attacked.

I'll often offer criticisms for a single player in the form of an open ended question. If a player is dying a lot (due to mistakes), I could say:
"You need to stop overextending and dying, it's costing us the game!"
But that will turn on the player's dinosaur brain and put them on the defensive. Instead I prefer to say:
"What can we do to stop dying so much and win the game?"
I'm saying the exact same thing, but now it's constructive and inclusive versus negative and alienating. The open ended nature of the question also appeals to the thinking brain instead of the dinosaur brain.

Slow down the conversation with questions like this to calm down a toxic player before they go beyond saving.


Step 4: Let them have the last word.

Leave your ego at the door. Your goal is to stop the toxicity and win the game, not proving that you are right and the toxic player is wrong. Give them the last word and stomach any of their crap until you've claimed victory.


As a jungler, you'll often be blamed for every single thing that goes wrong in a lane. There are a lot of players out there that need to blame something besides their own incompetence (or poor luck) when things turn for the worse. These are the same players who will push up in lane without wards and then cry at their jungler for not anticipating that they were going to get ganked. Sky sums this up nicely in one of his videos:



Now a win or a loss is shared amongst a 5 person team, and there are always improvements to be made and mistakes to avoid. That said, raging at your teammate WHEN YOU F*CK UP, is one of the worst things you can do for your team. More often than not, you piss off the entire team (they will probably mute you) making them play slightly worse with their dinosaur brains on. Sometimes you'll start a chat battle and then one of your teammates will type "Stop being an assho" right before they die (you can't move or do other actions while typing in league). Raging, nitpicking, or complaining about teammates does nothing to help your team win. Nothing.

So how do you stop it? Lie.

"You're right, it's my fault. What can we do differently so we do to turn this around?  Where should we focus on the map since they're ahead now? How can I help us win?"

This will usually calm the rager (and your team) down and switch the conversation back to team strategy. They will probably continue being a jackass. Hold your tongue (errr typing), and let them get it all out. Good players have enough EQ to suck up their pride and win games. Part of being a leader in league (and in life) is knowing how to stomach the special players that grace our games from time to time long enough to get a victory on the board.

The ends justify the means. Let the toxic player have the last word if it means victory.

Final Words


Sometimes the player that can control his teammates best is the one who will win the game. Here are the 4 steps necessary for fighting toxicity and pushing players towards teamwork:

Step 1: Keep calm.
Losing your cool will create two toxic players instead of only one.

Step 2: Ignore the toxicity (don't get baited).
Do you argue with a screaming toddler? Don't validate the toxicity.

Step 3: Slow down the situation and make them think.
Ask open ended questions.
Avoid statements that put the toxic player on the defensive.
Use "We" instead of "You".
Words matter. Choose them wisely to disarm your teammate's toxicity.

Step 4: Let them have the last word.
Suppress your urge to be right if it means winning the game.

And remember, after the game you are free to do whatever you want. Best of luck everybody!


Want to see these tactics in action? Want to learn how to jungle? Follow me on Twitch.