SOLVED ds_grid_sort with two columns?

S

Slothagami

Guest
I am making a leaderboard for my game (using networking, GMS:2) and I'm new to ds_grids
I want to sort it so that the player with the highest killcount is at the top, and if two or more players have the same score their order is decided by health e.g:

player1 has score of 2 and 6 hp
player2 has score of 2 and 4 hp
player3 has score of 1 and 1 hp
player4 has score of 1 and 4 hp

I want it to be:

player1
player2
player4
player3

there will be not too many more than 4 players once the game is finished, I am open to making my own sorting script if I have to.
I have no idea how to though, here's my best attempt:
Code:
// o_remote_entity - draw GUI event
    if(o_client.target == id){ // if this is the player you're controlling
            // create and sort grid
            var
            scores = ds_grid_create(2, instance_number(o_remote_entity));
       
            for(var i = 0; i < instance_number(o_remote_entity); i++){
                var p = instance_find(o_remote_entity, i);
           
                ds_grid_add(scores, 0, i, p.killcount);
                ds_grid_add(scores, 1, i, p.name);
                ds_grid_add(scores, 2, i, p.hp);
            }
       
            ds_grid_sort(scores, 2, false);
            ds_grid_sort(scores, 0, false);

            // draw leaderboard
            var
            xx = camera_get_view_width(view_camera[0]) / 2 - 52,
            yy = 20,
            y2 = 6;
       
            draw_set_font(ft_small);
            draw_set_halign(fa_left);
            draw_set_color(c_black);
            draw_set_alpha(0.4);
           
            draw_rectangle(xx * SCREEN_RATIO, (yy - 2) * SCREEN_RATIO, (xx + 104) * SCREEN_RATIO, (yy + 2 + (y2 * ds_grid_height(scores))) * SCREEN_RATIO, false);
           
            draw_set_color(c_white);
            draw_set_alpha(1);
           
            for(var j = 0; j < ds_grid_height(scores); j++){
                draw_text((xx + 2) * SCREEN_RATIO, (yy + (y2 * j)) * SCREEN_RATIO, "#" + string(j + 1) + " (" + string(scores[# 0, j]) + ") " + scores[# 1, j]);   
            }
       
            // destroy data
            ds_grid_destroy(scores);
    }
(the hard coded numbers are temporary until I can get this to work)

Thanks in advance!
 
Last edited by a moderator:

chamaeleon

Member
I am making a leaderboard for my game (using networking, GMS:2) and I'm new to ds_grids
I want to sort it so that the player with the highest killcount is at the top, and if two or more players have the same score their order is decided by health e.g:

player1 has score of 2 and 6 hp
player2 has score of 2 and 4 hp
player3 has score of 1 and 1 hp
player4 has score of 1 and 4 hp

I want it to be:

player1
player2
player4
player3

there will be not too many more than 4 players once the game is finished, I am open to making my own sorting script if I have to.
I have no idea how to though, here's my best attempt:
Code:
// o_remote_entity - draw GUI event
    if(o_client.target == id){ // if this is the player you're controlling
            // create and sort grid
            var
            scores = ds_grid_create(2, instance_number(o_remote_entity));
       
            for(var i = 0; i < instance_number(o_remote_entity); i++){
                var p = instance_find(o_remote_entity, i);
           
                ds_grid_add(scores, 0, i, p.killcount);
                ds_grid_add(scores, 1, i, p.name);
                ds_grid_add(scores, 2, i, p.hp);
            }
       
            ds_grid_sort(scores, 2, false);
            ds_grid_sort(scores, 0, false);

            // draw leaderboard
            var
            xx = camera_get_view_width(view_camera[0]) / 2 - 52,
            yy = 20,
            y2 = 6;
       
            draw_set_font(ft_small);
            draw_set_halign(fa_left);
            draw_set_color(c_black);
            draw_set_alpha(0.4);
           
            draw_rectangle(xx * SCREEN_RATIO, (yy - 2) * SCREEN_RATIO, (xx + 104) * SCREEN_RATIO, (yy + 2 + (y2 * ds_grid_height(scores))) * SCREEN_RATIO, false);
           
            draw_set_color(c_white);
            draw_set_alpha(1);
           
            for(var j = 0; j < ds_grid_height(scores); j++){
                draw_text((xx + 2) * SCREEN_RATIO, (yy + (y2 * j)) * SCREEN_RATIO, "#" + string(j + 1) + " (" + string(scores[# 0, j]) + ") " + scores[# 1, j]);   
            }
       
            // destroy data
            ds_grid_destroy(scores);
    }
(the hard coded numbers are temporary until I can get this to work)

Thanks in advance!
I'm sure you realize that calling ds_grid_sort() twice in a row will more or less only result in the second call being relevant. Your problem is essentially that ds_grid_sort() can only work with one column. So, perhaps a way to "cheat" could be to create a string in one more column that is the concatenation of kill count and hp. The only caveat is of course that the string_format() total number of places (10 in my example) is sufficiently large to contain the highest value.
GML:
scores = ds_grid_create(3, instance_number(o_remote_entity));
...
var sort_string = string_format(p.killcount, 10, 0) + ":" + string_format(p.hp, 10, 0); // add more attributes as needed
ds_grid_add(scores, 3, i, sort_string);
...
ds_grid_sort(scores, 3, false);
 
H

Homunculus

Guest
I have a recursive script that allows sorting ds_grids by 2 or more columns. It's not pretty (nor fast), but it works:

GML:
/// @desc ds_grid_multisort(grid,[[col1,ord1],[col2,ord2],...])
/// @arg grid
/// @arg cols

/*
    Description:    sorts a ds_grid based on multiple columns. Column order determines
                    the priority (high to low)
    Arguments:      grid {ds_grid} : grid to sort
                    [col_N] {real} : column index to sort
                    [col_N_asc] {boolean} : sorts column N ascending (true) or descending (false)
*/

var _grid = argument0;
var _cols = argument1;

_ds_grid_multisort_part(_grid,_cols,0);
GML:
/// @desc _ds_grid_multisort_part(grid,args,iteration)

}
You only call the first one, ds_grid_multisort. Columns are passed as nested arrays. As an example, suppose you want to sort your grid primarily by column 3 in ascending order, and then by column 5 in descending order for the entries that are equal in column 3:

GML:
ds_grid_multisort(grid, [[3, true], [5, false]]);
Looking at it now, I could definitely make this a single script instead of two, not sure what I was thinking... back in GMS1 days though, you could have multiple scripts into one so I guess it wasn't really a priority.
 
Last edited by a moderator:
S

Slothagami

Guest
I'm sure you realize that calling ds_grid_sort() twice in a row will more or less only result in the second call being relevant. Your problem is essentially that ds_grid_sort() can only work with one column. So, perhaps a way to "cheat" could be to create a string in one more column that is the concatenation of kill count and hp. The only caveat is of course that the string_format() total number of places (10 in my example) is sufficiently large to contain the highest value.
GML:
scores = ds_grid_create(3, instance_number(o_remote_entity));
...
var sort_string = string_format(p.killcount, 10, 0) + ":" + string_format(p.hp, 10, 0); // add more attributes as needed
ds_grid_add(scores, 3, i, sort_string);
...
ds_grid_sort(scores, 3, false);
Thanks! based in that I had the idea to just add the percentage of the players health as a decimal to the end of the score and it worked!
 
Top