Now that we have set all the storage related logic, we can focus on the algorithm that decides who can get which Achievement. Having 7 different types of Achievements naturally leads to the conclusion that each of them has to have its own implementation of this algorithm. This is true, but there is a slight catch we will get to a little bit later.
Here, we will be covering Game, Item and Achievement Achievement types.
Let us first think of GameAchievement
. The variables it has are:

days

days_consecutive

games

game_durations

games_unique
Attribute days is used to define how many days a Client has to pass this Achievement’s conditions and similarly, days_consecutive tells us the same thing but for consecutive days. Games tells us which Games can be played for this Achievement and game_durations tells us for how long. Attribute games_unique tells us how many Games from the variable Games are necessary.
With these 5 variables, we can define in our mind all the combination but the problem is now how to implement this neatly and give meaning to some of those variables when they are not defined. The first approach can be to have a lot of ifelse
statements covering the cases when some variables are defined and not, but this looks too messy and amateurlike for our tastes. The smart thing to do is to assign a meaning to undefined variables. So we say that days and days_consecutive are 1 (and mean all the data from desired period, regardless of days in this period) when Achievement manager doesn’t set them, empty games variable means all Games, empty game_durations means 1 second (at least some playtime) and empty games_unique means the count of all Games from games variables. Now we can approach the problem without having the main algorithm wonder what does it mean if it, for example, got no Games.
The main machinery checks whether a Client can get the Achievement, given the Client’s relevant data and Achievement variables. What is the Client’s relevant data? For GameAchievement
, this should be the list of dates, each of which is a list of Games the Client has played that day and the corresponding durations. Suppose we are handed this data by some GameGod entity and let us proceed with the algorithm.
The best approach is to simply loop through dates from Client’s data and count how many consecutive or regular days comply with Achievement’s conditions (variables). To comply with conditions, the games_unique number of Games that are both in Achievement’s Games variable and Client’s data must all be of at least duration defined in game_durations. We can break the loop as soon as we have the proof that Client can get this Achievement.
We are done. Are we done? Let us look at some examples.
What about this Achievement: “in 10 days, play PUBG and/or Fortnite for total of 10 hours”. This means the Client can play PUBG 2 hours and Fortnite 8 hours, or can play just PUBG for 10 hours. How is this covered by Achievement’s 5 variables? Sure, days and days_consecutive are considered to be 1 and all data from 10 days is combined as data for 1 day. Sure, Games variable contains PUBG and Fortnite. Sure, games_unique is 1. But what with the game_durations? 5 + 5? 1 + 9? It must be just 10, says the everyday normal guy. And he is almost right. But our algorithm is not an everyday normal guy, and “almost” is not sufficient, as we will see with our next 2 examples.
Consider now the similar Achievement: “in 10 days, play PUBG and Fortnite for 10 hours each”. This subtle difference in language that is easily understandable for humans, but hard for computers must also be somehow solved with just our 5 variables. The 2 Achievements mentioned differ only in the game_durations variable. The second one has game_durations = 10 + 10. Therefore, if algorithm notices that game_durations is just one number and Games variable contains multiple Games, it assumes that this means the sum of their durations. Otherwise, If game_durations has enough values as there are Games in Games variable, algorithm knows it is dealing with and Achievement like the second one mentioned above. It considers each duration for corresponding Game.
Keen reader might have noticed that there is something that might mess with the algorithm, as described so far; yet another interesting, fundamentally different, ItemAchievement
: “in 30 days, play PUBG or Fortnite or Apex Legends, but at least 2 of those, one for 2 hours and other for 1 hour”. It’s a little “pushing to the limits” example, but it is a valid Achievement. It would have games_unique = 2, game_durations = 2 + 1 and Games = PUBG, Fortnite, Apex Legends. The algorithm now cannot assume any above described case, and deals with this case by sorting the durations in descending order and finding the right ones.