saffronrust

Verity

I created a website that serves as a guide for the 4th raid encounter of Salvation's Edge in Destiny 2. This blog post documents the experience I had while coding the app, as well as my takeaways.

Warning: this post is quite long-winded and technical. Beware!

Table of Contents:

  1. Background
  2. Verity
    2.1 Outside
    2.2 Inside
  3. The Algorithm
    3.1. The Naive Solution
    3.2. The More Elegant Solution
    3.3. How The Algorithm Works
  4. Implementation
    4.1. How The Website Works
    4.2. Fumbling With SVGs
    4.3. Mobile First
  5. The Deployed App
  6. Takeaways
  7. Conclusion

Background

Destiny 2 has a game mode called raids. They are 6 player activities comprised of several encounters that are required to be cleared in order to complete the raid. This particular raid, Salvation's Edge, is remarkably tough. Its fourth encounter, called Verity, is the most mechanically challenging encounter in Destiny 2. I hoped by making the guide, I can help others complete the encounter with minimal difficulties.

Verity

Explaining how the encounter works is already very complex for regular Destiny players, let alone the average non-gamer. Nevertheless, I will attempt to explain how this encounter works.

WARNING: VERY WORDY EXPLANATION AHEAD

Outside

At the start of the encounter, three players will be teleported to three "inside" rooms, one in each room. The remaining three will stay at the "outside" room. The goal of the "outside" room is to manipulate and dissect shapes in order for the "inside" people to escape.

The room contains three statues, each statue pertaining to each player in the "inside" rooms. They will be holding a certain 3D shape.1 These 3D shapes are comprised of two 2D shapes2. For example, Cone is comprised of Circle + Triangle, Cube is comprised of Square + Square, and so on.

To manipulate the shapes, you will need to kill an enemy knight in the room, it will drop a 2D shape.

For example, it dropped Triangle.

Take that 2D shape, and deposit that shape into a statue containing that shape.

I deposited Triangle into a statue holding Cone (Triangle + Circle).

Kill another knight, take another 2D shape, and deposit it into another statue containing that shape.

The other knight dropped Square, and I deposited it into a statue holding Cube (Square + Square).

What happens now is that the shapes I deposited into the two statues will now swap with each other, creating new shapes.

The Triangle in Cone (Triangle + Circle) will now swap with the Square in Cube (Square + Square). Now, the first statue will become Cylinder (Square + Circle), and the second statue becomes Prism (Triangle + Square).

That is the basis of the outside room mechanics. The goal is to form certain 3D shapes in order for the inside people to escape.

In order to do that, the inside people needs to call out the shapes they see in the inside rooms. For example, if they see the inside statues holding Circle, Square, Triangle (from left to right by default), they will communicate that to the outside people.

With this information, the outside people now know what 3D shapes to form. The actual goal, is to form 3D shapes that are comprised of 2D shapes that is NOT the original shape corresponding to the statue.

For example, the inside callouts are Circle, Square, Triangle. The outside people need to form Prism (Square + Triangle), Cone (Circle + Triangle), Cylinder (Circle + Square). Notice that the desired 3D shapes does not contain the shape in the inside callout.

Inside

The goal of the inside people are somewhat similar to the outside room.

In the room, they will see two shapes, which are expressed in the shadows of the room. They will also identify their own statue, which will be holding a shape. Their goal is to swap shapes so that the two buffs in the room ARE NOT the shape their statue is holding.

For example, their statue is holding Circle. Their goal is to have the Square and Triangle shapes in the room.

The mechanics of the shape swapping are very similar. Kill a knight, pick up their shape, put it into the statues, etc.

Okay, that's basically the encounter explained. There are other mechanics like the Witness calcification, but for the purposes of this post, the shape swapping mechanic for the outside and inside rooms will suffice. There are plenty of helpful video guides that you can search on YouTube, so I will drop a few links below:

TrueVanguard's guide

3 minute guide by dukeiscool

We can now move on to the app.

The Algorithm

Before we can even think about the UI or implementation, the algorithm to solve the outside shapes needs to be created first. I'll be honest with you, I was stumped. I racked my brain for a few days trying to come up with an algorithm to solve the outside room, but to no avail.

The Naive Solution

Originally (and naively), I thought of hardcoding all possible states that can occur from the initial inputs. There are only so many states that can happen, so it wouldn't be an issue right? Besides, if I can just look up the states, the time complexity would be O(1), amazing! How wrong was I.

Let's do some quick napkin math. We have three 2D shapes, and six 3D shapes. The number of possible combinations for the 2D shapes is 3! (the ! is factorial), and the number of possible combinations for the 3D shapes is 6!. Multiplying them together gives 4320 possible states. I am NOT hardcoding 4000+ possible combinations, the large majority of which are "invalid" states (meaning that these shapes cannot possibly be solved). I need a much better and clever solution.

The More Elegant Solution

Thankfully, I found a Reddit post by u/swegmesterflex who came up with an algorithm for solving the outside room. Nice! The only issue was that the algorithm was in Python, so that was left was to translate it to JavaScript for my website. It took a lot longer than I thought it would have.

The algorithm is actually quite cool, below is a simplified explanation of how it works. A little bit of a CS background may be needed. You can skip it if you want to.

How The Algorithm Works

2D shapes are represented as a character. 'c' for Circle, 's' for Square, 't' for Triangle.
3D shapes are represented as a list of 2D shapes. For example, Cone is represented as ['c', 't'], Cube is ['s', 's'], and so on.

In order to "swap" shapes, you just need to find the corresponding indexes, remove the original shape, and replace them with the swapped shape.

Now, given a state of 3D shapes e.g. [['t','t'], ['c','c'], ['s','s']], we generate all possible swaps that can occur from this state, and store the "steps" needed to reach a certain state.

In order to reach our desired state from our original state, we employ the Depth First Search algorithm. Once we find a path from the original state to the desired state, the algorithm returns the steps needed to reach to that state, and terminates.

If there is no path, that must mean the initial inputs are invalid, and the output will reflect so.
If the original state and the desired state are identical, that means it has already been solved.

As you might have realised, this algorithm is also a brute force algorithm, generating all possible states, then searching through for the shortest path to the desired state. Fortunately, the algorithm works remarkably fast. However, I was unable to find a more "elegant" solution, but for the purposes of the website, this will do.

Implementation

Armed with this algorithm, I can finally start on the website. I elected to use Next.js as my web framework, and Material UI (MUI) for my user interface (UI).

The rationale for choosing Next.js and MUI was simply because I had never tried them before, and I wanted to try new things.

Setup was smooth, I simply downloaded the template off of their GitHub and got to work.

How The Website Works

I split the website into three parts, Outside, Inside, and Tutorial.

When the user enters the website for the first time, they will see the Outside page first. This is because the primary function of the website is the Outside solver.

The user simply needs to input the inside shapes, and the outside shapes, and the algorithm will automatically output the required swaps needed to achieve the desired state. The great thing is that the page is dynamic, the user doesn't need to click a submit button, changing the inputs will automatically run the algorithm and output the required steps. This is important as the user will be using the website while in-game, surrounded by enemies. The user needs the answer fast.

The Inside page is also similar. The user just needs to input the inside shapes, and the page will output a step-by-step guide on what to do. The page is also dynamic, changing as the input changes.

The Tutorial page is just a write-up on how the website works. I added a third page just because I like to have three tabs on the website. I also added some tips and tricks for the encounter because it helps to have some useful information.

Fumbling With SVGs

Progress on the website was going well. I learnt from my previous personal project to properly read documentation, and the lesson paid off. I encountered little issues while coding. However, there was a slight setback.

Scalable Vector Graphics (SVGs) is an image format that uses XML to define the graphics. As the name suggests, they are scalable, with no loss in quality. This ensures high quality images no matter the size.

I intended to use SVGs in my website, due to their scalable quality. However for some inexplicable reason, Next.js or MUI refuses to render them properly, and I wasted an ENTIRE AFTERNOON working on this crap. In the end I used PNGs instead. Fortunately they were high quality enough that I couldn't discern any pixels from the images. Moving on...

Mobile First

The website was almost complete. Apart from some minor touchups, the functions all worked as intended. That was when I came to a realisation: it works perfectly fine on my computer, but what about mobile?

Think about it: the user is realistically not going to constantly alt-tab out of the game to check the website, that'll be dumb. No, the user will ideally be checking their phone on the side!

This meant that I had to adjust my user interface to be mobile friendly. Luckily I could test this in real-time by using the browser to simulate mobile screens. Fortunately it looked not bad. I could still comfortably browse and use the website.

One big issue was that due to the large size of the buttons, the user would need to scroll down to see the output. That would not do. The user should not waste additional actions to see the required steps. That would defeat the purpose of the absence of the submit button. So I got to work resizing all of the buttons and boxes in order to fit the entire page into the screen.

The Deployed App

With that, my website was complete. I deployed the website on Vercel, bought a domain name on namecheap and attached it to my website.

You can view my website, D2 Verity Companion on d2veritycompanion.com

Takeaways

From this personal project, I learnt a few things:

  • The importance of mobile first

This was my first time having to accommodate a "mobile first" user interface, it was an eye-opening experience having to adjust the UI and dimensions of my website in order to fit a portrait style screen. In today's age of smartphones, most websites have to adhere to this "mobile first" design, and I got a firsthand experience on this principle.

  • What web framework to use

Initially, I wanted to use Next.js just because I hadn't used it before. On hindsight, it may not have been an ideal choice.

Next.js uses Server Side Rendering (SSR), which means the web page is rendered on the server, then the HTML page is sent to the client.

On the other hand, there is Client Side Rendering (CSR), which means the web page is rendered on the client using JavaScript. This enables more dynamic web applications, since the client can update parts of the DOM when needed instead of the entire page. React uses CSR.

My website would have benefitted from CSR instead of SSR, since the web pages on my website will dynamically change based on the user's inputs. While it was a simple workaround to use CSR on Next.js with the 'use client' line at the top of every page, I should have better researched on what web framework to use for my website.

Conclusion

It was a lot of fun working on this website. It was great creating something that I can actually use in-game! I was highly motivated to complete this project since it was something I had genuine interest in. I hope my website helped a struggling blueberry3 in Destiny 2, even by a marginal amount.

If you ever need ideas for projects to work on, just think about a real problem that you face in your life, and think about what solutions you can create to solve that issue. Perhaps you can make this world just a little bit better.


  1. The list of 3D shapes are Pyramid, Cube, Sphere, Prism, Cylinder, Cone. 

  2. The list of 2D shapes are Circle, Square, Triangle. 

  3. Random people in the game are called blueberries, this is because on your radar, random players are showed as blue dots, aka blueberries.