views:

50

answers:

1

My question is somewhat specific to my app's issue, but the answer should be instructive in terms of use cases for association logic and the record timestamp.

I have an NBA pick 'em game where I want to award badges for picking x number of games in a row correctly -- 10, 20, 30.

Here are the models, attributes, and associations in-play:

User
id

Pick
id
result # (values can be 'W', 'L', 'T', or nil. nil means hasn't resolved yet.)
resolved # (values can be true, false, or nil.)
game_time
created_at

*Note: There are cases where a pick's result field and resolved field will always be nil. Perhaps the game was cancelled.

Badge
id

Award
id
user_id
badge_id
created_at
  • User has many awards.
  • User has many picks.
  • Pick belongs to user.
  • Badge has many awards.
  • Award belongs to user.
  • Award belongs to badge.

One of the important rules here to capture in the code is that while a user can be awarded multiple streak badges (e.g., a user can win multiple 10-streak badges), the user CAN'T be awarded another badge for consecutive winning picks that were previously granted an award badge. One way to think of this is that all the dates of the winning picks must come after the date that the streak badge was awarded. For example, let's pretend that a user made 13 winning picks from May 5 to May 8, with the 10th winning pick occurring on May 7, and the last 3 on May 8. The user would be awarded a 10-streak badge on May 7. Now if the user makes another winning pick on May 9, the code must recognize that the user only has a streak of 4 winning picks, not 14, because the user already received an award for the first 10.

Now let's assume that the user makes 6 more winning picks. In this case, the code must recognize that all winning picks since May 5 are eligible for a 20-streak badge award, and make the award.

Another important rule is that when looking at a winning streak, we don't care about the game time, but rather when the pick was made (created_at). For example, let's say that Team A plays Team B on Sun. And Team C plays Team D on Sat. If the user picks Team C to beat Team D on Thurs, and Team A to beat Team B on Fri, and Team C wins on Sat, but Team A loses on Sun, then the user has a winning streak of 1. Team C was picked first.

So when must the streak-check kick-in? As soon as a pick is a win. If it's a loss or tie, no point in checking.

One more note: if the pick is not resolved (false) and the result is nil, that means the game was postponed and must be factored out.

With all that said, what is the most efficient, effective and lean way to determine whether a user has a 10-, 20- or 30-win streak?

A: 

I'd create a streak model, a user has_one streak, with 3 columns:

id
user_id
consecutive_wins integer default 0

OR

I'd add the consecutive_wins column to User.


Then, each time a bet resolves, if it's not a push, update the consecutive_wins.

  If it's a loss, consecutive_wins is reset to 0.
  If it's a win, consecutive_wins gets incremented by 1.
  If it's a win, award badges in this fashion (ruby'esque psuedo-code):

badge = 10
while badge < max_badge_size do

   if consecutive_wins < badge then
     break
   end

   if (consecutive_wins % badge == 0) then

     # Award the badge here

   end

   badge += 10

end

This simple code seems to cover your example. At win 2 of a 2 game streak, nothing is awarded. At win 10 of a 10 game streak, badge 10 is awarded, but not badge 20. At win 40 of a streak, it would award a 10 badge, a 20 badge, and a 40 badge, but not a 30 or 50 badge. At win 41 of a streak, it would award nothing.

Sean Johnson