Posts tagged Shortcuts
Building "Monopoly" in Shortcuts - Update #1: The Beginning of Something Terrible

As it had been a few months since my last post on building un-productive things in iOS Shortcuts, I felt like it was time to get back in the game and build something new. I knew that I wanted to create a game of some sort, though I didn’t feel quite up to perfecting the roguelite engine that I had painstakingly laid out previously— largely because, let’s face it, it’s a pain in the ass.

What I settled on was that I should use Shortcuts to build a local multiplayer game; something that used the phone and the power of Shortcuts, but still relied on good old-fashioned human interaction. Examples like Spaceteam and the Exploding Kittens apps were two of the big inspirations. Rather than starting from scratch, I wanted to emulate an existing game that most people would at least sort of know the rules of, saving me the burden of explanation.

The result, I’m sorry to say, is that I’ve started building Monopoly in Shortcuts.

If you are reading this on an iOS device with Shortcuts installed, you can run the current version of the game using this link:

https://www.icloud.com/shortcuts/9b9c8b7103444aa2bd804561b88dac85

It’s important to note that the current version is just barely playable, and well below the minimum viable product for a Monopoly clone, but it’s been about a week since I’ve had time to work on it, so I wanted to share where it was at right now.

What it has:

  • Support for 2-6 local players

  • Emojis instead of token pieces

  • Full Monopoly board

  • Roll dice for movement

  • Purchase properties, stations, and utilities. Each space keeps track of its owner, so you can’t buy another player’s property.

  • Each player gets their own money to spend

  • Players get $200 each time they pass Go

  • All of the Chance and Community Chest cards from the U.S. version of the game

  • Income Tax/Luxury Tax spaces

What it does not yet have:

  • Rent payments

  • Bankruptcy Rules

  • The Community Chest cards don’t do anything yet

  • The Chance cards have minimal functionality, certain cards don’t do what they say they do

  • Landing in/getting out of Jail

  • Monopolies

  • Win conditions

  • An end state

What it will probably never have:

  • Trading

  • Houses/Hotels

  • Fun


Roughly How It Works:

Lists. I’ve used Lists for basically everything here.

The board is a list, where each item contains the space’s name, colour, purchase cost, rental cost, and owner, as an array separated by forward slashes (“/”). I use the slashes a lot here to spoof a two-dimensional array, so if I need a particular part of the space’s information (like the name), I grab the list variable, “get item in list” based on which item I need, “split text” by “/”, and “get item in list” to grab the specific item I need.

The players’ cash-on-hand, token, and board position are all separate lists, where the index matches the player’s spot in the turn order. When I need a bit of player info, I just grab the relevant list variable, and “get item in list” by the current player’s number (1 through 6). If I ever get around to figuring out monopolies, I’ll probably have a list to catalogue how many spaces of each colour each player owns.

Repeats. There are two sort of “core” repeat functions.

The big loop: After the more universal variables and lists have been set, I wrap the whole of the “gameplay” in one big loop, set to repeat an insane number of times. Because Shortcuts doesn’t give us custom functions or “loop forever” support, I get around this by taking a number function, and mashing the 9 key until my thumb gets tired (there is a max value for the number, but it’s something like 1^10*25). Passing this value to the repeat lets it loop until the heat death of the universe, or at least until someone accidentally hits “cancel”. This means that the game won’t end after the first round.

The turn loop: Immediately inside the #BigLoop is another repeat function that loops a number of times equal to the number of players (which is the first question asked of the user). This benefits us in two ways:

  1. When all of the players have taken a turn, we can perform “end of round” actions, like ranking charts or special events.

  2. “Repeat Index 2” == the current player, so we have an easy way of finding the information pertinent to the person whose turn it is, and no one else.

Even smaller loops: There are a few places where I need to update a player’s information inside of one of the lists. Because Shortcut’s doesn’t really let us update items in the middle of lists, we use short loops to achieve this. Each one looks roughly like this:

  • Define which list we’re updating data in

  • Repeat for “number of players”

    • If “repeat index 3” (because we’re in the big and turn loops) equals “current player”

      • Get whatever relevant variable

      • Add it to a temporary variable

    • Otherwise

      • Get item from the list we’re updating, at the “repeat index 3” index

      • Add it to a temporary variable

    • End if

  • End repeat

  • Get that temporary variable

  • Split text by new lines

  • Set variable so that it overrides the original list

It’s a bulkier workaround than I would like, however it lets us reliably update things as needed. We used this to update the player’s money and positions at the end of each turn, plus it’s how we handle payments in the middle of each turn.


Next steps:

  1. Add in paying rent to a player when you land on a space they own. (I know how I want to do this, I just need to make the time for it.)

  2. Add alerts and rules for bankruptcy.

  3. Add an endgame event, and win conditions.

I genuinely don’t know if I want to bother adding houses/hotels. I might rather adjust the dollar values and some minor rules to make this more of a “Speed Monopoly” so that it’s more tenable for a mobile experience.

I would add “make it fun” as a next step here, but, come on. It’s Monopoly.

Working with Save States in iOS Shortcuts games

While experimenting with the text adventure frameworks outlined in a previous post, I became frustrated with having to start from scratch each time I re-loaded the shortcut. For shorter games, this isn’t an issue, but it prevents us from making longer experiences attractive to players. It’s not much fun to play an epic space opera adventure if you have to do it all in one sitting. So, I began playing around with the idea of implementing basic save states in a text adventure game.

To start, I made a basic layout according to the MUD-style format I outlined here: Text Adventures in iOS Shortcuts

You can download the example here:

Utilizing the “Get File” and “Save File” features, I allow the shortcut to save and receive information from iCloud Drive. In this example, I use this to create a text file with the following information:

  1. Player Class

  2. Player Position

  3. Player Inventory

  4. Amount of Gold the player has

I also put the words “Save Data” right at the top. This lets me load data using this process:

  1. Get File “filename” from iCloud Drive

  2. If file contains “Save Data”

  3. Get Text from Input

  4. Split Text by New Lines

  5. Get each item, and assign it to the proper variable.

We want to use the “Save Data” text check to verify that there is even a save file there to begin with. Otherwise, we only allow the player to start a new game. All of this is done before the main Repeat function of the game takes place, so that if we are loading a game, the player begins right where they left off. Since the loop does not particularly care where we start, no extra steps are necessary within the loop for differentiating between new or saved games.

To save the game’s progress, all you have to do is create a new Text element with the variables you want to save, in their proper order. Then pass this to the Save File function, being sure to check the “Overwrite if already exists” option. I hid this option within a couple of menus, to allow the player to start a new game and save over the previous one, but you could feasibly add the Save File function to the end of the Repeat, so that the game auto-saves after each action the player takes. It’s up to how you want things to play out.

Additionally, you’ll notice that I created a “Filename” element, and un-checked the option to “Show file selector” in both the Get File and Save File functions. This was largely an aesthetic choice for me, as I don’t much enjoy the file picker. This also allows us to ensure that the correct file is saved and loaded each time, preventing any potential errors. If you would rather let the player pick where they save/load from, just enable that option.

Building a Roguelike in iOS Shortcuts

Diverging off of my last post about using iOS Shortcuts, an app built with the intention of productivity, for purely useless reasons, I’m going to go into detail on another one of my ill-conceived Shortcuts-in-progress: Roguemoji.

Link: https://www.icloud.com/shortcuts/a8b49fe817ed43f4b20b78322b2d8d07

With a bit of elbow grease and repetition, Shortcuts actually allows us to build a fairly plausible roguelike game, complete with random maps and items that we can pick up. No monsters yet, that’s on the way.

How it works

At its core, the roguelike “loop” reads very similarly to the text adventure outlined in the previous post: Find the player’s current position, draw the surroundings accordingly, allow the user to change position, repeat. What it does differently is incorporate graphics into the equation, allowing us to visualize a maze (dungeon,. labyrinth, what-have-you) around the player character, rather than relying on just text. Even though it may be more accurate to tradition to use ASCII characters, the iPhone gives us plenty of emoji to play with, so we’ll use them.

As with the text adventure, we start by defining all of our required variables outside of the main “loop”. Most importantly, we’ll need to know what our Map looks like, what our player’s Position is, and since we’re bringing graphics into this, we’ll define a Camera element, as well. The idea here being that we don’t want to show the entire map at once, and show instead a small part of it, centered around the player.

To define the Map, I recommend using the Text function, and drawing in a map of your liking using the Black Square emoji for walls, and the White Square emoji for walk-able floor tiles. The map should be a rectangle of any width/height you prefer, though I recommend keeping the width to something like 12 or 13 characters, as I found that gave me the best legibility while working in the app. Then, use the Set Variable function to save the Text as a variable that we can call up later.

Note: You’ll notice that in the example provided, I used a variety of different Text functions, and assigned them to a list. This allowed me to pseudo-randomly generate a map, which works well enough most of the time. Procedural map-building is a hallmark of the roguelike genre, but not yet a strength of mine, so this will be something we revisit later on. For the time being, feel free to use a single, static Text command to make your Map.

For example purposes, let’s say that we have a map that is 12 emoji wide, with a good spot for the player to start off in at 3 spaces in, and three lines from the top. Our first bit of the Shortcut should then look something like this:

Text: [Map of emoji blocks here]

Set Variable: Map

Number: 12

Set Variable: Map Width

Number: 3

Set Variable: Player X

Set Variable: Player Y

Notice that we’re using two separate variables for X and Y. Since our emoji map exists in a Text function, we can manipulate it into letting us use the two-dimensional arrays that our text adventure lacked. We’ll come back to that, though. Next, let’s look at the camera.

Because there’s only so much space on the phone screen, we don’t want to show the whole Map all at once. Instead, we want to show the player just a segment of it, centered on the player character. This should have enough information of the player character’s surroundings to be useful, but not so much that it becomes cumbersome or difficult for the program to load. After some experimenting, I settled for showing the player a 5-by-5 square, with the character positioned in the camera’s center as often as possible. To achieve this, we first set the Camera’s beginning X and Y positions by subtracting from the player position:

Number: 3

Set Variable: Player X

Set Variable: Player Y

Calculate: Subtract 3

Set Variable: Camera X

Set Variable: Camera Y

Number: 5

Set Variable: Camera Width

Set Variable: Camera Height

Using this, when the Player is at X,Y {3,3}, for instance, the Camera will be at {0,0}, showing us the first 5 characters of the first 5 rows of the Map. If this doesn’t make sense just yet, keep going, and play around with it later. You could also replace the “3” in the Calculate command with a variable that you can easily change later on, so that you can find a balance that you prefer.

Disclaimer: I’m sure you’ve already noticed something about Shortcuts: it takes a while to do literally anything. It certainly isn’t as efficient as programming this sort of thing in C++, Java, Bash, Python, or whatever. Hell, you could probably do this faster in BASIC if you wanted. Shortcuts does not equal programming. This is just how my brain works: taking something that is clearly intended for being useful, and making dumb games with it. Savvy? Okay, moving on.

The Camera

Now that we’ve done all of our setup for the Player and Camera positions, we have to turn that into something that actually does something, right? We’ll do this by returning to the “Nigh-Infinite Repeat” trick that we used for the text adventure in my previous post, which starts like this:

Number: 99999999999999999999999999999

Repeat: [Number] times

We’ll place that at the bottom of the program, under our setup functions, and put our gameplay “loop” inside. For starters, let’s go ahead and figure out what the Camera sees of the Map. I’ll write out what the functions look like first, then explain it line-for-line after. Feel free to also consult the example I provided for a less-than-perfect reference as you go.

Get Variable: Camera Height

Get Variable: Camera Width

Repeat: [Camera Height] times

Get Variable: Camera Y

Calculate: Add [Repeat Index 2]

Set Variable: Row To Get

Get Variable: Map

Split Text: New Lines

Get Item From List: Item at Index [Row To Get]

Set Variable: Current Row

Repeat: [Camera Width] times

Get Variable: Camera X

Calculate: Add [Repeat Index 3]

Set Variable: Character To Get

Get Variable: Current Row

Split Text: Every Character

Get Item From List: Item at Index [Character To Get]

Set Variable: Current Item

Get Variable: Character To Get

If: Equals [Player X]

Get Variable: Row To Get

If: Equals [Player Y]

Text: [Whatever emoji you want to represent your character]

Set Variable: Current Item

End If

End If

Get Variable: Current Item

Add To Variable: Row To Draw

End Repeat

Get Variable: Row To Draw

Split Text: New Lines

Combine Text: Custom [Leave blank]

Add To Variable: What The Camera Sees

Nothing

Set Variable: Row To Draw

End Repeat

Text: [What The Camera Sees]

Alright, wow! That was a lot. It looks like a lot. It is a lot.

But!

That is the core of what we’re doing here today. Everything else is secondary to this, and it’s not that hard once we break it down. Let’s do that now:

First, we’re going to put two more Repeat functions into our big Repeat function (yo dawg, etc., etc.). This is how we create the “square” of what our camera sees: the first Repeat looks at five rows, starting at Camera Y then adding the Repeat Index 2 each time to move to the next row down. Notice that it’s Repeat Index 2, because Repeat Index would correspond to our “master” repeat, and be no use at all. The second Repeat then takes whatever row we’re looking at, and breaks it up into individual characters. From there, we grab the Camera X value, and add Repeat Index 3 to find the individual character that we’re looking to draw.

If the result of Camera X plus Repeat Index 3 matches Player X, and If Camera Y plus Repeat Index 2 matches Player Y, that’s where the character is! Let’s draw that emoji instead of whatever’s on the map.

Whether we’re drawing the Player character or whatever’s at that place on the map, we’re going to Add To Variable to add it to our “Row To Draw”. If you’re playing around with the Add To Variable function, you’ll see pretty quickly that it adds whatever’s passed to it into a new line in the variable. While this would be fine if our map was one character wide and an infinite number of characters long, it doesn’t suit our two-dimensional look. That’s why, after we’ve gone through all of the steps in the second Repeat function (getting the characters out of the row), we Split Text on the “Row To Draw” variable, then immediately Combine Text. Combine Text allows us to combine a List (which is what Split Text gives us) using a Custom value, which we’re just going to leave blank. That takes our vertical column, and returns it as a horizontal row of the proper characters. We then Add To Variable again, adding this nice horizontal row to “What The Camera Sees”.

Before looping back around and going to the next row, let’s use the Nothing function to set the “Row to Draw” variable back to nil. Otherwise, we’ll just keep adding to that variable, and things will get real weird.

Once we have all of the rows that the Camera sees, we’ll use Text to neatly wrap them all up, and if you’re feeling intrepid, you can use Quick Look to check your work.

A Few Notes Before Braving Forth

So far, we’ve done a handful of things:

  1. Trick Shortcuts into letting us use Text as two-dimensional arrays

  2. Used those arrays to display only a small part of the overall map

  3. Separated our camera from our player

The first two things are essential parts of bringing the roguelike genre to this format; if we showed the player the whole map at once, that would not only ruin some of the surprise element, but also not fit on the most mobile device screens (although the iPhone XS is massive so, like, who knows).

The third thing is a simple one, probably, but one that I’m really proud of. That’s because it’s a fundamental element of making games that are set in the third-person (where you see your player character on the screen): the Camera object and the Player object are two separate entities, and can be moved independently. This means that as the Player character approaches the edges of the map, we can have them move all the way up to that edge, without the Camera trying to show the “great beyond” that doesn’t exist.

As an example of this in our roguelike game, imagine that the player wants to move to a space at Row 1, Column 1 (Shortcuts only uses 1-indexed arrays, so there’s no real 0,0). Using what we’ve set up so far, once the player moves to that space, placing them in the middle of what the Camera is showing would first require pulling two empty rows, then two empty characters, before showing the player. You would be using only about a quarter of the space we set up for our Camera to display on, and risk errors or funky display issues.

Using what we’ve set up now, we can let the Player move all the way to that corner, and simultaneously make sure that the Camera doesn’t go past that point. The player would temporarily move out of the Camera’s center while they explore those edges, but come safely back to center once they turn back and explore the rest of the map. It’s a small thing when you put it into words, but speaks volumes for the quality of the final product.

Okay, coming down off my soap box. Now that we’ve set up the base variables, and our loop for using the camera to the player and the map, let’s set up the interface for actually playing the game.

Actually playing the game

If you’ll remember from our previous Text Adventure example, the main cruft of the game revolved around using the Choose From Menu function in a loop, wherein the choices adjusted the Player’s X and Y positions. Here, we’re going to do roughly the same thing, with some extra If functions to keep our Player and Camera in line, and from going off the map. Let’s start with dropping in the Choose From Menu, which we’ll place after the Text function we used at the end of the last section, and before the End Repeat at the end of the function (we want this to take place within our main loop).

Text: [What The Camera Sees]

Choose From Menu: [What The Camera Sees]

Up

Down

Left

Right

End Menu

Nothing

Set Variable: What The Camera Sees

End Repeat

Pretty basic so far. We display “What The Camera Sees”, then ask the player to choose from the four cardinal directions. If you wanted to get more fancy, you could add more things to the Text function (health, inventory, etc.) and use that in the menu instead, or add diagonal directions for a fun isometric feel. For now, we’ll keep things as simple as we can, probably.

We also pass Nothing back to the “What The Camera Sees” variable, so that we don’t just keep adding to it with each repeat.

Within each of the four options (which you’re welcome to rearrange or rename as you like, I ended up using emoji), we’re going to add If statements to make sure that there’s space that direction, then update the Player and Camera positions as needed. Because these get pretty big, pretty fast, I’ll take a look at each direction separately, then we can look at the Menu as a whole. Let’s start with the easiest one:

Up

Get Variable: Player Y

If: Is Greater Than 1

Calculate: Subtract 1

Set Variable: Player Y

Get Variable: Camera Y

If: Is Greater Than 1

Calculate: Subtract 1

Set Variable: Camera Y

End If

End If

We’ll start with the obvious: If the Player’s Y value is greater than 1 (I keep having to remind myself that Shortcuts uses arrays that start at 1), then we subtract 1 from that value, and assign the result back to Player Y.

If the Player changed positions, we’ll also check to see If the Camera has space to move, as well. We do this in the exact same way: If Camera Y is greater than 1, we subtract one from that value, and return it to Camera Y.

Challenge: We can apply that same basic function to the Left option in our Menu, but trading in Player X and Camera X for Player Y and Camera Y. Give it a shot!

We’ll now tackle the more complex movements:

Down

Get Variable: Map

Count: New Lines

Set Variable: Map Height

Get Variable: Player Y

If: Is Less Than [Map Height]

Calculate: Add 1

Set Variable: Player Y

Get Variable: Camera Y

Calculate: Add [Camera Height]

If: Is Less Than [Map Height]

Get Variable: Camera Y

Calculate: Add 1

Set Variable: Camera Y

End If

End If

At lot of the same notes as the other direction, but with some important additions. First, unlike the “Map Width” variable, we didn’t set a variable for getting the Map’s height, so we use Count to get the number of lines in “Map”, representing the number of rows we have to work with. You could do this right up top with Map Width, I supposed, but calling the action here allows us a degree of dynamism (Is that the right word? Probably not.) and gives us the opportunity to change the height of the Map between actions (secret rooms!).

Second, we grab Camera Y and Calculate where the bottom of the frame is, by adding the “Camera Height” variable that we set a forever ago. This way, we are actually checking to see if the bottom of the camera’s viewport is going to go off the edge of the map, then adjust the Camera Y value if it is not.

Challenge: This same concept can be applied to the Right option in our Menu, using Map With, Player X, and Camera X. Can you figure it out before we put everything together?

Putting Everything Together

By now, we have something resembling the structure of a basic, top-down game, with the potential for roguelike elements. Moving into those specific elements will complicate what we’ve done today, at least in my own brain, so I’m going to compile an overview of what we’ve done here, then go into more depth in my next post. If you have questions or comments, let me know! I’m positive I fucked something up somewhere, so don’t be shy.

If you’ve been following along, here’s likely what you have so far in your Shortcut:

Text: [Map of emoji blocks here]

Set Variable: Map

Number: 12

Set Variable: Map Width

Number: 3

Set Variable: Player X

Set Variable: Player Y

Calculate: Subtract 3

Set Variable: Camera X

Set Variable: Camera Y

Number: 5

Set Variable: Camera Width

Set Variable: Camera Height

Number: 99999999999999999999999999999

Repeat: [Number] times

Get Variable: Camera Height

Get Variable: Camera Width

Repeat: [Camera Height] times

Get Variable: Camera Y

Calculate: Add [Repeat Index 2]

Set Variable: Row To Get

Get Variable: Map

Split Text: New Lines

Get Item From List: Item at Index [Row To Get]

Set Variable: Current Row

Repeat: [Camera Width] times

Get Variable: Camera X

Calculate: Add [Repeat Index 3]

Set Variable: Character To Get

Get Variable: Current Row

Split Text: Every Character

Get Item From List: Item at Index [Character To Get]

Set Variable: Current Item

Get Variable: Character To Get

If: Equals [Player X]

Get Variable: Row To Get

If: Equals [Player Y]

Text: [Whatever emoji you want to represent your character]

Set Variable: Current Item

End If

End If

Get Variable: Current Item

Add To Variable: Row To Draw

End Repeat

Get Variable: Row To Draw

Split Text: New Lines

Combine Text: Custom [Leave blank]

Add To Variable: What The Camera Sees

Nothing

Set Variable: Row To Draw

End Repeat

Text: [What The Camera Sees]

Choose From Menu: [What The Camera Sees]

Up

Get Variable: Player Y

If: Is Greater Than 1

Calculate: Subtract 1

Set Variable: Player Y

Get Variable: Camera Y

If: Is Greater Than 1

Calculate: Subtract 1

Set Variable: Camera Y

End If

End If

Down

Get Variable: Map

Count: New Lines

Set Variable: Map Height

Get Variable: Player Y

If: Is Less Than [Map Height]

Calculate: Add 1

Set Variable: Player Y

Get Variable: Camera Y

Calculate: Add [Camera Height]

If: Is Less Than [Map Height]

Get Variable: Camera Y

Calculate: Add 1

Set Variable: Camera Y

End If

End If

Left

Get Variable: Player X

If: Is Greater Than 1

Calculate: Subtract 1

Set Variable: Player X

Get Variable: Camera X

If: Is Greater Than 1

Calculate: Subtract 1

Set Variable: Camera X

End If

End If

Right

Get Variable: Map Width

Get Variable: Player X

If: Is Less Than [Map Width]

Calculate: Add 1

Set Variable: Player X

Get Variable: Camera X

Calculate: Add [Camera Width]

If: Is Less Than [Map Width]

Get Variable: Camera X

Calculate: Add 1

Set Variable: Camera X

End If

End If

End Menu

Nothing

Set Variable: What The Camera Sees

End Repeat

At the end of it all, we have a map built from emoji, a camera that displays only part of that map, a player character right in the middle, and a way to move them around. That leaves us with a certain set of new challenges!

  1. At the start, I mentioned that I used White Square and Black Square emoji for drawing our map. How do we keep the player character from passing through Black Squares? (I did this in the example, check it out.)

  2. How would we go about creating random maps?

  3. How do we handle item interactions?

  4. Can we add random enemies to the map, with their own discrete movement and actions?

I’ve tried my hand at the first three items, and will be tackling the last one soon. In the next post, we’ll go into detail about some potential ways to make this happen, and try it out ourselves.

Text Adventures in iOS Shortcuts

Like a lot of people, I was aware of Workflow as an iOS app that seemed to be a slightly more robust version of IFTTT, but didn’t see an immediate use for it until iOS 12 was released, and the app was re-branded as Apple’s official Shortcuts app.

Only problem was, by that time, I had very little left in my life that I really cared to automate. Something about being sad and unemployed? I don’t know, it sounds vaguely familiar. Anyway.

For Android users, or at least people who don’t care, Shortcuts is a tool that allows you to use a drag-and-drop programming interface to put a limited amount of functions together. The end result of this being a new app, which harnesses the power of other apps on your phone. This can range from things like, polling the Weather app to see whether you will need an umbrella today, and giving you a push notification letting you know when and where the rain will fall. Or, for the smart-homed, curating intricately-choreographed “scenes” far beyond what the Home app is capable of; perhaps texting your entire home an emoji, and watching as the lights, temperature, entertainment center, and coffee machine all work in unison to fit a certain mood.

You could also, if you’re a true Portlander and are so inclined, use it to brag about how many coffee shops are within walking distance.

Though the new tool, with the ability to run programs that brought apps together using Siri, Home Screen buttons, or even the Widgets panel, was designed for those with a will to capital-O Optimize their everyday life, I saw in it a very different potential.

This started with the discovery of Space Alert, a “crappy text adventure” that takes advantage of the branching “Choose from Menu” function. While it’s a short game all told, it proffered a revelation: While the functions provided within are small, they contain all of the necessary elements of a variety of text and turn-based games. And so I set to work.

What unfolded over the last month has been an exploration, of sorts, into the practicalities of building games within a program that is all but explicitly not decided for making games in. Many of them are still a work-in-progress, but it’s proving to be a deeper well of experimentation than I anticipated. Below are two of the methods that I’ve been exploring, with some detailed reference for trying it out yourself.

If neither of them interest you, I’ve also figured out how to make a roguelike with Shortcuts, but that’s… a much longer post. Stay tuned.


Choose-Your-Own-Adventure

Starting with the example set forth from Space Alert, you can use a combination of the Show Alert and Choose from Menu functions to create a narrative with branching paths. Because of the visual nature of the Shortcuts interface, the results of a choice get “nested” inside the choice in the code, so it may be wise to create “routes” that diverge based on choice, then eventually come back together.

Roughly, that looks like this:

Show Alert: “Hello! Welcome to the game. You are in a room with two doors.”

Choose From Menu: “Do you want to go Left, or Right?”

Left

Show Alert: “You have chosen the left path.”

Right

Show Alert: “You have chosen the right path.”

Show Alert “You win!”

Reading that back, you’ll first see an alert saying “Hello! Welcome to the game. You are in a room with two doors.” You will then be presented a menu, allowing you to choose left or right, resulting in the respective alert shown. Then, regardless of choice, you’ll be shown the alert “You win!”

Of course, this can get more complicated as you go deeper and deeper into the branches. You can place a “Choose From Menu” inside a “Choose From Menu”, for example, or even list the result of a previous choice as an option for a new menu to list. Space Alert does a great example showing this off, so I won’t try to provide my own reference outside of that.


MUD-Style Text Adventure

Example: Simple Text Adventure Framework

One of my earliest and most important memories of playing videogames with my dad involves playing my first “MUD”, or “Multi-User Dungeon". A precursor to MMOs and MMORPGs, a MUD allowed a bunch of users to connect to the same server, and play a simple text adventure game together. Typically, this would follow a stereotypical ZORK-like pattern: “You are in a very dark room. You are likely to be eaten by a grue. Exits are NORTH, SOUTH, and WEST.”

You would then have to type commands like, “go NORTH”, or, “attack GRUE” and see how the world responded.

The reason this particular memory stuck with me, however, is that the game we played took place over a large expanse of “rooms” with various descriptions. Sprawling mansions, open marketplaces, creepy dungeons, all of which rendered only in text, without a map to keep them all organized. If you wanted to remember which way you’d turned previously, so that you can make your way back out of the labyrinth before the minotaur catches you, you had better keep track of that somehow. So, we would spend hours together, drawing out each action and movement onto a large sheet (and quickly several large sheets) of graph paper. The game, just a virtual fantasy, soon had a weighty physical counterpart, that we had built ourselves.

In Shortcuts, it’s possible to recreate this same sort of game on a smaller scale by replicating the core conceit of the text adventure above, but adding a few more functions from the Shortcuts arsenal, namely List, Variables, Calculate, and Repeat. All very simple ideas unto themselves, but can be combined powerfully.

Let’s start with how we want the game to look, and work backwards from there. I would like to have the game described to me a fictional room that I’m in, and provide a list of directions, which I can then use to move to the next room, which will then be described to me, and so on…

This is the core “loop” of the game, and since Shortcuts applications are only designed to be run all the way through once, this is achieved through the Repeat function. As of yet, there isn’t a “repeat infinitely” option, however we can set a Repeat of something like 1,000,000,000,000 times, which is basically the same thing. Inside that “loop”, we will do four things:

  1. Calculate what room the player is in

  2. Show that room’s description

  3. Allow the player to choose from a list of available exits

  4. Update the player’s position accordingly

If, like me, you’re prone to draw out maps on graph paper, you’re probably used to thinking about locations and positions in terms of X and Y coordinates. Picturing the top-left corner of the page as the origin, subtracting from a player’s Y value moves them “up” the page, whereas adding to it moves them “down”. Same for the X value with left and right. Once you have the X and Y position of the player, all you have to do is return the room that matches that value, and you’re off to the races.

Well, almost.

Shortcuts, not being an app that’s designed for such purposes, doesn’t allow us an easy method for creating two-dimensional arrays, which is what we typically want for X/Y coordinates. So, we have to get creative (“yaaaaaay,” I hear you saying).

What we’re going to do instead, is take advantage of the List function: a one-dimensional array that we can trick into becoming a two-dimensional array.

To do this, let’s take a look at the total amount of space you’re looking to create. How many rooms wide and how many rooms tall will it be? For the purposes of example, I stuck with three rooms wide, and three rooms high, for a total of nine spaces altogether in our “map”. We then specify the Map Width and Map Height as variables, like this:

Number: 3

Set Variable: Map Width

Set Variable: Map Height

Note that we only have to set the Number once here, as the Set Variable function passes its input on to the next action. If we wanted the height to be different from the width, we would place another Number in-between the two Set Variable commands.

With those numbers in mind, we’ll now create a List of our rooms, with the idea that we will be listing 9 rooms, in imaginary rows of 3. You could conceivably picture them as columns of 3, as well, but I prefer rows, so there. In my example, the list looks roughly like this:

List: “Northwest Room”

“North Center Room”

“Northeast Room”

“West Room”

“Center Room”

“East Room”

“Southwest Room”

“South Center Room”

“Southeast Room”

Set Variable: Room Title

Using the three-by-three layout works well enough for these purposes, but remember that you can adjust this to fit any width or height that you need. You can then use the Set Variable command to save this list for easy use later.

If you want to get more detailed, as I did in the example Shortcut provided above, you can create multiple lists that all correspond to the same rooms. For example, you could have a list of Room Titles, another list of Room Descriptions, and another list of Room Items, just making sure that the order of each list lines up with the order of the others. To determine what item in our lists to return, we will use not X and Y, but the player’s “Position”, a gestalt of the two dimensions.

“Position” will represent the player’s current spot in our many lists, and moving the position up or down will affect what room is display. This is where knowing the width of your map ahead of time is important, because that allows us to fool the list into thinking that it has more dimensions than it really does. To move the player to the right or left, we simply add or subtract 1 from the player position as needed. To move the player “up”, or “down”, we will add or subtract the Map Width variable. This will have the effect of simulating vertical movement in our imaginary map.

In the example we’ve laid out so far, let’s say that we set the Player Position to 5, like so:

Number: 5

Set Variable: Player Position

We can use that number to then return the information that we have about the room at that position, using the Get Item From List function, like this:

Get Variable: Room Title

Get Item From List: Item at Index: Player Position

Show Alert: Get Item From List

When you create the Show Alert, you can use the Select Magic Variable option to pull the result of the Get Item From List directly, rather than having to set/get more variables. When this is all run together, we’ll see an alert telling us that we are currently in the “Center Room”.

With our map and our initial positions set, we must now find a way to move around, which we’ll accomplish by returning to our old friend Choose From Menu. First, we’ll create a menu giving us four options, which we’ll name “North”, “South”, “East”, and “West”, to keep with tradition. Inside each option, we will get, adjust, and set the “Player Position” accordingly:

Choose From Menu: “What direction would you like to go?”

North

Get Variable: Player Position

Calculate: Subtract “Map Width”

South

Get Variable: Player Position

Calculate: Add “Map Width”

East

Get Variable: Player Position

Calculate: Add 1

West

Get Variable: Player Position

Calculate: Subtract 1

End Menu

Set Variable: Player Position

Note that while we Get Variable in each option, to ensure that we really are dealing with the Player Position, we only Set Variable once at the end. Since the Choose From Menu function passes any result to the next action, we can rely on the correct calculation being applied to our variable in the end. If you’re a fan of redundancy, though, feel free to add Set Variable to the end of each choice (like I did in the example I linked above).

With the Player Position updated, we will now loop back around to the beginning of this diatribe, get the correct room from the list, display it, and start all over. In the briefest possible outline, here is how all of that looks within your Shortcuts program:

List: “Northwest Room”

“North Center Room”

“Northeast Room”

“West Room”

“Center Room”

“East Room”

“Southwest Room”

“South Center Room”

“Southeast Room”

Set Variable: Room Title

Number: 5

Set Variable: Player Position

Repeat: 9999999999999999 times

Get Variable: Room Title

Get Item From List: Item at Index: Player Position

Show Alert: Get Item From List

Choose From Menu: “What direction would you like to go?”

North

Get Variable: Player Position

Calculate: Subtract “Map Width”

South

Get Variable: Player Position

Calculate: Add “Map Width”

East

Get Variable: Player Position

Calculate: Add 1

West

Get Variable: Player Position

Calculate: Subtract 1

End Menu

Set Variable: Player Position

End Repeat

And that, as they say, is that. Every time you select a direction, the loop will reset, grab the new Player Position, and return the relevant room.

Now, obviously, that isn’t the whole story, but you’ve read enough! There are a number of remaining questions that I’ve attempted to answer in the example I linked to at the start of this, and invite you to try and answer in your own version of this:

  1. How do you keep the player from moving out of bounds?

  2. Can you make the limited number of Repeats interesting?

  3. Can you update the exits dynamically, forcing the player into corridors and such?

  4. Can you add elements of interactivity, such as items, puzzles, or enemies?

  5. Can you add graphics?

Happy hunting, my friends. As always, if you have questions or comments about any of this, please reach out! The amount of research being done into this avenue of using Shortcuts seems relatively limited, so I’m curious to hear what thoughts are out there.

Like I said at the start, I’ve also been working on a roguelike game, following similar principles to those outlined above, just… much more complex. I’ll work up a new post about that soon.