Rock Paper Scissors Code Golf

Rock Paper Scissors Code Golf Featured Image

No, this isn’t some new version of Rock Paper Scissors. Instead, we’re going to revisit an old article of mine where I implemented Rock Paper Scissors, and we’re going to try to reduce the size of the program as much as possible without sacrificing too much of the readability.

To save you some time, I was only able to reduce the size of the program by about 250 characters or 25% of it’s original size. That said, I think you’ll like to see what that process looked like! Can you do any better?

Table of Contents

What Is Code Golf?

Before we dig in, I figured we could take a moment to talk briefly about code golf. For the uninitiated, code golf is basically a programming metagame where you not only try to write a correct solution to a problem, but you also look to solve it in as few characters as possible.

Now, I’ve never really been a huge fan of code golf because it’s not exactly practical (except maybe in the web development space). And, as someone who values code readability, it’s not exactly fun to code to read.

With that said, coding does not always have to be practical. For example, I see a lot of people engage in all sorts of fun activities like making art in CSS or designing esoteric languages. In other words, it’s totally okay to shitpost, and so that’s what I’ll be doing today!

For the purposes of this article, however, we won’t quite go that extreme. After all, I still want the code to be readable. Ultimately, the goal will be to exploit as many programming features as possible to reduce the overall character count.

Where Are We Starting?

As you may recall, we did something similar to code golf in the previous article where we reduced the number of branches we needed to check to simplify the Rock Paper Scissors algorithm. Ultimately, we moved from ten branches down to the following four:

  • Bad Input
  • Win
  • Lose
  • Tie

This resulted in a solution that looked something like this:

import random
import sys

# Create number to choice mapping
mapping = ["Rock", "Paper", "Scissors"]

# Generate computer choice
pc_choice = random.randint(0, 2)
pc_choice_output = "I chose %s." % mapping[pc_choice]

# Request user choice
try:
  user_choice = int(input("Choose Rock (0), Paper (1), or Scissors (2): "))
  user_choice_output = "You chose %s." % mapping[user_choice]
except (ValueError, KeyError):
  print(pc_choice_output)
  print("You chose nothing.")
  print("You lose by default.")
  sys.exit(0)

# Share choices
print(pc_choice_output)
print(user_choice_output)

# Setup results
i_win = "%s beats %s - I win!" % (mapping[pc_choice], mapping[user_choice])
u_win = "%s beats %s - you win!" % (mapping[user_choice], mapping[pc_choice])
tie = "Tie!"

# Share winner
if pc_choice == user_choice:
  print(tie)
elif (user_choice + 1) % 3 == pc_choice:
  print(i_win)
else:
  print(u_win)

As you can see, we’re not exactly starting from a large program (i.e. 864 characters and 36 lines)—though this is probably large from code golf standards. That said, I still think there are tons of ways we can reduce the number of lines in this program, and that’s the challenge today!

Initiate Optimization

So, what’s the plan? How are we going to tackle this? Well, similar my obfuscation article, I’m thinking we’ll try some things and see how they go.

Reducing the Number of Branches

Near the end of the previous article, I mentioned that we could reduce the solution to two cases: good and bad input. To do that, we need to rework the expression we’re using to calculate ties and losses. In other words, instead of the following which returns a boolean:

(user_choice + 1) % 3 == pc_choice

We can use something like this which gives us all three possible states (i.e. tie, win, loss) as an integer:

(user_choice - pc_choice) % 3

As mentioned in the previous article, this minor change can then be used to index a list which contains the expected results:

print([tie, u_win, i_win][(user_choice - pc_choice) % 3])

As a result, our program goes from 36 lines to 31 lines:

import random
import sys

# Create number to choice mapping
mapping = ["Rock", "Paper", "Scissors"]

# Generate computer choice
pc_choice = random.randint(0, 2)
pc_choice_output = "I chose %s." % mapping[pc_choice]

# Request user choice
try:
  user_choice = int(input("Choose Rock (0), Paper (1), or Scissors (2): "))
  user_choice_output = "You chose %s." % mapping[user_choice]
except (ValueError, KeyError):
  print(pc_choice_output)
  print("You chose nothing.")
  print("You lose by default.")
  sys.exit(0)

# Share choices
print(pc_choice_output)
print(user_choice_output)

# Setup results
i_win = "%s beats %s - I win!" % (mapping[pc_choice], mapping[user_choice])
u_win = "%s beats %s - you win!" % (mapping[user_choice], mapping[pc_choice])
tie = "Tie!"

# Share winner
print([tie, u_win, i_win][(user_choice - pc_choice) % 3])

Now that’s an improvement!

Cleaning Up String Formatting

Every time I look back at the original article, I cringe a little bit at the use of string interpolation. Instead, I almost exclusively use f-strings which improve both readability and character count. There are a few places these are used, so I’ll just show you the aggregate code with string interpolation replaced by f-strings:

import random
import sys

# Create number to choice mapping
mapping = ["Rock", "Paper", "Scissors"]

# Generate computer choice
pc_choice = random.randint(0, 2)
pc_choice_output = f"I chose {mapping[pc_choice]}"

# Request user choice
try:
  user_choice = int(input("Choose Rock (0), Paper (1), or Scissors (2): "))
  user_choice_output = f"You chose {mapping[user_choice]}"
except (ValueError, KeyError):
  print(pc_choice_output)
  print("You chose nothing.")
  print("You lose by default.")
  sys.exit(0)

# Share choices
print(pc_choice_output)
print(user_choice_output)

# Setup results
i_win = f"{mapping[pc_choice]} beats {mapping[user_choice]} - I win!"
u_win = f"{mapping[user_choice]} beats {mapping[pc_choice]} - you win!"
tie = "Tie!"

# Share winner
print([tie, u_win, i_win][(user_choice - pc_choice) % 3])

While this sort of change doesn’t reduce the line count, we do save a few characters overall (i.e. 790 vs. 808). Also, it makes me feel warm and fuzzy inside.

Reducing Print Statements

Another thing we might notice is that there’s a ton of calls to `print()`python in this program. One thing we could try is taking advantage of the fact that `print()`python accept variable length arguments. For example, we might try converting the three print statements in the except block into a single call to print. In other words, we might try transforming this:

print(pc_choice_output)
print("You chose nothing.")
print("You lose by default.")

Into this:

print(pc_choice_output, "You chose nothing.", "You lose by default.", sep="\n")

Unfortunately, this change doesn’t actually save us anything. They’re both 79 characters long!

Alternatively, it might be a better to defer all printing until the end. To do that, we’ll need a way to accumulate strings throughout the program. Naturally, one way to do that would be to concatenate all the strings together. Personally, I don’t like this idea because we’ll have to manually append newlines to the end of every string.

Instead, we’ll use a list in combination with `join()`python once we’ve collected the strings we need. In other words, anywhere we see `print()`python will be replaced by a call to `append()`:

import random
import sys

# Create number to choice mapping
mapping = ["Rock", "Paper", "Scissors"]

# Create output accumulator
output = []

# Generate computer choice
pc_choice = random.randint(0, 2)
pc_choice_output = f"I chose {mapping[pc_choice]}"

# Request user choice
try:
  user_choice = int(input("Choose Rock (0), Paper (1), or Scissors (2): "))
  user_choice_output = f"You chose {mapping[user_choice]}"
except (ValueError, KeyError):
  output.append(pc_choice_output)
  output.append("You chose nothing.")
  output.append("You lose by default.")
  print("\n".join(output))
  sys.exit(0)

# Share choices
output.append(pc_choice_output)
output.append(user_choice_output)

# Setup results
i_win = f"{mapping[pc_choice]} beats {mapping[user_choice]} - I win!"
u_win = f"{mapping[user_choice]} beats {mapping[pc_choice]} - you win!"
tie = "Tie!"

# Share winner
output.append([tie, u_win, i_win][(user_choice - pc_choice) % 3])
print("\n".join(output))

Unfortunately, this doesn’t exactly reduce our character count. In fact, it balloons it by about 136 characters (i.e. 790 to 926).

Compressing Repeated Method Calls

Alright, so we aren’t exactly reducing our footprint, so what else can we try? Well, there are a couple fixes we can make. For example, we might use `extend()`python in places where there are consecutive calls to `append()`python. In other words, this:

output.append(pc_choice_output)
output.append("You chose nothing.")
output.append("You lose by default.")

Becomes this:

output.extend([pc_choice_output, "You chose nothing.", "You lose by default."])

In this example, we manage to move from 103 to 79 characters. Unlike with the `print()`python example, this form of compression actually works!

Overall, unfortunately, we’ve still grown:

import random
import sys

# Create number to choice mapping
mapping = ["Rock", "Paper", "Scissors"]

# Create output accumulator
output = []

# Generate computer choice
pc_choice = random.randint(0, 2)
pc_choice_output = f"I chose {mapping[pc_choice]}"

# Request user choice
try:
  user_choice = int(input("Choose Rock (0), Paper (1), or Scissors (2): "))
  user_choice_output = f"You chose {mapping[user_choice]}"
except (ValueError, KeyError):
  output.extend([pc_choice_output, "You chose nothing.", "You lose by default."])
  print("\n".join(output))
  sys.exit(0)

# Share choices
output.extend([pc_choice_output, user_choice_output])

# Setup results
i_win = f"{mapping[pc_choice]} beats {mapping[user_choice]} - I win!"
u_win = f"{mapping[user_choice]} beats {mapping[pc_choice]} - you win!"
tie = "Tie!"

# Share winner
output.append([tie, u_win, i_win][(user_choice - pc_choice) % 3])
print("\n".join(output))

In total, our solution is sitting at 887 characters. That said, we’re starting to drop our line count back down.

Removing Redundant Code

So, what can we do? Well, while working through the previous change, I realized there’s a bit of redundant code we can remove. For example, notice how we define variables for strings we only use once:

pc_choice_output = f"I chose {mapping[pc_choice]}"
user_choice_output = f"You chose {mapping[user_choice]}"

Oddly enough, not only are these strings only used once, but their use is sometimes even redundant. For example, we append `pc_choice_output`python twice depending on context. Why don’t we append it as soon as we create it?

import random
import sys

# Create number to choice mapping
mapping = ["Rock", "Paper", "Scissors"]

# Create output accumulator
output = []

# Generate computer choice
pc_choice = random.randint(0, 2)
output.append(f"I chose {mapping[pc_choice]}")

# Request user choice
try:
  user_choice = int(input("Choose Rock (0), Paper (1), or Scissors (2): "))
  output.append(f"You chose {mapping[user_choice]}")
except (ValueError, KeyError):
  output.extend(["You chose nothing.", "You lose by default."])
  print("\n".join(output))
  sys.exit(0)

# Setup results
i_win = f"{mapping[pc_choice]} beats {mapping[user_choice]} - I win!"
u_win = f"{mapping[user_choice]} beats {mapping[pc_choice]} - you win!"
tie = "Tie!"

# Share winner
output.append([tie, u_win, i_win][(user_choice - pc_choice) % 3])
print("\n".join(output))

Overall, I’m quite pleased with this change, but it didn’t do a ton for our overall character count. Now, we’re done to 791 which is slightly above our lowest total so far (i.e. 790). That said, we’re down to the fewest lines yet!

Dismantling the Try/Except Block

One of the things that’s holding us back from really reducing the size of this program is the massive try/except block. The main reason for this is that it introduces an additional way of exiting the program. If we’re somehow able to remove this block, we’d be able to drop an import, an exit statement, and an extra print statement.

Of course, the key to getting this to work is to find a way to validate input without raising an exception. Unfortunately, there’s two things we have to validate. First, we need to know if the string is an integer. If it is, then we need to verify that it’s between 0 and 2.

To do that, we could take advantage of the `isdecimal()`python method of string and the `range()`python function. As far as I can tell, these will give us the behavior we want, but there may be weird edge cases. Regardless, here’s the original try/except block:

try:
  user_choice = int(input("Choose Rock (0), Paper (1), or Scissors (2): "))
  output.append(f"You chose {mapping[user_choice]}")
except (ValueError, KeyError):
  output.extend(["You chose nothing.", "You lose by default."])
  print("\n".join(output))
  sys.exit(0)

And, here’s how we might simplify it:

choice = input("Choose Rock (0), Paper (1), or Scissors (2): ")
if choice.isdecimal() and (user_choice := int(choice)) in range(3):
  output.append(f"You chose {mapping[user_choice]}")
else:
  output.extend(["You chose nothing.", "You lose by default."])
  print("\n".join(output))
  sys.exit(0)

Then, if we wanted to simplify this further, we could move the game code into the upper block. Here’s the final result:

import random

# Create number to choice mapping
mapping = ["Rock", "Paper", "Scissors"]

# Create output accumulator
output = []

# Generate computer choice
pc_choice = random.randint(0, 2)
output.append(f"I chose {mapping[pc_choice]}")

# Request user choice
choice = input("Choose Rock (0), Paper (1), or Scissors (2): ")

# Play game
if choice.isdecimal() and (user_choice := int(choice)) in range(3):
  output.append(f"You chose {mapping[user_choice]}")

  # Setup results
  i_win = f"{mapping[pc_choice]} beats {mapping[user_choice]} - I win!"
  u_win = f"{mapping[user_choice]} beats {mapping[pc_choice]} - you win!"
  tie = "Tie!"

  # Select winner
  output.append([tie, u_win, i_win][(user_choice - pc_choice) % 3])
else:
  output.extend(["You chose nothing.", "You lose by default."])

# Share winner
print("\n".join(output))

Now, surprisingly, we actually went up on character count. Even after getting sneaky with the walrus operator, we moved up from 791 to 806.

Grouping Similar Code

At this point, I just started thinking of ways we could apply some of the same techniques from above to the existing code. For example, we can certainly combine the append statements in the upper block. In other words, this:

output.append(f"You chose {mapping[user_choice]}")

# Setup results
i_win = f"{mapping[pc_choice]} beats {mapping[user_choice]} - I win!"
u_win = f"{mapping[user_choice]} beats {mapping[pc_choice]} - you win!"
tie = "Tie!"

# Select winner
output.append([tie, u_win, i_win][(user_choice - pc_choice) % 3])

Becomes this:

# Setup results
i_win = f"{mapping[pc_choice]} beats {mapping[user_choice]} - I win!"
u_win = f"{mapping[user_choice]} beats {mapping[pc_choice]} - you win!"
tie = "Tie!"

# Select winner
output.extend([f"You chose {mapping[user_choice]}", [tie, u_win, i_win][(user_choice - pc_choice) % 3]])

While it’s not pretty, it does save us like 11 characters. In addition, it mirrors the lower block which makes me think we might be able to merge them in some way. In other words, we can try to store the lists in the same variable and only call `extend()`python when we’re done. That way, this:

if choice.isdecimal() and (user_choice := int(choice)) in range(3):
  # Setup results
  i_win = f"{mapping[pc_choice]} beats {mapping[user_choice]} - I win!"
  u_win = f"{mapping[user_choice]} beats {mapping[pc_choice]} - you win!"
  tie = "Tie!"

  # Select winner
  output.extend([f"You chose {mapping[user_choice]}", [tie, u_win, i_win][(user_choice - pc_choice) % 3]])
else:
  output.extend(["You chose nothing.", "You lose by default."])

Becomes this:

if choice.isdecimal() and (user_choice := int(choice)) in range(3):
  # Setup results
  i_win = f"{mapping[pc_choice]} beats {mapping[user_choice]} - I win!"
  u_win = f"{mapping[user_choice]} beats {mapping[pc_choice]} - you win!"
  tie = "Tie!"

  # Select winner
  outcome = [f"You chose {mapping[user_choice]}", [tie, u_win, i_win][(user_choice - pc_choice) % 3]]
else:
  outcome = ["You chose nothing.", "You lose by default."]
output.extend(outcome)

Of course, as you can probably imagine, we actually get 12 characters back with this change. Isn’t that fun? That said, I quite like the result:

import random

# Create number to choice mapping
mapping = ["Rock", "Paper", "Scissors"]

# Create output accumulator
output = []

# Generate computer choice
pc_choice = random.randint(0, 2)
output.append(f"I chose {mapping[pc_choice]}")

# Request user choice
choice = input("Choose Rock (0), Paper (1), or Scissors (2): ")

# Play game
if choice.isdecimal() and (user_choice := int(choice)) in range(3):
  # Setup results
  i_win = f"{mapping[pc_choice]} beats {mapping[user_choice]} - I win!"
  u_win = f"{mapping[user_choice]} beats {mapping[pc_choice]} - you win!"
  tie = "Tie!"

  # Select winner
  outcome = [f"You chose {mapping[user_choice]}", [tie, u_win, i_win][(user_choice - pc_choice) % 3]]
else:
  outcome = ["You chose nothing.", "You lose by default."]
output.extend(outcome)

# Share winner
print("\n".join(output))

Yet, by some magic, we actually end up with fewer characters than the previous solution (i.e. 805 vs 806). Don’t ask me how.

Cleaning up Strings

In all this rearranging of code, I’ve found one of the more annoying things is how many times we access the mapping. As a result, one quick change we could make is storing that result of the mapping once for reuse. In other words, instead of this:

# Setup results
i_win = f"{mapping[pc_choice]} beats {mapping[user_choice]} - I win!"
u_win = f"{mapping[user_choice]} beats {mapping[pc_choice]} - you win!"
tie = "Tie!"

# Select winner
outcome = [f"You chose {mapping[user_choice]}", [tie, u_win, i_win][(user_choice - pc_choice) % 3]]

We could try something like this:

# Setup results
user_pick = mapping[user_choice]
i_win = f"{mapping[pc_choice]} beats {user_pick} - I win!"
u_win = f"{user_pick} beats {mapping[pc_choice]} - you win!"
tie = "Tie!"

# Select winner
outcome = [f"You chose {user_pick}", [tie, u_win, i_win][(user_choice - pc_choice) % 3]]

Unfortunately, this does basically nothing for us. However, I did try doing the same thing with the computer’s choice. In addition, I defined the output list with the first string in it. Here’s the result:

import random

# Create number to choice mapping
mapping = ["Rock", "Paper", "Scissors"]

# Generate computer choice
pc_choice = random.randint(0, 2)
pc_pick = mapping[pc_choice]
output = [f"I chose {pc_pick}"]

# Request user choice
choice = input("Choose Rock (0), Paper (1), or Scissors (2): ")

# Play game
if choice.isdecimal() and (user_choice := int(choice)) in range(3):
  # Setup results
  user_pick = mapping[user_choice]
  i_win = f"{pc_pick} beats {user_pick} - I win!"
  u_win = f"{user_pick} beats {pc_pick} - you win!"
  tie = "Tie!"

  # Select winner
  outcome = [f"You chose {user_pick}", [tie, u_win, i_win][(user_choice - pc_choice) % 3]]
else:
  outcome = ["You chose nothing.", "You lose by default."]
output.extend(outcome)

# Share winner
print("\n".join(output))

Now, we’re talking! The total character count is now down to 759. Unfortunately, beyond really wrecking the readability, I’m starting to grasp at straws. What else could we do?

Removing Else Branch

One idea I had was to assume the user entered bad data and only change the result if we get good data. As a result, we could remove the else branch and define the outcome variable sooner.

Of course, this only removes like 5 characters. As a result, we need to think bolder! For example, what if we appended the outcomes to the output variable and used slice assignment to overwrite those values. That result would be pretty interesting:

import random

# Create number to choice mapping
mapping = ["Rock", "Paper", "Scissors"]

# Generate computer choice
pc_choice = random.randint(0, 2)
pc_pick = mapping[pc_choice]
output = [f"I chose {pc_pick}", "You chose nothing.", "You lose by default."]

# Request user choice
choice = input("Choose Rock (0), Paper (1), or Scissors (2): ")

# Play game
if choice.isdecimal() and (user_choice := int(choice)) in range(3):
  # Setup results
  user_pick = mapping[user_choice]
  i_win = f"{pc_pick} beats {user_pick} - I win!"
  u_win = f"{user_pick} beats {pc_pick} - you win!"
  tie = "Tie!"

  # Select winner
  output[1:] = [f"You chose {user_pick}", [tie, u_win, i_win][(user_choice - pc_choice) % 3]]

# Share winner
print("\n".join(output))

In case it’s not abundantly clear how this works, basically we create our output list assuming the user will enter bad data. If they don’t we use slice assignment to overwrite irrelevant data with the proper data. In other words, the strings that read “You chose nothing.” and “You lose by default.” are replaced by their proper counterparts depending on how the game goes.

By making this change, we shave off another ~30 characters. We’re down to 723, and I still think this is very readable. Also, we’re down to 26 lines. How cool is that?

Removing Extraneous Variables

At this point, all I can really think to do is remove variables that aren’t used more than once. For example, we can embed all the variables in the if statement directly into the list. Don’t worry, I’ll format it nicely:

import random

# Create number to choice mapping
mapping = ["Rock", "Paper", "Scissors"]

# Generate computer choice
pc_choice = random.randint(0, 2)
pc_pick = mapping[pc_choice]
output = [f"I chose {pc_pick}", "You chose nothing.", "You lose by default."]

# Request user choice
choice = input("Choose Rock (0), Paper (1), or Scissors (2): ")

# Play game
if choice.isdecimal() and (user_choice := int(choice)) in range(3):
  user_pick = mapping[user_choice]
  output[1:] = [
    f"You chose {user_pick}", 
    [
      "Tie!", 
      f"{user_pick} beats {pc_pick} - you win!", 
      f"{pc_pick} beats {user_pick} - I win!"
    ][(user_choice - pc_choice) % 3]]

# Share winner
print("\n".join(output))

It may not seem like much, but this change actually drops us into sub-700 character count territory. Specifically, we’re sitting at 678!

Cleaning Up Code

At this point, I’m fairly satisfied with what we’ve accomplished so far. Certainly there are ways to keep shrinking this program, but I think I’m going to save that for a new series!

Instead, let’s take one more pass on this program. In particular, I want to move some of the statements around, change some of the variable names, and clean up the comments. Here’s the result:

import random

# Generate default outcome
choices = ["Rock", "Paper", "Scissors"]
pc_index = random.randint(0, 2)
pc_choice = choices[pc_index]
output = [f"I chose {pc_choice}", "You chose nothing.", "You lose by default."]

# Play game
user_pick = input("Choose Rock (0), Paper (1), or Scissors (2): ")
if user_pick.isdecimal() and (user_index := int(user_pick)) in range(3):
  user_choice = choices[user_index]
  output[1:] = [
    f"You chose {user_choice}", 
    [
      "Tie!", 
      f"{user_choice} beats {pc_choice} - you win!", 
      f"{pc_choice} beats {user_choice} - I win!"
    ][(user_index - pc_index) % 3]]

# Share outcome
print("\n".join(output))

In the end, we were really only able to shave off about 200 characters. In its final form, this program sits at 644 characters and 22 lines which is a bit smaller than its original 864 characters and 36 lines.

What Else Could Be Done?

Having taken a very long look at this Rock Paper Scissors program, there were a lot of things I tried or wanted to try. Unfortunately, my iterative approach could have lead us to a local minima. In other words, maybe there’s something we could have done to the original program that would have had a much larger impact. Clearly modular arithmetic did most of the heavy lifting, so I really struggled to find anything that effective.

Of course, that wasn’t for a lack of trying. For instance, one of the things I really wanted to do was merge the “I win!”/’You win!” strings as well as the “You chose” strings, but I couldn’t find a way to do it that would require fewer characters. In general, I’m noticing that sometimes it’s shorter to write duplicate code outright.

Similarly, there was always this pressure in the back of my head to write a scalable program. For example, the use of `range(3)`python really bothers me because it should be a function of the number of choices. Of course, writing `range(len(choices))`python sort of defeats the point of this activity.

Obviously, we could really shrink this program if we abandoned our readability constraint. By removing comments alone, we’d save another 50 characters. Then, we could do a bunch of stuff that we did in the obfuscation article like rename all our variables to single characters or remove all extraneous spaces. In fact, I’m already planning to leverage some of these tipsOpens in a new tab. in the follow-up.

That said, is there anything else you would do? Let me know! Otherwise, I thought this was a fun exercise which tested my limits of the language. In the end, I was able to sneak in the walrus operator AND slice assignment. I’m one list comprehension away from a perfect game!

At any rate, thanks for checking this article out! I’m actually going to write a follow-up shortly that takes this code golf idea to the extreme. Keep an eye out for that! Otherwise, consider checking out my list of ways to support the site. Any little bit helps!

Likewise, here are a few related posts in the meantime:

And, here are some helpful Python resources from Amazon (ad):

Thanks again! See you next time.

Jeremy Grifski

Jeremy grew up in a small town where he enjoyed playing soccer and video games, practicing taekwondo, and trading Pokémon cards. Once out of the nest, he pursued a Bachelors in Computer Engineering with a minor in Game Design. After college, he spent about two years writing software for a major engineering company. Then, he earned a master's in Computer Science and Engineering. Today, he pursues a PhD in Engineering Education in order to ultimately land a teaching gig. In his spare time, Jeremy enjoys spending time with his wife, playing Overwatch and Phantasy Star Online 2, practicing trombone, watching Penguins hockey, and traveling the world.

Recent Posts