Sorting values.

The place to post your WML questions and answers.

Moderator: Forum Moderators

Forum rules
  • Please use [code] BBCode tags in your posts for embedding WML snippets.
  • To keep your code readable so that others can easily help you, make sure to indent it following our conventions.
BajMic
Posts: 72
Joined: January 24th, 2023, 1:22 am

Sorting values.

Post by BajMic »

Hello,

I am working on some hand-tailored AI scripts and I am currently trying to figure out how to sort a series of values. Let's say that I have 6 units and I want to have an array of their hp values sorted from max to min. How can I do this?

Thanks in advance.

EDIT:

On the same token, what would be the cleanest way (I can imagine the crude way) to sort six hexes based on how many enemies surround it?
gnombat
Posts: 892
Joined: June 10th, 2010, 8:49 pm

Re: Sorting values.

Post by gnombat »

You probably want table.sort in the Lua standard library.
BajMic
Posts: 72
Joined: January 24th, 2023, 1:22 am

Re: Sorting values.

Post by BajMic »

Ok, so let's say my table is named "Dudes".

Should I do it more or less like that?

[lua]
Dudes = table.sort (Dudes)
[/lua]

I am really lacking examples from the tutorials.
gnombat
Posts: 892
Joined: June 10th, 2010, 8:49 pm

Re: Sorting values.

Post by gnombat »

BajMic wrote: September 2nd, 2024, 11:52 am Ok, so let's say my table is named "Dudes".

Should I do it more or less like that?

[lua]
Dudes = table.sort (Dudes)
[/lua]
No, it's an in-place sort, so it doesn't return anything; it just modifies the array that is passed as an argument. So you would just write:

Code: Select all

  table.sort (Dudes)
An example:

Code: Select all

  -- get the units
  local Dudes = wesnoth.units.find_on_map { side = 1 }

  -- sort the units (by hp)
  local function compare_hp(a, b)
    if a.hitpoints > b.hitpoints then
      return true
    else
      return false
    end
  end
  table.sort(Dudes, compare_hp)

  -- display the units
  for i, Dude in ipairs(Dudes) do
    print(Dude.type .. ': ' .. Dude.name .. ': ' .. Dude.hitpoints .. ' hp')
  end
BajMic
Posts: 72
Joined: January 24th, 2023, 1:22 am

Re: Sorting values.

Post by BajMic »

Ok, so I suppose I can't do it the way I was guessing?

I.e., create list of hitpoint values using WML, then pass this list to lua?
gnombat
Posts: 892
Joined: June 10th, 2010, 8:49 pm

Re: Sorting values.

Post by gnombat »

BajMic wrote: September 2nd, 2024, 12:44 pm Ok, so I suppose I can't do it the way I was guessing?

I.e., create list of hitpoint values using WML, then pass this list to lua?
I'm not sure what you mean... you can sort a list of anything, including hitpoint values (numbers). For example, you might have 6 units with hitpoints {32, 36, 23, 35, 41, 42} and then after sorting that becomes {42, 41, 36, 35, 32, 23}. But that might not be very useful - that doesn't tell you which unit has 42 hp, which unit has 41 hp, etc. In most cases it would be more useful to sort a list of units.
BajMic
Posts: 72
Joined: January 24th, 2023, 1:22 am

Re: Sorting values.

Post by BajMic »

Ok, so my first concern is noobish - how do I integrate it into my [scenario][/scenario].

Technically I could identify units by their hp, i.e., "send the one with 23 hp here, then the one with 32 hp here".

So if I make a list using WML, i.e., store units, and then just put their $unit.hp into a list using just WML, not lua...

... then do I just make [lua][/lua] bracket to make the sorting operation from above?
white_haired_uncle
Posts: 1456
Joined: August 26th, 2018, 11:46 pm
Location: A country place, far outside the Wire

Re: Sorting values.

Post by white_haired_uncle »

This is untested, and I'm just plugging gnombat's sort algo in w/o really thinking about it (looks fine), but it should give you the basics:

Code: Select all

[lua]
code = <<
  local ul = wml.array_access.get "unit_list"  -- get the WML array 'unit_list' and put it in lua table 'ul'
 
 -- sort the units (by hp)
  local function compare_hp(a, b)
    if a.hitpoints > b.hitpoints then
      return true
    else
      return false
    end
  end
  table.sort(ul, compare_hp)

  wml.array_access.set("unit_list", ul)   -- put lua table 'ul' in WML array 'unit_list' 
>>
[/lua]
Speak softly, and carry Doombringer.
white_haired_uncle
Posts: 1456
Joined: August 26th, 2018, 11:46 pm
Location: A country place, far outside the Wire

Re: Sorting values.

Post by white_haired_uncle »

white_haired_uncle wrote: September 2nd, 2024, 1:51 pm This is untested, and I'm just plugging gnombat's sort algo in w/o really thinking about it (looks fine), but it should give you the basics:

Code: Select all

[lua]
code = <<
  local ul = wml.array_access.get "unit_list"  -- get the WML array 'unit_list' and put it in lua table 'ul'
 
 -- sort the units (by hp)
  local function compare_hp(a, b)
    if a.hitpoints > b.hitpoints then
      return true
    else
      return false
    end
  end
  table.sort(ul, compare_hp)

  wml.array_access.set("unit_list", ul)   -- put lua table 'ul' in WML array 'unit_list' 
>>
[/lua]
BTW, this assumes you just store the units into unit_list, and forget about
So if I make a list using WML, i.e., store units, and then just put their $unit.hp into a list using just WML, not lua...
Speak softly, and carry Doombringer.
BajMic
Posts: 72
Joined: January 24th, 2023, 1:22 am

Re: Sorting values.

Post by BajMic »

I planned on storing only hp due to my limited understanding of the lua. WML was my only point of reference, but now you have given me something to tinker with top-down. Particularly, I'm reading on wml.array_access.set now. Thanks.
white_haired_uncle
Posts: 1456
Joined: August 26th, 2018, 11:46 pm
Location: A country place, far outside the Wire

Re: Sorting values.

Post by white_haired_uncle »

You could store only hp, but if you store it as "hp" make sure to change the sort to use a.hp > b.hp and not a.hitpoints > b.hitpoints.

I don't think that makes any sense, but you could do it. If you're just getting started, just use the list of units from [store_unit], let lua sort it, and go from there.
Speak softly, and carry Doombringer.
User avatar
Celtic_Minstrel
Developer
Posts: 2371
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: Sorting values.

Post by Celtic_Minstrel »

white_haired_uncle wrote: September 2nd, 2024, 1:51 pm

Code: Select all

[lua]
code = <<
  local ul = wml.array_access.get "unit_list"  -- get the WML array 'unit_list' and put it in lua table 'ul'
 
 -- sort the units (by hp)
  local function compare_hp(a, b)
    if a.hitpoints > b.hitpoints then
      return true
    else
      return false
    end
  end
  table.sort(ul, compare_hp)

  wml.array_access.set("unit_list", ul)   -- put lua table 'ul' in WML array 'unit_list' 
>>
[/lua]
This is excessively verbose, just write it like this:

Code: Select all

table.sort(ul, function(a,b) return a.hitpoints > b.hitpoints end)
(Note, that only replaces the function definition and sort call from gnombat's example, not the entire quoted snippet.)
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
gfgtdf
Developer
Posts: 1452
Joined: February 10th, 2013, 2:25 pm

Re: Sorting values.

Post by gfgtdf »

table.sort is not a stable sort, and I'm not sure whether they exact outcome is guaranteed to be Platform Independent, if is it's using stable sort where elements might compare as equal, might result in oos.
Scenario with Robots SP scenario (1.11/1.12), allows you to build your units with components, PYR No preperation turn 1.12 mp-mod that allows you to select your units immideately after the game begins.
gnombat
Posts: 892
Joined: June 10th, 2010, 8:49 pm

Re: Sorting values.

Post by gnombat »

gfgtdf wrote: September 2nd, 2024, 7:26 pm table.sort is not a stable sort, and I'm not sure whether they exact outcome is guaranteed to be Platform Independent, if is it's using stable sort where elements might compare as equal, might result in oos.
If it were necessary to perform the sort in multiplayer, this would prevent oos, wouldn't it?

Code: Select all

  local function compare_hp(a, b)
    if a.hitpoints > b.hitpoints then
      return true
    elseif a.hitpoints < b.hitpoints then
      return false
    elseif a.id < b.id then
      return true
    else
      return false
    end
  end
gfgtdf
Developer
Posts: 1452
Joined: February 10th, 2013, 2:25 pm

Re: Sorting values.

Post by gfgtdf »

gnombat wrote: September 2nd, 2024, 7:51 pm
gfgtdf wrote: September 2nd, 2024, 7:26 pm table.sort is not a stable sort, and I'm not sure whether they exact outcome is guaranteed to be Platform Independent, if is it's using stable sort where elements might compare as equal, might result in oos.
If it were necessary to perform the sort in multiplayer, this would prevent oos, wouldn't it?

Code: Select all

  local function compare_hp(a, b)
    if a.hitpoints > b.hitpoints then
      return true
    elseif a.hitpoints < b.hitpoints then
      return false
    elseif a.id < b.id then
      return true
    else
      return false
    end
  end
hmm I don't know actually, in particular about whether the Id is guaranteed to be the same on all clients, I know for sure that there were issues in the past where the I'd was _not_ the same on all clients, but I don't remember them well enough to say for sure whether they have been fixed.

I do know however that many codes still refer to units by locations (for example the "rename" commandwhich actually complicates things since moves can be undone and then the location is no longer valid) Instead of the units id for these reasons

Also I want to point out that oos are also an issue in single player, for example when someone uses the "replay turn" button that was added to the menu in 1.18
Scenario with Robots SP scenario (1.11/1.12), allows you to build your units with components, PYR No preperation turn 1.12 mp-mod that allows you to select your units immideately after the game begins.
Post Reply