Analysis of Block Smash Churn Levels

Author

Aisling Quinn

Introduction

Block Smash is a popular mobile game that involves players clearing a board of different size and shape blocks within a limited number of moves. In this file we will analyse key aspects of the game and various connections between different elements of game play. The key goal of this research is to find what leads to churns in game play and how we may be able to counteract these issues.

Block Smash Logo

Block Smash Game Play
library(tidyverse)
library(knitr)
library(kableExtra)
#install,packages("janitor")
library(janitor)
#install,packages("furniture")
library(furniture)
library(gghighlight)
#housekeeping
game <- read_csv("game_churn_assignment 03.11.25.csv")
game_clean <- game %>%
  clean_names()

game_clean$used_moves <- washer(game_clean$used_moves, -1, -2, -3, -4, -5)

How Frequent is Churning

The first piece of information to assess is the level of churning that Block Smash is experiencing.

ggplot(game_clean) +
  geom_bar(mapping = aes(x = churn, fill = churn)) +
             labs(title = "Count of Churns",
                  x = "Churn Status",
                  y = "Count") +
             scale_fill_manual(name = "Churn Status", 
                               values = c("#061b46", "#dc2b23")) +
theme_bw()

We can clearly see that the majority of Block Smash players are not churning, however, more insightful information can be gained by a percentage breakdown of the churn rate.

perc_game_clean <- game_clean %>%
  count(churn) %>%
  mutate(perc = n / sum(n) * 100) #Percentage breakdown

ggplot(perc_game_clean, aes(x = "", y = perc, fill = churn)) +
  geom_bar(position = "stack", stat = "identity") + #tells R not to agregate
  labs(title = "How Frequent is Churning?",
       x = "Responce",
       y = "Percentage") +
  scale_fill_manual(name = "Churn Status",
                    values = c("#061b46", "#dc2b23")) +
geom_text(aes(label = paste0(round(perc, 2),"%")), #paste0 adds % sign
            position = position_stack(vjust = 0.5), size = 5, colour = "white") +
  theme_bw()

We can see here that approximately 1/3 of player churn from the game. This is impressive as on average mobile games experience over 90% churn rate before day 30. Click here for more information on the churn rates of mobile games. With this being said, there is always room for improvement.

What Levels Cause the Most Churns

We need to assess at what point in the game the players are deciding to churn to further analyse what is causing churning.

ggplot (data = game_clean) +
  geom_histogram (mapping = aes(x = level, fill = ""),show.legend = FALSE) +
  labs(title = "What Levels Lead to Churning?",
       x = "Level",
       y = "Count of Churns") +
  scale_fill_manual(values = c("#061b46")) +
  theme_bw()

We can clearly see that the majority of players have churned from the game prior to level 2000. We can assume that the players who churned after this point left due to prolonged use or boredom. For this reason we will be assessing only the levels up to 2000 for the remainder of this assessment.

#housekeeping
top_game_clean <- game_clean %>%
                       filter(level <= 2000)

We can see from the above histogram that the lower levels which are suppose to be designed to train the player as they progress and engage them may are the ones where players are most likely to drop off. Further Assessment of these levels is required to identify where the issue lies.

most_freq_lv_churn <- top_game_clean %>%
                      filter(churn == "Yes") %>%
                      group_by(level) %>%
                      summarise(num_churns = n()) %>%
                      arrange(desc(num_churns))

top_10_rows <- head(most_freq_lv_churn, 10)#takes top 10 rows

kable(top_10_rows,
      col.names = c("Level", "Number of Churns"),
      align = "rr",
      format = "html",
      caption = "Levels With The Highest Number of Churns",
      table.attr = 'data-quarto-disable-processing = "true"') %>% 
  kable_styling(full_width = FALSE) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#091b44", underline = TRUE) %>%
  row_spec(1:10, color = "black", background = "#c2efff") #Would not run when using colour in English/Irish spelling
Levels With The Highest Number of Churns
Level Number of Churns
12 142
15 142
53 140
19 133
11 132
25 125
131 115
13 92
42 91
14 90

We can see from the above table that the levels with the most churns is 12 and 15. We can also see that all of the highest churns occur within the first 500 levels. These initial levels are typically designed as tutorial levels to teach the player how the game works and slowly introduce new elements. We can assume from the data that these tutorial levels are not progressing fast enough, leading to boredom and churning.

Churn Density Based on Level and Playtime

We ave looked at what levels are leading to churns but other factors of the game play are also significant. Next we will assess how churning is distributed based on levels and the playtime length of each player.

ggplot(top_game_clean, aes(x = level, y = play_time_sec, fill = churn)) +
  geom_bin2d(bins = 20) + #creates heat map based on frequency of data within certain bins 
  labs(title = "Churn Density by Level and Playtime",
       x = "Level",
       y = "Play Time (sec)") +
   scale_fill_manual(name = "Churn Status",
                     values = c("#091b44", "#dc2b23")) +
   theme_bw()

We can draw two clear conclusions from this heat map. First of all we can see that the shorter the play time, the more likely a player is to churn. Trying to find ways to encourage players to spend more time on each level is a key element to combating churning. Block Smash could consider testing the variety of game play based on churn levels, or try to introduce a reward system for playing the entire level.

We can also see that the player who spend the longest time on the initial levels are also likely to churn. For these initial levels we want to introduce quick, snappy wins that encourage the player to keep playing instead of spending so much time on the one level. Perhaps introducing hints after a few seconds of inactivity would help with this issue.

Why are players churning?

There are different elements of Block Smash that could be leading to churning including coins used, rolling losses, and scores.

stats_by_churn <- top_game_clean %>%
                  group_by(churn) %>%
                    summarise(avg_coins = round(mean(used_coins), 2),
                              avg_roll_loss = round(mean(rolling_losses), 2),
                              avg_score = round(mean(scores), 2))


kable(stats_by_churn,
      col.names = c("Did they churn?", "Average Coins Spent", "Average Rolling Losses", "Average Score"),
      align = "lrrr",
      format = "html",
      caption = "Factors Affecting Churn Rates",
      table.attr = 'data-quarto-disable-processing = "true"') %>% 
  kable_styling(full_width = FALSE) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#091b44", underline = TRUE) %>%
  row_spec(1:2, color = "black", background = "#c2efff")
Factors Affecting Churn Rates
Did they churn? Average Coins Spent Average Rolling Losses Average Score
No 1.16 12.16 3183.80
Yes 1.24 7.14 2895.57

From this table we can conclude that churning players spend slightly more coins on average, however their scores are lower. We could assume that players who churn do not feel as fulfilled because they spend more on the game but do not see the same result reflected in their scores. Block Smash should consider providing rewards for players who invest more money in the game as it leads to profit fro the company.

We can also see that rolling losses doesn’t appear to impact the likelihood of churning.

top_game_clean <- mutate(top_game_clean, scores_level = case_when(scores >= 7500 ~ "High Score",
                                                                  scores >= 5000 ~ "Above Average Score",
                                                                  scores >= 2500 ~ "Average Score",
                                                                  TRUE ~ "Low Score"),
                           scores_level = factor(scores_level, levels = c("High Score",
                                                           "Above Average Score",
                                                           "Average Score",
                                                           "Low Score")))

ggplot(top_game_clean) +
  geom_point(mapping = aes(x = used_coins, y = rolling_losses)) +
  facet_grid(scores_level ~ churn,
             labeller = label_wrap_gen(width = 9))  + #wraps the labels
labs(title = "Factors Affecting Churning", x = "Coins Used", y = "Number of Rolling Losses") +
  theme_bw()

Looking at this faceted scatter plot we can see that the people with a high score have a low number of rolling losses regardless of churn. The higher number of rolling losses actually appear to lead to fewer churns.

Time of Play In Relation to Churn

churn_yes <- top_game_clean %>%
  filter(churn == "Yes") %>%
  count()
hours_play <- top_game_clean %>%
group_by(hour) %>%
  filter(churn == "Yes") %>%
  summarise(churn = n())

ggplot(hours_play, aes(x = hour, y = churn)) +
  geom_line() +
  labs(title ="Start Time of Play in Relation to Churn Frequency", x = "Hour (24hr)", y ="Churn Count") +
  theme_bw()

From this line graph we can see the distribution of churns based on the hours at which players are active on the game. We can clearly see that the most likely time for churning is at 7pm (19hrs). This could indicate that players are using the game to unwind after work but are potentially not feeling engaged or get fed-up with the game. Block Smash could consider introducing a rewards wheel at this time, or introduce mini games that would take less time to play, but still discourage churning.

churn_by_day <- top_game_clean %>%
  filter(churn == "Yes") %>%
  count(day) 
churn_by_day$day = factor(churn_by_day$day, levels = c("Monday",
                                              "Tuesday",
                                              "Wednesday",
                                              "Thursday",
                                              "Friday",
                                              "Saturday",
                                              "Sunday"))

ggplot(churn_by_day, aes(x = day, y = n, fill = day)) +
  geom_col(show.legend = FALSE) +
  labs(title = "Number of Churns by Day of Week",
       subtitle = "There is little difference between the number of churns on days of the week.",
       x = "Day of Week",
       y = "Number of Churns") +
  gghighlight(day == "Thursday") +
  scale_fill_manual(name = "Day",
                    values = c("#dc2b23")) +
  theme_bw()

From the above bar chart we can see that there is only a slight difference in churns depending on the day of the week. The day with the most churns is Thursday, so Block Smash could consider creating some type of extra mini games that lead to rewards for a Thursday to engage players and reduce churning.

Conclusion

From our above assessment we can see that there is a potential relationship between the levels and the churn rate. Block Smash may want to run some test on these levels (level 12 and 15) and see where the issues lie. Is there an extra tutorial element that is not landing well with players? Is the level too difficult or too easy? Does it take too much thought to figure out where to put pieces? Block Smash may also want to conduct some qualitative research into the connection between churns and scores to see if it really is the lower scores that are causing players to leave the game.

Meme

RStudio Meme