DEV Community

Unpublished Post. This URL is public but secret, so share at your own discretion.

DerpWars: The "Buying" Gameplay Loop [Live Coding Recap]

Remember Dope Wars?

STORYTIME! Back in the late-90's, my school friends and I got pretty sucked into a simple text-based game that went by the name Dope Wars (or DRUGWAR, depending on the version you had).

drugwars

Installing Dope Wars was probably one of the first "hacks" I ever did on my TI and it felt so cool to be completely blowing off actual learning and, you know, buying and selling fake drugs on my calculator.

The predecessor to Dope Wars was Drugwars, released in 1984 on MS-DOS by John E. Dell. Since then, there've been many versions of the buy/sell/trade game on many different platforms - one of which was my sweet sweet TI-83.

dope wars
Keep in mind, this was pre-GTA! Sure, being a complete dork IRL and pretending to be such an edgelord had it's own appeal... But what really kept me (and millions of other delinquent teenagers) playing was the addictive core gameplay loop.


The core gameplay loop (premise)

Here are the basics: It was an economy-based game. You bought and sold drugs. If you ran out of time, money, or were busted - you lost the game. If you made enough money back to pay off your loan shark in time - you won.

The Loop

  1. You arrive at a location.
  2. You're presented with a list of things to buy/sell.
  3. You buy and sell stuff.
  4. You move to another location (and hope you don't get caught).

Rinse & repeat.


Time to make the game unique

Since promoting drug use is inarguably a very bad idea, we're going to call our version Derp Wars. Hecc, let's even hashtag it. And let's set our artificial release date vaguely in... 2020. Not only will it sound like we're working really hard on this, but it'll make us look future AF. 😎

#DerpWars: The peak 2020 gaming experience.

Perfect! 😗👌

So how can we make ours different? For starters, instead of buying and selling drugs (which are bad, mmk?), we'll buy and sell... derps. Let's jot down a few.

# DERPS
- laptop stickers
- deepfaked cat pics
- conference swag
- mix tapes (they're fire)
- hand sanitizer
- floppy disk (8" SSSD)
Enter fullscreen mode Exit fullscreen mode

And since we're not roaming around alleyways in NYC, we should prob pick some places to set up shop. Let's pick a handful to start out with. We can expand on these later - it'll be super easy to customize.

# LOCATIONS
- ruddit.com/ragrets
- 7chin
- the dork web
- an coffee shop
- some hacker's basement
Enter fullscreen mode Exit fullscreen mode

At this point, our player isn't a drug dealer anymore. I'll be honest, it sounds like our player might be a 2020 Influencer. Yea, let's go with that.

Let's call the player an Influencer.

Oh! One last piece. We need a currency. Since our players an Influencer, how about we use Clout for a currency? We can even use this fancy Colon character for it:

Our Influencer will deal Derps in ₡ (Clout)

Ok sweet, things are really coming together 🤣! Bigups to chatters & community members on Twitch for helping come up with these ideas during a recent live stream.


The MVP/VS

Our first goal in making this game is creating what's called an MVP, or Minimum Viable Product. To be more specific, we're going to take a VS (Vertical Slice) of the MVP and make that first. It'll be the simplest, testable version of the game - which will hopefully confirm our overall design works. It'll also give us a way to quickly playtest what we've made before coding more complex logic and polishing our turd beautiful work of art.

For the VS, we'll create the basic buy and move loop.

Your player will arrive at a random location, be presented with a list of items to buy at a random price, make a purchase, then move to a new location.

Psuedo-code the game-loop

To get an idea of what the TI source code looked like, have a look at this before we get started - just to freak yourself out a little.

Ok, now that you're thoroughly overwhelmed, let's actually take a minute to break the game down into some simple yet actionable pseudo-code.

# Core gameloop (buying VS)
    # show what day it is and how many are left
    # show the player their inventory
    # show the player some locations & ask them where they want to go
    # move the player
    # show the player what's for sale and ask what they want to buy
    # report/track/log the purchase & update their inventory
Enter fullscreen mode Exit fullscreen mode

Now that we've got that done, we can start filling in the blanks/comments/psuedo with functions calls. Don't worry about how they'll do what they'll do, just name them what we want them to do and we'll add functionality later.

Setting up derp.py

Paste this in a file and call it derp.py. Or whatever. I mean, GOTY_2020.py would work too..

Core gameloop (vertical slice = buying):

# derp.py
while still_playing():

    # show how many days are left
    start_day()

    # show the player their inventory
    list_inventory()
    list_currency()

    # show the player some locations & ask them where they want to go
    move_options = get_locations(locations)
    list_locations(move_options)
    location = move_options[get_move()-1]

    # move the player
    display_location(location)

    # show the player what's for sale and ask what they want to buy
    rates = get_rates()
    display_rates(rates)
    while True:
        purchase = prompt_buyer()
        if can_buy(rates[derps[purchase]]):
            # report/track/log the purchase & update their inventory
            make_purchase(purchase, rates[derps[purchase]])
            break
        else:
            print("\nDude! You don't have enough money! Buy something else..\n")
Enter fullscreen mode Exit fullscreen mode

👆 Sweet! We have a rough draft of the game loop that's easy to step through and get an idea of what's supposed to happen. Let's do our best to keep this loop simple and tuck all the working bits neatly away in functions we make.


Adding functionality

Now it's time to go through each function we have in our rough draft and define the functions and variables we called in our main loop.

still_playing()

This function will return True if the player hasn't won or lost the game yet. Otherwise, they'll stay in the core of the loop.

For now, let's just keep this function simple and return True so we can lock ourselves in the loop and test/play it indefinitely. We'll set up win/lose conditions near the end.

# at the top of derp.py, above the while loop
def still_playing():
    """check for end conditions"""
    return True  # TODO add win/lose conditions
Enter fullscreen mode Exit fullscreen mode

start_day():

This function will be a banner that displays at the start of each move/round. Let's have it display how many days we have left.

# below still_playing()
def start_day():
    """a banner that signals a new round and lists remaining days"""
    global day
    day += 1
    print(f"\n===================================")
    print(f"|                                 |")
    print(f"|             Day {day}               |")
    print(f"|                                 |")
    print(f"===================================")

Enter fullscreen mode Exit fullscreen mode

We'll need a variable day that keeps track of what day it is.

# add above start_day()
day = 0  # starting day
Enter fullscreen mode Exit fullscreen mode

list_inventory()

We'll use this function to print the player's Influencer's inventory to the console at the start of every round. If their inventory is empty, it'll just tell them and not show an empty table.

# below start_day()
def list_inventory():
    """print inventory & clout prettily™ on screen"""
    print('\n======== I N V E N T O R Y ========')
    # if inventory is empty, say so explicitely
    if all(val == 0 for val in inventory.values()):
        print("\nYour inventory is currently empty. feelsbadman.jpg.")
    # otherwise, print the inventory
    else:
        print('QTY | DERP')
        print('----+------------------------------')
        # only list items that have inventory
        for item,qty in inventory.items():
            if qty > 0:
                print(f"{qty}   | {item}")
    print('')  # extra line at the end looks kleeeeen 👌
Enter fullscreen mode Exit fullscreen mode

For this to work, we're going to need to generate an empty inventory for the Influencer™. Let's create a variable inventory and use gen_inventory(derps) to iterate through the list of purchasable derps and assign each the value/qty of 0. We'll also need to put our derps in a list at this point.

# above list_inventory()
derps = [
    'laptop stickers',
    'deepfake cat pics',
    'conference swag',
    'hand sanitizer',
    'iced espresso americano',
    "floppy disk (8\" SSSD)",
]

def gen_inventory(derps):
    """generates an empty starter inventory for the influencer"""
    inventory = {}
    for derp in derps:
        inventory[derp] = 0
    return inventory

# generate starting inventory for player (empty)
inventory = gen_inventory(derps)
Enter fullscreen mode Exit fullscreen mode

list_currency()

We'll update the player with their inventory status every round with this function. We'll also need to make variables for how much currency the player has and what we're calling our currency.

# below inventory = gen_inventory(derps)
clout = 20  # starting funds
currency = 'c (Clout)'

def list_currency():
    print(f"You have {clout}{currency} in your fanny pack.\n")
Enter fullscreen mode Exit fullscreen mode

I should also take a moment to specify that our Influencer™ is storing their clout (US) in a fanny pack (US) not a fanny pack (UK), so I don't offend my friends across the pond.


get_locations()

This will return a list of 3 random locations out of our list of 6. In order for it to work, we're gonna need to go ahead and make our list of locations as well.

# below list_currency()
locations = [
    "ruddit.com/ragrets",
    '7chin',
    'the dork web',
    'an coffee shop',
    "some hacker's basement"
]

def get_locations(places):
    """returns a list of 3 random locations"""
    random.shuffle(places)  # shuffle in place
    return places[:3] # return the first 3
Enter fullscreen mode Exit fullscreen mode

Since we're using the random library, we'll need to import it at the top of the file.

# at the very top
import random
Enter fullscreen mode Exit fullscreen mode

list_locations()

Passing in the move_options variable we return the list from get_locations() to, we'll iterate through the list of 3 places and print them to the console.

# below get_locations()
def list_locations(places):
    """print a list of locations the player can move this turn"""
    print("======== L O C A T I O N S ========")
    for num,place in enumerate(places):
        print(f"{num+1}) {place}")
    print('')
Enter fullscreen mode Exit fullscreen mode

get_move()

This function will prompt the player Influencer™ for their move. It'll take in an integer we can use as an index for the list of move_options to translate their integer input to the name of the place they chose. We also need to keep asking them where they want to go until they enter a valid choice. Since Python doesn't have do-while loops, we'll do it this way:

# below list_inventory()
def get_move():
    """ask the player where they want to go"""
    # keep asking whre they want to go until they give a valid input
    while True:
        print('Where to, Dude? ')
        move = prompt()
        if 1 <= move <= 3:
            break
        else:
            print("\nThat location doesn't exist. Try again! :D")
    return move
Enter fullscreen mode Exit fullscreen mode

What it looks like in the main loop: location = move_options[get_move()-1]

You probably noticed that we introduced a new function we haven't defined yet called prompt(). We can use prompt() to display some info to the player on the command-line then return their input.


prompt()

Since we're going to be prompting the player for several things throughout the game, it makes sense for it to live in it's own function and for each function that needs to interface with the player to do so, in their own unqiue ways, using prompt().

# ABOVE get_move()
def prompt():
    """get input from the player"""
    try:
        return int(input(f"[ DAY {day} | {clout}{currency[0]} ] >>> "))
    except ValueError:  # in case they send an empty string or a weird char
        return False
Enter fullscreen mode Exit fullscreen mode

Another nice thing about having the player interface with prompt() is we can keep basic input validation all in one place. This try & except error handler ensures the player won't break the game by entering weird (or no) characters.


display_location()

Now let's pass the location we got from prompting the player into a function that updates the player of their move.

# belog get_move()
def display_location(location):
    """print where the player chose to move on screen"""
    print(f"\nYou've arrived at {location}.\n")
Enter fullscreen mode Exit fullscreen mode

get_rates()

This function will return a random price to the derps for purchase this round.

# belog display_location()
def get_rates():
    """returns derps at random rates"""
    derp_rates = {}
    for derp in derps:
        derp_rates[derp] = random.randint(1,10)
    return derp_rates
Enter fullscreen mode Exit fullscreen mode

display_rates()

Let's pass the rates that get_rates() returned in the line above into this function. It'll print formatted prices in a simple ascii table.

# # below get_rates()
def display_rates(rates):
    """show the rates of everything that's for sale"""
    print(' # |  C  | DERP')
    print('---+-----+-------------------------')
    index = 1
    for derp,rate in rates.items():
        print(f"{index:>2} | {rate:>2}{currency[0]} | {derp}")
        index += 1
    print('')
Enter fullscreen mode Exit fullscreen mode

Prompting the buyer

Alright, we've set up the round. The game starts at day 1, with 20 Clout, and an empty inventory. They can pick a place to go and see a list of what's available to buy and for how much. Now we they just need to buy something.

For this part, we'll need a while loop that prompts the player for input and checks if they have the Clout to make the purchase before charging them and adding it to their inventory.

In psuedo, it'll look like this.

# while forever
    # ask what the buyer wants to buy
    # if they have the Clout
        # report the purchase and exit the loop
    # otherwise, let them know they need to buy something cheaper
Enter fullscreen mode Exit fullscreen mode

And the code breaks down like this.. (taken from the above main loop, shown for reference)

# taken from the main loop
while True:
    purchase = prompt_buyer()
    if can_buy(rates[derps[purchase]]):
        # report/track/log the purchase & update their inventory
        make_purchase(purchase, rates[derps[purchase]])
        break
    else:
        print("\nDude! You don't have enough money! Buy something else..\n")
Enter fullscreen mode Exit fullscreen mode

Let's build out the functions.


prompt_buyer()

This function will need a bit of extra input validation. We'll use a while loop like we did before.

# below display_rates()
def prompt_buyer():
    """ask the player what they want to buy and return an int"""
    # keep asking until they return a valid input
    print("What do you want to buy, cool guy?")
    while True:
        purchase = prompt()
        # check to see if their input is valid
        if 1 <= purchase <= len(derps):
            break
        else:
            print("\nThat's not something you can *technically* buy here... Try again.")
    return purchase - 1
Enter fullscreen mode Exit fullscreen mode

can_buy()

For this function, we'll need to return a True if they have the Clout to make the purchase, and a False if they don't.

# below prompt_buyer
def can_buy(rate):
    """make a purchase."""
    # check to see if they have enough money first
    if rate > clout:
        return False
    return True # let 'em know the buy was successful
Enter fullscreen mode Exit fullscreen mode

make_purchase()

If they have the money, we'll also subtract the Clout and add the item to their their fanny pack.

# below can_buy()
def make_purchase(derp, rate):
    global clout
    inventory[derps[derp]] += 1  # adjust their inventory
    clout -= rate  # remove the clout from their fanny pack (US)
    report_purchase(derp, rate)
Enter fullscreen mode Exit fullscreen mode

report_purchase() give the player confirmation that their transaction was successful.

# above make_purchase()
def report_purchase(derp, rate):
    """prints what the player purchased and for how much"""
    print(f"\nAiight. You just bought some {derps[derp]} for {rate}{currency[0]}")
Enter fullscreen mode Exit fullscreen mode

Let's test it out! :D

We're finally at a state we can start testing! The buying loop is complete. Your player should now be able to travel around and make purchases. And since we don't have any end-game scenarios, you can just keep playing until you run out of money and can't buy anything.

Go ahead and run derp.py in the terminal.

How'd it go? Now's a good time to trouble shoot things before adding the selling functionality and end-game scenarios.

Wait wait wait! One last thing before we move on!

let me show you something

We really could use a splash-screen/logo for when we start the game. Go ahead and add this function and it's call right before the main loop and give the game another try.

# just above the while/main loop
def splash_screen():
    print('')
    print("| ̄ ̄ ̄ ̄ ̄ ̄ |")
    print("|   WELCOM   |")
    print("|    TO      |")
    print("|     DERP   |")
    print("|   WARS     |")
    print("| _______|")
    print("(\__/) ||")
    print("(•ㅅ•) ||")
    print("/   づ")

splash_screen()
Enter fullscreen mode Exit fullscreen mode

there it is


| ̄ ̄ ̄ ̄ ̄ ̄ |
|   WELCOM   |
|    TO      |
|     DERP   |
|   WARS     |
| _______|
(\__/) ||
(•ㅅ•) ||
/   づ

===================================
|                                 |
|             Day 1               |
|                                 |
===================================

======== I N V E N T O R Y ========

Your inventory is currently empty. feelsbadman.jpg.

You have 20c (Clout) in your fanny pack.

======== L O C A T I O N S ========
1) an coffee shop
2) the dork web
3) some hacker's basement

Where to, Dude?
[ DAY 1 | 20c ] >>>
Enter fullscreen mode Exit fullscreen mode

Yea, we're definitely taking home GOTY.

The sauce

If you need help trouble-shooting or just wanna skip ahead, here's the source code so far:

Source Code: #DerpWars (part 1)

Up next : Part 2 - Selling & End-game

Next up in the series, we're going to add the selling functionality to the game and set the player up to lose if they run out of money, time, or get caught in an awkward conversation with a stranger they don't know how to leave.

😐

Live Coding on Twitch

If you have any questions about this article, the code, or just wanna hang out with fellow nerds - swing by during a live stream and say hello! I stream Python shenanigans & lame jokes™ every Tuesday, Thursday, & Saturday at 7pm PST.

💜 Follow on Twitch

*For the most up to date schedule, check my pinned post on Twitter.

Top comments (0)