How to sort Variables High to Low in a passage?

Hi All,

I’m trying to add a passage that keeps track of a variable and sorts that variable Highest to lowest in the form of a chart.

In the example below I’ve used Name and Score.
The somewhat clunky code I’ve got works but has no provision for if 2 players have the same score and I think will be unworkable for a larger amount of players.
Ideally I want a list of 10 ordered High to Low.

Is there a better way to do what I’m doing?

Many many thanks!!

Twine Version: 2.35
Story Format: Harlowe 3.1.0

{
(set: $PLAYER_A to (datamap:
"name", "Rod",
"score", 10))
(set: $PLAYER_B to (datamap:
"name", "Jane",
"score", 20))
(set: $PLAYER_C to (datamap:
"name", "Freddy",
"score", 30))



<!--PLAYER A-->
(if: $PLAYER_A's score > $PLAYER_B's score and > $PLAYER_C's score)[(set: $Number1 to $PLAYER_A's name)]

(if: $PLAYER_A's score < $PLAYER_B's score and > $PLAYER_C's score)[(set: $Number2 to $PLAYER_A's name)]

(if: $PLAYER_A's score > $PLAYER_B's score and < $PLAYER_C's score)[(set: $Number2 to $PLAYER_A's name)]

(if: $PLAYER_A's score < $PLAYER_B's score and < $PLAYER_C's score)[(set: $Number3 to $PLAYER_A's name)]


<!--PLAYER_B-->
(if: $PLAYER_B's score > $PLAYER_A's score and > $PLAYER_C's score)[(set: $Number1 to $PLAYER_B's name)]

(if: $PLAYER_B's score < $PLAYER_A's score and > $PLAYER_C's score)[(set: $Number2 to $PLAYER_B's name)]

(if: $PLAYER_B's score > $PLAYER_A's score and < $PLAYER_C's score)[(set: $Number2 to $PLAYER_B's name)]

(if: $PLAYER_B's score < $PLAYER_A's score and < $PLAYER_C's score)[(set: $Number3 to $PLAYER_B's name)]


<!--PLAYER_C-->
(if: $PLAYER_C's score > $PLAYER_A's score and > $PLAYER_B's score)[(set: $Number1 to $PLAYER_C's name)]

(if: $PLAYER_C's score < $PLAYER_A's score and > $PLAYER_B's score)[(set: $Number2 to $PLAYER_C's name)]

(if: $PLAYER_C's score > $PLAYER_A's score and < $PLAYER_B's score)[(set: $Number2 to $PLAYER_C's name)]

(if: $PLAYER_C's score < $PLAYER_A's score and < $PLAYER_B's score)[(set: $Number3 to $PLAYER_C's name)]

Hi-score chart
<br>
<br>1. (print: $Number1)
<br>2. (print: $Number2)
<br>3. (print: $Number3)

}

First I would change from using one variable per Player to storing all players within a single Datamap…

(set: $players to (dm:
	"Player A", (dm: "name", "Rod", "score", 10),
	"Player B", (dm: "name", "Jane", "score", 20),
	"Player C", (dm: "name", "Freddy", "score", 30)
))

…this allows you to use the (dm-names:) macro to programmatically generate a list that contains the identifiers of all the players no matter how many you later add to the $players variable.

(set: _list to (dm-names: $players))

debug: before: (print: _list)

Next you can use the (sorted:) macro to sort the list of identifiers by the value of the associated score property looked up in the $players variable.

note: The (sorted:) macro only orders things in ascending alphanumerical order, so a little maths trickery is needed to get descending order.

(set: _list to (sorted: via (0 - $players's (it)'s score), ..._list))

debug: after: (print: _list)

Now that we have a sorted list of identifiers we can use a (for:) macro to output the Hi-score chart…

Hi-score chart {
(for: each _index, ...(range: 1, _list's length))[
	<br>(print: _index). (print: $players's (_list's (_index))'s name)
]
}

note: The (range:) macro is used to generate a list of numbers from 1 to the number of identifiers, and this is only needed because you wanted a number displayed before the player’s name.

1 Like

So elegant! Thank you so much!

I wonder is there a solution that doesn’t use (dm-names:) ?

I upgraded my game to the newest Twine so I could use the solution kindly provided. But I’m finding more and more things in my game that worked in Harlowe 3.1.0 that no longer work in 3.3.4.

Can you give some examples?

The SugarCube story format supports access a variable using a String representation of its Name via its State.variables object. So the following two examples product the same outcome…

Favourite fruit: <<= $fruit >>
Favourite fruit: <<= State.variables["fruit"] >> 

So in that story format you could use an Array of variable names to access each of your original $PLAYER_A like variables…

<<set _variables to ["PLAYER_A", "PLAYER_B", "PLAYER_C"]>>
<<for _name range _variables>>
   Player: <<= State.variables[_name].name>>; Score: <<= State.variables[_name].score>>
<</for>>

…and you could code similar to my Harlowe Data-map based example to sort the above _variables Array based on each player’s score.

As far as I’m aware Harlowe doesn’t include a method for accessing a variable using just a String representation of its Name, other than using what is called the Stupid Print Trick where you use a (print:) macro call to execute a String representation of a valid statement. So the SPT equivalent of…

(set: $fruit to "apple")

…would be something like…

(set: _variableName to "fruit")

(print: '(set: $' + _variableName + ' to "banana")' )

So while it may be technically possible to implement the Data-map based example I supplied earlier, using the Stupid Print Trick technique to access your original $PLAYER_A like variables, it would result in fairly dodgy code!

.toFixed() uses were throwing up error messages and also some javascript that I had opening up external windows.

I think I’m gonna transition to the newer Harlowe and concentrate on the sorted array stuff then fix the bugs as an when they become an issue. Thanks for all the help. :+1: