]> git.sesse.net Git - ccbs/blobdiff - bigscreen/groupscreen.cpp
Yet more refactoring.
[ccbs] / bigscreen / groupscreen.cpp
index a1141f077188567cba56d8371cb700923f8423c3..03b61db2f32e38cbf65e99808fee0646d96c943b 100644 (file)
@@ -216,166 +216,75 @@ void GroupScreen::find_column_widths(const Group &group, std::vector<unsigned> &
        }
 }
 
-// some refactoring done, more should be
-void GroupScreen::draw(unsigned char *buf, unsigned width, unsigned height)
+/* Find the first player with the fewest songs played and part of this machine. */
+const Player *GroupScreen::get_next_player(const Group &group)
 {
-       std::vector<TextDefer> td;
-       
-       scores_changed.reset_flag();
-       set_screen_size(width, height);
-
-       /*
-        * We'll probably need some values from here later on (although not all), just fetch them
-        * all while we're at it.
-        */
-       std::map<unsigned, unsigned> song_scores, player_scores;
-       conn.perform(FetchMaxScoreForSongs(tournament, &song_scores));
-       conn.perform(FetchMaxScoreForPlayers(tournament, round, &player_scores));
-       
-       Group group;
-       conn.perform(FetchGroup(tournament, round, parallel, &group));
-       gettimeofday(&last_updated, NULL);
-
-       memset(buf, 0, width * height * 4);
-
-       std::vector<unsigned> colwidth;
-       
-       draw_main_heading(td);
-       find_column_widths(group, colwidth);
-       draw_column_headings(td, group, colwidth);
-       draw_scores(td, group, colwidth);
-       
-       unsigned num_scores = group.players[0].scores.size();
-
-       /*
-        * Approximate (but probably working quite well in practice) heuristic
-        * for finding the min and max rank of a player works as follows:
-        *
-        * First of all, find out, for each player in the group, what the
-        * maximum remaining score possibly can be (the minimum score is of
-        * course identical to the player's current total). For a random song,
-        * this is of course 1000 * (maximum feet rating) (but of course, that
-        * depends on whether we can play single or double! for now, assume
-        * double is okay, but this logic will be deferred to FetchMaxScore
-        * anyhow); for a random song, we simply pick the highest-ranking song
-        * we can find, EXCEPT those the player has chosen earlier AND the
-        * random songs this round, AND all random songs from elimination rounds
-        * (ie. rounds with only one group). (Phew!) This doesn't solve problems
-        * we'd face with more than one chosen song, but it should be good enough.
-        *
-        * After we've found the max and min scores for all players, it's a simple
-        * matter of sorting; the best attainable rank for player X is obtained if 
-        * X gets max score and all others get min score, the worst attainable rank
-        * is obtained if X gets min score and all others get max score.
-        */
-       std::vector<unsigned> max_score, min_score;
-       for (std::vector<Player>::const_iterator i = group.players.begin(); i != group.players.end(); ++i) {
-               unsigned min_score_tp = 0, max_score_tp = 0;
-               for (std::vector<Score>::const_iterator j = i->scores.begin(); j != i->scores.end(); ++j) {
-                       if (j->score != -1) {
-                               // already given
-                               min_score_tp += j->score;
-                               max_score_tp += j->score;
-                       } else {
-                               unsigned max_score_this_song;
-                               if (j->song.id != -1) {
-                                       // random song, or we know what song the player picked
-                                       max_score_this_song = song_scores[j->song.id];
-                               } else {
-                                       max_score_this_song = player_scores[i->id];
-                               }
-                               max_score_tp += max_score_this_song;
-                       }
-               }
-               max_score.push_back(max_score_tp);
-               min_score.push_back(min_score_tp);
-       }
-
-       // now finally find min and max rank, and draw it all
-       unsigned show_players = get_show_players(group);
-       unsigned y = (show_players <= 7) ? 140 : (140 - (show_players - 7) * 5);
-       for (unsigned i = 0; i < group.players.size() && (i/num_machines) < show_players; ++i) {
-               unsigned best_rank = 1, worst_rank = 1;
-               for (unsigned j = 0; j < group.players.size(); ++j) {
-                       if (i == j)
-                               continue;
-
-                       if (max_score[i] < min_score[j])
-                               ++best_rank;
-                       if (min_score[i] <= max_score[j])
-                               ++worst_rank;
-               }
-
-               char text[16];
-               if (best_rank == worst_rank)
-                       std::sprintf(text, "%u", best_rank);
-               else
-                       std::sprintf(text, "%u-%u", best_rank, worst_rank);
-               
-               if (i % num_machines != machine)
-                       continue;
-               
-               // find out where to place this
-               unsigned x = 40 + colwidth[0];
-               for (unsigned j = 1; j <= num_scores + 1; ++j)
-                       x += colwidth[j] + 20;
-               
-               unsigned this_width = my_draw_text(text, NULL, 22.0);
-               my_draw_text_deferred(td, text, 22.0, x + colwidth[num_scores + 2] / 2 - this_width / 2, y);
-
-               if (show_players > 7)
-                       y += 40 - (show_players - 7) * 4;
-               else 
-                       y += 40;
-       }
-               
-       /*
-        * Next up (at the bottom) is "who's playing, what will he/she be playing, and
-        * optionally, how much to lead/win and how much to secure qualification" (the
-        * last one only in the final round). We assume playing is done in a modified
-        * zigzag; all the random songs are played first in zigzag/wrapping order (player
-        * 1 song 1, player 2 song 2, player 3 song 3, player 1 song 2, player 2 song 3,
-        * player 3 song 1, etc... assuming three songs and three players) and then all
-        * the chosen songs are played (we assume only one chosen song).
-        *
-        * The lines are as follows:
-        *
-        * <player>
-        * <song>
-        * High score: <hs> by <hsplayer> at <hsevent>
-        * Needs to lead: <leadscore>
-        * Needs to secure qualification: <qualscore>
-        * Needs to win group: <winscore>
-        */
-       
-       /* Find the first player with the fewest songs played and part of this machine. */
-       unsigned min_played_songs = 9999, num_random_songs = 0;
-       Player *next_player = NULL;
+       unsigned min_played_songs = 9999;
+       const Player *next_player = NULL;
        unsigned m = 0;
-       for (std::vector<Player>::iterator i = group.players.begin(); i != group.players.end(); ++i) {
-               unsigned this_played = 0, this_random_songs = 0;
+       for (std::vector<Player>::const_iterator i = group.players.begin(); i != group.players.end(); ++i) {
+               unsigned this_played = 0;
                for (std::vector<Score>::const_iterator j = i->scores.begin(); j != i->scores.end(); ++j) {
                        if (j->score != -1)
                                ++this_played;
-                       if (!j->chosen)
-                               ++this_random_songs;
                }
 
                if ((m++ % num_machines == machine) && this_played < min_played_songs) {
                        min_played_songs = this_played;
                        next_player = &(*i);
-                       num_random_songs = this_random_songs;  // should be equal for all
                }
        }
 
-       /* Find out what song this player is supposed to play next; try random songs first */ 
-       Score *next_song = NULL;
+       return next_player;
+}
+
+/*
+ * At the bottom, for a single player, is "who's playing, what will he/she be
+ * playing, and optionally, how much to lead/win and how much to secure
+ * qualification" (the last one only in the final round). We assume playing is
+ * done in a modified zigzag; all the random songs are played first in
+ * zigzag/wrapping order (player 1 song 1, player 2 song 2, player 3 song 3,
+ * player 1 song 2, player 2 song 3, player 3 song 1, etc... assuming three
+ * songs and three players) and then all the chosen songs are played (we assume
+ * only one chosen song).
+ *
+ * The lines are as follows:
+ *
+ * <player>
+ * <song>
+ * High score: <hs> by <hsplayer> at <hsevent>
+ * Needs to lead: <leadscore>
+ * Needs to secure qualification: <qualscore>
+ * Needs to win group: <winscore>
+ */
+void GroupScreen::draw_next_up_single(unsigned char *buf, const Group &group, const std::vector<unsigned> &colwidth,
+       std::map<unsigned, unsigned> &song_scores, std::map<unsigned, unsigned> &player_scores,
+       const std::vector<unsigned> &max_score, const std::vector<unsigned> &min_score)
+{
+        unsigned num_scores = group.players[0].scores.size();
+       
+       // Find out how many random songs there are (equal for all players).
+       unsigned num_random_songs = 0;
+       for (std::vector<Score>::const_iterator i = group.players[0].scores.begin(); i != group.players[0].scores.end(); ++i) {
+               if (!i->chosen)
+                       ++num_random_songs;
+       }
+
+       /* 
+        * Find out which player is next, and what song he she is supposed to play. First
+        * try random songs.
+        */
+       const Player *next_player = get_next_player(group);
+       const Score *next_song = NULL;
+       unsigned num_played = 0;  // will not always be completely accurate, but always as accurate as we need it :-)
 
        for (unsigned i = 0; i < num_random_songs; ++i) {
                unsigned j = (i + next_player->position - 1) % num_random_songs;
                if (next_player->scores[j].score == -1) {
                        next_song = &(next_player->scores[j]);
                        break;
+               } else {
+                       ++num_played;
                }
        }
 
@@ -386,6 +295,8 @@ void GroupScreen::draw(unsigned char *buf, unsigned width, unsigned height)
                        if (next_player->scores[j].score == -1) {
                                next_song = &(next_player->scores[j]);
                                break;
+                       } else {
+                               ++num_played;
                        }
                }
        }
@@ -411,7 +322,7 @@ void GroupScreen::draw(unsigned char *buf, unsigned width, unsigned height)
                }
 
                // only show lead/win/qualify for the last song
-               if (min_played_songs == num_scores - 1) {
+               if (num_played == num_scores - 1) {
                        /*
                         * Find out how much we need to lead, how much we need to be guaranteed
                         * to win the group, and how much we need to secure qualification. (FIXME:
@@ -542,6 +453,122 @@ void GroupScreen::draw(unsigned char *buf, unsigned width, unsigned height)
                        }
                }
        }
+}
+
+// some refactoring done, more should be
+void GroupScreen::draw(unsigned char *buf, unsigned width, unsigned height)
+{
+       std::vector<TextDefer> td;
+       
+       scores_changed.reset_flag();
+       set_screen_size(width, height);
+
+       /*
+        * We'll probably need some values from here later on (although not all), just fetch them
+        * all while we're at it.
+        */
+       std::map<unsigned, unsigned> song_scores, player_scores;
+       conn.perform(FetchMaxScoreForSongs(tournament, &song_scores));
+       conn.perform(FetchMaxScoreForPlayers(tournament, round, &player_scores));
+       
+       Group group;
+       conn.perform(FetchGroup(tournament, round, parallel, &group));
+       gettimeofday(&last_updated, NULL);
+
+       memset(buf, 0, width * height * 4);
+
+       std::vector<unsigned> colwidth;
+       
+       draw_main_heading(td);
+       find_column_widths(group, colwidth);
+       draw_column_headings(td, group, colwidth);
+       draw_scores(td, group, colwidth);
+       
+       unsigned num_scores = group.players[0].scores.size();
+
+       /*
+        * Approximate (but probably working quite well in practice) heuristic
+        * for finding the min and max rank of a player works as follows:
+        *
+        * First of all, find out, for each player in the group, what the
+        * maximum remaining score possibly can be (the minimum score is of
+        * course identical to the player's current total). For a random song,
+        * this is of course 1000 * (maximum feet rating) (but of course, that
+        * depends on whether we can play single or double! for now, assume
+        * double is okay, but this logic will be deferred to FetchMaxScore
+        * anyhow); for a random song, we simply pick the highest-ranking song
+        * we can find, EXCEPT those the player has chosen earlier AND the
+        * random songs this round, AND all random songs from elimination rounds
+        * (ie. rounds with only one group). (Phew!) This doesn't solve problems
+        * we'd face with more than one chosen song, but it should be good enough.
+        *
+        * After we've found the max and min scores for all players, it's a simple
+        * matter of sorting; the best attainable rank for player X is obtained if 
+        * X gets max score and all others get min score, the worst attainable rank
+        * is obtained if X gets min score and all others get max score.
+        */
+       std::vector<unsigned> max_score, min_score;
+       for (std::vector<Player>::const_iterator i = group.players.begin(); i != group.players.end(); ++i) {
+               unsigned min_score_tp = 0, max_score_tp = 0;
+               for (std::vector<Score>::const_iterator j = i->scores.begin(); j != i->scores.end(); ++j) {
+                       if (j->score != -1) {
+                               // already given
+                               min_score_tp += j->score;
+                               max_score_tp += j->score;
+                       } else {
+                               unsigned max_score_this_song;
+                               if (j->song.id != -1) {
+                                       // random song, or we know what song the player picked
+                                       max_score_this_song = song_scores[j->song.id];
+                               } else {
+                                       max_score_this_song = player_scores[i->id];
+                               }
+                               max_score_tp += max_score_this_song;
+                       }
+               }
+               max_score.push_back(max_score_tp);
+               min_score.push_back(min_score_tp);
+       }
+
+       // now finally find min and max rank, and draw it all
+       unsigned show_players = get_show_players(group);
+       unsigned y = (show_players <= 7) ? 140 : (140 - (show_players - 7) * 5);
+       for (unsigned i = 0; i < group.players.size() && (i/num_machines) < show_players; ++i) {
+               unsigned best_rank = 1, worst_rank = 1;
+               for (unsigned j = 0; j < group.players.size(); ++j) {
+                       if (i == j)
+                               continue;
+
+                       if (max_score[i] < min_score[j])
+                               ++best_rank;
+                       if (min_score[i] <= max_score[j])
+                               ++worst_rank;
+               }
+
+               char text[16];
+               if (best_rank == worst_rank)
+                       std::sprintf(text, "%u", best_rank);
+               else
+                       std::sprintf(text, "%u-%u", best_rank, worst_rank);
+               
+               if (i % num_machines != machine)
+                       continue;
+               
+               // find out where to place this
+               unsigned x = 40 + colwidth[0];
+               for (unsigned j = 1; j <= num_scores + 1; ++j)
+                       x += colwidth[j] + 20;
+               
+               unsigned this_width = my_draw_text(text, NULL, 22.0);
+               my_draw_text_deferred(td, text, 22.0, x + colwidth[num_scores + 2] / 2 - this_width / 2, y);
+
+               if (show_players > 7)
+                       y += 40 - (show_players - 7) * 4;
+               else 
+                       y += 40;
+       }
+       
+       draw_next_up_single(buf, group, colwidth, song_scores, player_scores, max_score, min_score);
        
        valid = true;
        draw_all_deferred_text(buf, td, last_text);