├── LICENSE ├── README.md ├── pawn.json └── sort-inline.inc /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # inline-sort 2 | 3 | Sort arrays inline. Similar to comparator sorts in [md-sort](https://github.com/oscar-broman/md-sort), but it does not use `CallLocalFunction` or any "#emit magic". 4 | 5 | The sort being used is merge sort, which is stable. That means you can sort an array multiple times on different conditions without losing the order. 6 | 7 | ## Basic syntax 8 | 9 | ``` 10 | sortInline array => (R = left > right) { 11 | R = comparison; 12 | } 13 | ``` 14 | 15 | * `array` - The array to sort. One-dimensional. 16 | * `left` - The left item being compared. 17 | * `right` - The right item being compared. 18 | * `R` - The comparison result. Boolean. 19 | * `R` should be `true` if `left > right` 20 | * `R` should be `false` if `right >= left` 21 | 22 | ## Examples 23 | 24 | This simple example uses helpers aimed specifically at sorting players. It's both concise and efficient. 25 | 26 | ```sourcepawn 27 | new g_Money[MAX_PLAYERS]; 28 | 29 | public OnSomething() 30 | { 31 | // Sort all players based on the value in g_Money 32 | new PlayerArray; 33 | 34 | sortPlayersInline top_money => (R = l > r) { 35 | R = g_Money[l] > g_Money[r]; 36 | } 37 | 38 | forPlayerArray (top_money => playerid) { 39 | printf("%d has $%d", playerid, g_Money[playerid]); 40 | } 41 | } 42 | ``` 43 | 44 | This is a more complex example, to show the flexibility of this include. 45 | 46 | ```sourcepawn 47 | #define MAX_PLAYERS 20 48 | // array of player IDs, in sorted order (0 - 19) 49 | new players[MAX_PLAYERS] = {0, 1, ...}; 50 | // pretend IsPlayerConnected 51 | new isConnected[MAX_PLAYERS] = {0, ...}; 52 | // pretend GetPlayerScore 53 | new score[MAX_PLAYERS] = {44, 471, 69, 155, 211, 397, 392, 284, 445, 80, 446, 345, 230, 151, 270, 179, 206, 178, 136, 176}; 54 | 55 | isConnected[2] = true; 56 | isConnected[4] = true; 57 | isConnected[8] = true; 58 | isConnected[9] = true; 59 | isConnected[15] = true; 60 | isConnected[19] = true; 61 | 62 | // Sort players by score 63 | sortInline players => (R = left > right) { 64 | R = score[left] < score[right]; 65 | } 66 | 67 | // Sort the array again, this time in 2 parts - connected and disconnected players 68 | sortInline players => (R = left > right) { 69 | R = isConnected[left] > isConnected[right]; 70 | } 71 | 72 | // Print the score of connected players, sorted by their score 73 | for (new i = 0; i < sizeof(players); i++) { 74 | new playerid = players[i]; 75 | if (!isConnected[playerid]) break; 76 | printf("score[%d] = %d", playerid, score[playerid]); 77 | } 78 | ``` 79 | 80 | ## Usage example 81 | 82 | This will print out the score of connected players, sorted by the score. 83 | 84 | ```sourcepawn 85 | new players_sorted[MAX_PLAYERS] = {0, 1, ...}; 86 | 87 | sortInline players_sorted => (R = left > right) { 88 | new lc = IsPlayerConnected(left); 89 | new rc = IsPlayerConnected(right); 90 | 91 | // Is one of the players disconnected? Put the disconnected player below the connected. 92 | if (lc != rc) { 93 | R = lc > rc; 94 | } else { 95 | R = GetPlayerScore(left) > GetPlayerScore(right); 96 | } 97 | } 98 | 99 | for (new i = 0; i < sizeof(players_sorted); i++) { 100 | new playerid = players_sorted[i]; 101 | if (!IsPlayerConnected(playerid)) break; 102 | 103 | printf("score of %d = %d", playerid, GetPlayerScore(playerid)); 104 | } 105 | ``` 106 | -------------------------------------------------------------------------------- /pawn.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": "oscar-broman", 3 | "repo": "samp-inline-sort", 4 | "dependencies": ["sampctl/samp-stdlib"] 5 | } -------------------------------------------------------------------------------- /sort-inline.inc: -------------------------------------------------------------------------------- 1 | #if defined _inc_sort_inline 2 | #endinput 3 | #endif 4 | #define _inc_sort_inline 5 | 6 | #define MergeSort%0(%1); \ 7 | { new _buf[sizeof(%1)]; MergeSortImpl(%1, _, _buf); } 8 | 9 | stock MergeSortImpl(arr[], size = sizeof(arr), buf[]) 10 | { 11 | if (size < 2) { 12 | return; 13 | } 14 | 15 | new mid = size / 2; 16 | 17 | MergeSortImpl(arr, mid, buf); 18 | MergeSortImpl(arr[mid], size - mid, buf[mid]); 19 | 20 | for (new i = 0; i < size; i++) { 21 | buf[i] = arr[i]; 22 | } 23 | 24 | for (new i, j, k = mid; i < size; i++) { 25 | if (k >= size) { 26 | arr[i] = buf[j++]; 27 | } else if (j >= mid) { 28 | arr[i] = buf[k++]; 29 | } else if (buf[j] > buf[k]) { 30 | arr[i] = buf[j++]; 31 | } else { 32 | arr[i] = buf[k++]; 33 | } 34 | } 35 | } 36 | 37 | #define MergeSortBU%0(%1); \ 38 | { new _buf[sizeof(%1)]; MergeSortBUImpl(%1, _, _buf); } 39 | 40 | stock MergeSortBUImpl(arr[], size = sizeof(arr), buf[], &initial = true, &sz, &lo, &mid, &hi, &i, &j, &k, &left, &right, &bool:r) 41 | { 42 | static const T = true; 43 | 44 | if (!initial) { 45 | goto comparison_done; 46 | } 47 | 48 | initial = false; 49 | 50 | for (sz = 1; sz < size; sz += sz) { 51 | for (lo = 0; lo < size - sz; lo += sz * 2) { 52 | mid = lo + sz - 1; 53 | hi = min(lo + sz * 2 - 1, size - 1); 54 | 55 | if (hi - lo < 1) { 56 | continue; 57 | } 58 | 59 | for (new n = lo; n <= hi; n++) { 60 | buf[n] = arr[n]; 61 | } 62 | 63 | for (i = lo, j = lo, k = mid + 1; i <= hi; i++) { 64 | if (k > hi) { 65 | arr[i] = buf[j++]; 66 | } else if (j > mid) { 67 | arr[i] = buf[k++]; 68 | } else { 69 | left = buf[j]; 70 | right = buf[k]; 71 | if (T) { 72 | return true; 73 | } 74 | comparison_done: 75 | if (r) { 76 | arr[i] = buf[j++]; 77 | } else { 78 | arr[i] = buf[k++]; 79 | } 80 | } 81 | } 82 | } 83 | } 84 | 85 | return false; 86 | } 87 | 88 | #define sortInline%1=>%0(%2=%3>%4) \ 89 | for ( \ 90 | new _buf[sizeof(%1)], _initial = true, _sz, _lo, _mid, _hi, _si_i, _si_j, _si_k, %3, %4, bool:%2; \ 91 | MergeSortBUImpl(_:%1, sizeof(%1), _buf, _initial, _sz, _lo, _mid, _hi, _si_i, _si_j, _si_k, _:%3, _:%4, %2); \ 92 | ) 93 | 94 | #define sortPartInline%1(%5)%0=>%0(%2=%3>%4) \ 95 | for ( \ 96 | new _buf[sizeof(%1)], _initial = true, _sz, _lo, _mid, _hi, _si_i, _si_j, _si_k, %3, %4, bool:%2; \ 97 | MergeSortBUImpl(_:%1, %5, _buf, _initial, _sz, _lo, _mid, _hi, _si_i, _si_j, _si_k, _:%3, _:%4, %2); \ 98 | ) 99 | 100 | #define sortPlayersInline%0\32;%1=>%0(%2=%3>%4) \ 101 | sortPartInline %1 (c_%1) => (%2 = %3 > %4) 102 | 103 | stock PreparePlayerArray(array[], bool:npc, size = sizeof(array)) 104 | { 105 | new m = min(GetPlayerPoolSize() + 1, size); 106 | new j = 0; 107 | new count = 0; 108 | 109 | for (new i = 0; i < m; i++) if (IsPlayerConnected(i) && (npc || !IsPlayerNPC(i))) { 110 | array[j++] = i; 111 | count++; 112 | } 113 | 114 | while (j < size) { 115 | array[j++] = INVALID_PLAYER_ID; 116 | } 117 | 118 | return count; 119 | } 120 | 121 | #define PlayerArray%0<%1> \ 122 | %1[MAX_PLAYERS], \ 123 | c_%1 = PreparePlayerArray(%1, false) 124 | 125 | #define PlayerArrayWithNPC%0<%1> \ 126 | %1[MAX_PLAYERS], \ 127 | c_%1 = PreparePlayerArray(%1, true) 128 | 129 | #define forPlayerArray%0(%1=>%2) \ 130 | for (new %2_i = 0, %2; %2_i < c_%1 && (%2 = %1[%2_i]) != INVALID_PLAYER_ID; %2_i++) 131 | --------------------------------------------------------------------------------