POST

How to become an automatic winner

A test engineer talks through the process of building and debugging an automated test to win the perfect circle game.

Introduction screen of the perfect circle game
Vole.wtf’s perfect circle game.

‘Can you draw the perfect circle?’ This is the aim of vole.wtf’s online game.  Draw a circle, and receive a rating — 100% equals perfect. But why do it myself when I can make the computer do it for me? I had recently been learning a new tool for automated tests, and I had a feeling that it could play the game just like any user could. All I would need to do is tell it where to draw, and it should win the game every time. Below I share my process behind creating a Cypress test script to win the game for me, and discovering some interesting facts about the game in the process.

My best manual attempt of drawing the perfect circle with a mouse. My artistic prowess is unmatched.

Breaking the game down into actions

Firstly, I break the perfect circle game down into three main actions that I can then use for the Cypress test: 

  1. Click the go button
  2. Press the mouse down on the drawing area at any point on the circle
  3. Move the mouse to the next point of the circle
  4. Repeat until the start point is reached
  5. Win

Additional information for the automated test

For the test to perform those actions however, it needs to target these parts of the page, so I have to determine how it will target them: 

  1. go button — Cypress can target elements based on what text shows to the user so I tell Cypress to find the first thing on the page with the word “Go” in it, and click that. No in-depth knowledge of html is needed here. 
  2. drawing area — I click ‘inspect element’ on the game and check the dom. From this I find out that the drawing area of the game is just a div inside a section. I notice there aren’t any other section elements on the page, so I tell Cypress to get the first section on the page, and then get the div inside it, and remember that as the drawspace. This lets me refer back to it later without having to find it again.

In addition to the page elements that need to be targeted, I also need to understand the positions of the circle in order to draw it correctly:

  1. position of the centre — I need to know the center of the page for the circle to be drawn around, I work this out through trial and error —  telling the test to click at a spot, then another spot, until I find the centre. This sounds inefficient but it’s quick and easy to edit the test, re-run it, and see the results immediately. 
  2. points of the circle — initially I try iterating through the pixels directly between each corner, but this results in a diamond not a circle, I should have seen that coming really. Instead, I find code online that allows me to generate many points of a circle based on the centre, radius, and number of points that I specify. It conveniently works in a loop, taking the current iteration as input for which point to generate, so it’s easy to make a loop that will drag the mouse to each spot as they’re generated.
  3. Number of points I want to draw — This needn’t be precise, but too many points and the test runs out of time trying to draw, too few and it wouldn’t be a circle, presumably. I find that 65 is a good amount so I go with that.

The test stages 

With all of that worked out, I put it together in a test that will:

Almost success

The test is complete. Each command is working correctly, the circle is being drawn, and I have my victory, almost:

Strange, that circle certainly looks as round as can be, yet the game won’t give it a 100% score? Is the game’s calculation off? Is something preventing the test from winning when a human would have?

No it turns out my method for finding the center of the page was slightly flawed. I thought that the center of the page was at 395, 395, but it was in fact 395.5, 395.5. The center of the page is a point between pixels, and that 7/10ths of a pixel off is enough to bring the score down by 0.3%

But once I’ve figured that out, the test can finally accomplish its mission.

Even at 100%, the game is still not satisfied, telling me to try again for a better score. This screen was never meant for mortal eyes.

Understanding the game through debugging 

With the test written, I now experiment drawing different circles by altering the radius, centre, and number of points used, and this quickly gives us some strange results. 

The circle above has 65 points, but what if I drew with only 10 points:

That doesn’t look quite perfect, and why are my lines curved? There should be direct, straight lines between each point.

What if it has only 4 points?

That’s really not right.

Even a just a circle with 2 points, also known as a line, is considered perfect by this game:

Why does that pass, but being off by less than a pixel fail? Moreover, why does that pass, but a circle with specifically 7 points fail?

I try debugging this, along with some other questions below.

Why are the lines between my points curved? 

I expect the lines that connect my points together to be straight, not curved. The curves are either being added by the perfect circle game or by Cypress. To test this, I make Cypress draw the same thing on a different site. I copy the test, make some slight adjustments, and that gives us this.

It doesn’t look the same, and that’s good because it means the curves are being added by the game, not by Cypress.

Why does the perfect circle game allow the test draw across the centre?

Usually the game stops this happening, and will say “too close to center”, but a 2 point circle draws straight through the centre. This one’s easy to test by manually drawing a quick line. 

Pow, it doesn’t care. It complains that I didn’t draw the full circle, but not that I crossed the line. That tells me it’s likely not paying attention to the lines, it’s only measuring specific points. It could be that it’s measuring where the mouse turns, or just picking up where the mouse is every x milliseconds. If I make a test that’s hardcoded to draw a line, but include a point near the centre, I see that it catches that. 

It does. This should still be too fast for it to catch, so it’s probably paying attention to the points where the mouse direction changes.

Key learnings:

The game doesn’t care about the lines, they’re imaginary. All it’s watching are the points the mouse moves to. It checks 3 things: 

  1. The points match perfectly in distance from the centre 
  2. None of the points are too close to the centre 
  3. The points happen in a singular direction around the centre (it stops you if you go the wrong way after starting) 

This is why the game accepts a square, because it’s a perfectly centred square. In fact even if I cut most of the points of one side out, it will still be a perfect shape because every point it does draw are the exact same distance from the center:

Since I understand now that the game is only looking at the points and not the lines, I have a theory for my final question:

Why doesn’t the game like a circle with 7 points? 

I suspect that 7 is the perfect number where some points are just barely not perfect enough, and there’s not enough of them for it to average out to 100%. How to test that? I experiment by setting the radius to 301 instead of 300 and:

Perfect this time. The circle should be drawn exactly the same, but the points will be slightly different, and I suspect slightly more perfect in the eyes of the game. This also explains why it doesn’t like other specific numbers of points, (50 for instance only got 99.8%) as no circle drawn with straight lines can ever be truly perfect. There’s always a chance for it to decide a point is just barely off.

Learning through testing   

Curiosity was the main driver behind this experiment, however I also came away with a better understanding of how the perfect circle game functions, and an idea for how similar experimentation could reveal functions of other systems as well. For more learnings and curiosities, subscribe to the Springlabs newsletter below.

TAGS