Skip to content

Commit

Permalink
started to optimize rankings
Browse files Browse the repository at this point in the history
  • Loading branch information
edelkas committed Nov 24, 2020
1 parent 3f93785 commit 507d905
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 16 deletions.
23 changes: 9 additions & 14 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,21 @@ Eddy's TODO list for inne:
- "nevermind": delete last inne message prompted by that user.
- Implement NReality's asterisk to previous 0th holders using the new Archives feature.

\\ ------------------------ Userlevels ------------------------
- Browse (DONE)
- Search (DONE)
- Leaderboards (DONE)
- Screenshots (DONE)
- Download (DONE)
- Rankings (DONE)

Search using the local database (we would have to code a search function, it could consist on taking the search string into downcase, and separating each word by spaces/underscores/etc, and then the titles must match each word, regardless of order).
\\ ------------------------ Current ------------------------
- Optimize all rankings, and then others like cleanest/dirtiest.
- Scores table: Add column "tab", add indices for "tied_rank", "tab" and "highscoreable_type".
- Improve help (add command list, add help menu for each command).

Implement storage of all userlevel scores, to this end we should have a column in the userlevels table that contains the date when its scores were updated, and we regularly (e.g. daily) check the userlevel list sorted by this date in descending order, and update the scores of the first N userlevels (the ones that were updated the longest ago), say N = 5000 (this takes about 2 hours). Or we can have N = 2000 and also update the newest 2000 levels (since those are more likely to change).
\\ ------------------------ Userlevels ------------------------

Report:
- Store history (only the top 20 players for each of top20, top10, top5, 0th and point rankings, so 100 new entries per day).
- Unify "search" and "browse" for ease of use.
- Search using the local database (we would have to code a search function, it could consist on taking the search string into downcase, and separating each word by spaces/underscores/etc, and then the titles must match each word, regardless of order).
- Implement storage of all userlevel scores, to this end we should have a column in the userlevels table that contains the date when its scores were updated, and we regularly (e.g. daily) check the userlevel list sorted by this date in descending order, and update the scores of the first N userlevels (the ones that were updated the longest ago), say N = 5000 (this takes about 2 hours). Or we can have N = 2000 and also update the newest 2000 levels (since those are more likely to change).

Less priority:
- Demo analysis.
- Imagesearch: Show pictures of results (miniature) instead of text.
- Userlevel stats: How many unique authors, maps, averages, etc. Break down by mode and total.
- Another browse type: "random" (returns a random selection of maps)
- Simplify author search by having a separate query for it: "userlevel from/by/made by <username>".
- When an ID is not in the database, if it's close (or always), download newest, but without printing it.
- Store a daily list of all top 50 userlevels (just ID's for instance), so we can see how the hardest levels evolve.
Expand Down Expand Up @@ -95,7 +90,7 @@ Analysis:

\\ ------------------------ Hardcore ------------------------

- Cleanliness, and other functions.
- Cleanliness, splits, and other functions.

\\ ------------------------ Humanlike features ------------------------

Expand Down
5 changes: 4 additions & 1 deletion messages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def send_rankings(event)
rank = parse_rank(msg) || 1
ties = !!(msg =~ /ties/i)

t = Time.now
if msg =~ /average/i
if msg =~ /point/i
players = Player.where(id: Player.joins(:scores).group('players.id').having("count(highscoreable_id) > #{MIN_SCORES}").pluck(:id))
Expand All @@ -63,11 +64,13 @@ def send_rankings(event)
rankings = Player.rankings { |p| p.top_n_count(1, type, tabs, true) - p.top_n_count(1, type, tabs, false) }
header = "tied 0th rankings "
else
rankings = Player.rankings { |p| p.top_n_count(rank, type, tabs, ties) }
# rankings = Player.rankings { |p| p.top_n_count(rank, type, tabs, ties) }
rankings = Score.rank(:rank, rank - 1, type, tabs, ties)
rank = format_rank(rank)
ties = (ties ? "with ties " : "")
header = "#{rank} rankings #{ties}"
end
log(Time.now - t)

type = format_type(type)
tabs = format_tabs(tabs)
Expand Down
20 changes: 20 additions & 0 deletions models.rb
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,26 @@ class Score < ActiveRecord::Base
belongs_to :story, -> { where(scores: {highscoreable_type: 'Story'}) }, foreign_key: 'highscoreable_id'
default_scope -> { select("scores.*, score * 1.000 as score")} # Ensure 3 correct decimal places

def self.rank(ranking, n, type, tabs, ties)
rev = true # Whether to sort in asceding or descending order
scores = self.where(highscoreable_type: type.nil? ? ['Level', 'Episode'] : type.to_s)
scores = tabs.empty? ? scores : scores.includes(:level).where(levels: {tab: tabs}) +
scores.includes(:episode).where(episodes: {tab: tabs}) +
scores.includes(:story).where(stories: {tab: tabs})
# scores = tabs.empty? ? scores : scores.includes(:level).where(levels: {tab: tabs}).or(
# scores.includes(:episode).where(episodes: {tab: tabs})).or(
# scores.includes(:story).where(stories: {tab: tabs}))
case ranking
when :rank
scores.where("#{ties ? "tied_rank" : "rank"} <= #{n}")
.group(:player_id)
.order('count_id desc')
.count(:id)
.take(NUM_ENTRIES)
.map{ |p| [Player.find(p[0]), p[1]] }
end
end

def self.total_scores(type, tabs, secrets)
tabs = (tabs.empty? ? [:SI, :S, :SL, :SU, :SS, :SS2] : tabs)
tabs = (secrets ? tabs : tabs - [:SS, :SS2])
Expand Down
2 changes: 1 addition & 1 deletion userlevels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -988,7 +988,7 @@ def send_random_userlevel(event)

if amount > 1
maps = Userlevel.where(mode: mode, author: author).sample(amount)
event << "Random selection of #{amount} #{mode.to_s} userlevels#{!author.nil? ? "by #{author}" : ""}:" + format_userlevels(Userlevel::serial(maps), 0, (0..amount - 1))
event << "Random selection of #{amount} #{mode.to_s} userlevels#{!author.empty? ? " by #{author}" : ""}:" + format_userlevels(Userlevel::serial(maps), 0, (0..amount - 1))
else
map = Userlevel.where(mode: mode, author: author).sample
send_userlevel_screenshot(event, map)
Expand Down

0 comments on commit 507d905

Please sign in to comment.