• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

Merge Sort translation issue

Status
Not open for further replies.
Hello Hive

I translated Merge Sort [GUI Friendly] v1.01 to Lua using cJass2Lua(v1.09) - Powerfull cJass and JASS converter and the map compiles fine without errors, and the merge sort is being ran. The problem is that I am getting unexpected results, and the units aren't properly sorted.

Furthermore one of the variables in one of the loops had to be changed for the map to even compile.


Doesn't compile;
  • For each (Integer BattleSystem_Int3) from 1 to TotalCurrentUnits , do (Actions)
    • Loop - Actions
      • Set MSort_Values[BattleSystem_Int3] = (Real(BattleSystem_TurnSpeed[BattleSystem_Int3]))
      • Set BattleSystem_Int4 = (BattleSystem_Int4 + 1)
      • Set MSort_IndexInitial[BattleSystem_Int3] = BattleSystem_Int4

Compiles;
  • For each (Integer BattleSystem_Int3) from 1 to 7, do (Actions)
    • Loop - Actions
      • Set MSort_Values[BattleSystem_Int3] = (Real(BattleSystem_TurnSpeed[BattleSystem_Int3]))
      • Set BattleSystem_Int4 = (BattleSystem_Int4 + 1)
      • Set MSort_IndexInitial[BattleSystem_Int3] = BattleSystem_Int4

Does anyone know what is going on?



Here's the translated code;
Lua:
-- TESH.scrollpos=89
-- TESH.alwaysfold=0
--               Merge Sort [GUI Friendly] v1.01
--                     by Flux
--       Flux
--
--       Merge Sort is an efficient sorting algorithm
--       that has a worst case performance of O(n logn)
--       This system allows you to sort values of an array
--       in either ascending or descending order
--       Additionally, it this system allows you to get the
--       sorted indices of the array values.
--
--       Purely written in JASS and doesn't require anything
---[USER=121133]@Return[/USER] nothing
function MSort_Display()
    local i = udg_MSort_StartIndex
    BJDebugMsg("========== |cffffcc00[Merge Sort]:|r Data ===============")
    while true do
        if i > udg_MSort_EndIndex then break end
        BJDebugMsg("MSort_Values[" .. I2S(i) .. "] = " .. R2S(udg_MSort_Values) .. ", MSort_Indices[" .. I2S(i) .. "] = " .. I2S(udg_MSort_Indices))
        i = i + 1
    end
    BJDebugMsg("============== End Display =================\n")
end


---@param leftStart integer
---@param leftEnd integer
---@param rightStart integer
---@param rightEnd integer
---[USER=121133]@Return[/USER] nothing
function MSort_Merge(leftStart, leftEnd, rightStart, rightEnd)
    local i = leftStart
    local j = rightStart
    local k = i
    print("Hello World")
    while true do
        if i > leftEnd or j > rightEnd then break end
        if udg_MSort_Ascending then
            if udg_MSort_Left < udg_MSort_Right[j] then
                udg_MSort_Values[k] = udg_MSort_Left
                udg_MSort_Indices[k] = udg_MSort_IndexInitial
                k = k + 1
                i = i + 1
            else
                udg_MSort_Values[k] = udg_MSort_Right[j]
                udg_MSort_Indices[k] = udg_MSort_IndexInitial[j]
                k = k + 1
                j = j + 1
            end
        else
            if udg_MSort_Left > udg_MSort_Right[j] then
                udg_MSort_Values[k] = udg_MSort_Left
                udg_MSort_Indices[k] = udg_MSort_IndexInitial
                k = k + 1
                i = i + 1
            else
                udg_MSort_Values[k] = udg_MSort_Right[j]
                udg_MSort_Indices[k] = udg_MSort_IndexInitial[j]
                k = k + 1
                j = j + 1
            end
        end
    end
    -- Fill up remaining
    while true do
        if i > leftEnd then break end
        udg_MSort_Values[k] = udg_MSort_Left
        udg_MSort_Indices[k] = udg_MSort_IndexInitial
        k = k + 1
        i = i + 1
    end
    while true do
        if j > rightEnd then break end
        udg_MSort_Values[k] = udg_MSort_Right[j]
        udg_MSort_Indices[k] = udg_MSort_IndexInitial[j]
        k = k + 1
        j = j + 1
    end
    -- Update IndexInitial
    i = leftStart
    while true do
        if i == k then break end
        udg_MSort_IndexInitial = udg_MSort_Indices
        i = i + 1
    end
end
-- Recursive Function

---@param start integer
---@param end_ integer
---[USER=121133]@Return[/USER] nothing
function MSort_Merge_Sort(start, end_)
    local i = start
    local mid
    if end_ - start >= 1 then
        mid = (end_ + start) / 2
        MSort_Merge_Sort(start, mid)
        MSort_Merge_Sort(mid + 1, end_)
        while true do
            if i > mid then break end
            udg_MSort_Left = udg_MSort_Values
            i = i + 1
        end
        while true do
            if i > end_ then break end
            udg_MSort_Right = udg_MSort_Values
            i = i + 1
        end
        MSort_Merge(start, mid, mid + 1, end_)
        -- MSort_Display()
        --  ^uncomment to display values
    end
end

---[USER=121133]@Return[/USER] boolean
function Trig_Merge_Sort_Main()
    MSort_Merge_Sort(udg_MSort_StartIndex, udg_MSort_EndIndex)
    return false
end
-- ===========================================================================
---[USER=121133]@Return[/USER] nothing
function InitTrig_Merge_Sort()
    gg_trg_Merge_Sort = CreateTrigger()
    TriggerAddCondition(gg_trg_Merge_Sort, Condition(Trig_Merge_Sort_Main))
end
 
Last edited:

Uncle

Warcraft Moderator
Level 65
Joined
Aug 10, 2018
Messages
6,679
So how exactly does your turn order system work? Because Lua already offers table sorting.
Lua:
--This will sort your table in ascending order
table.sort(yourTable)

--Calling this function will reverse the order of your table (arr), so you can make it descending
function ReverseTable(arr)
    local i, j = 1, #arr
    while i < j do
        arr[i], arr[j] = arr[j], arr[i]
        i = i + 1
        j = j - 1
    end
end

--This function will shuffle your table (arr), randomizing the elements
function ShuffleTable(arr)
    for i = #arr, 2, -1 do
        local j = math.random(i)
        arr[i], arr[j] = arr[j], arr[i]
    end
end

I feel like it'd be very simple to come up with something much better than that Jass system.

Edit:
Something like this. You call GetTurnOrder() and put your Array of Speeds in the parameters like GetTurnOrder(udg_BattleSystem_Speeds)
Lua:
function GetTurnOrder(speedTable)
    table.sort(speedTable) --This sorts the table values so that they are in ascending order (1 -> 10)
    ReverseTable(speedTable) --This reverses your table, which would be descending order in this case (10 -> 1)
end

function ReverseTable(arr)
    local i, j = 1, #arr
    while i < j do
        arr[i], arr[j] = arr[j], arr[i]
        i = i + 1
        j = j - 1
    end
end
udg_BattleSystem_Speeds will then be sorted in descending order.

And I think there's an even easier way of doing it with only using table.sort. Programming in Lua : 19.3
 
Last edited:
So how exactly does your turn order system work? Because Lua already offers table sorting.
Lua:
--This will sort your table in ascending order
table.sort(yourTable)

--Calling this function will reverse the order of your table (arr), so you can make it descending
function ReverseTable(arr)
    local i, j = 1, #arr
    while i < j do
        arr[i], arr[j] = arr[j], arr[i]
        i = i + 1
        j = j - 1
    end
end

--This function will shuffle your table (arr), randomizing the elements
function ShuffleTable(arr)
    for i = #arr, 2, -1 do
        local j = math.random(i)
        arr[i], arr[j] = arr[j], arr[i]
    end
end

I feel like it'd be very simple to come up with something much better than that Jass system.

I will let @xYours Trulyx answer that, since he's the one who needs this system.
 
To clarify:

Let's say there are 10 units and the variable BattleSystem_TotalCurrentUnits is now set to that amount.

BattleSystem_TurnSpeed are those units' custom values.

Merge Sort will now sort these units from highest to lowest in descending order, and will display those units in a multiboard.

However, despite the normal Merge Sort system working fine and arranges them into the right order, the compiled Lua Merge Sort does not work correctly.

First, they do not use the BattleSystem_TotalCurrentUnits' value. This was shown in the multiboard that it references an unknown/nonexistent unit. A value of more than 10, the actual amount of units, would not display anything. So, the value that Merge Sort used was higher than 10 or lower than 0 which was not the desired number.

Second, if that wasn't the issue, that means the Merge Sort was not compiled properly to Lua.
 

Uncle

Warcraft Moderator
Level 65
Joined
Aug 10, 2018
Messages
6,679
Ah, no clue, I imagine that system is pretty buggy...

But why is it necessary to use that system in the first place? If you're working in Lua then you have access to basically two lines of code that do what you want without issue.
 

Uncle

Warcraft Moderator
Level 65
Joined
Aug 10, 2018
Messages
6,679
Really? Would the Table Sorting you suggest above work without issue?

If so, where should I put the variables for it?

Would speedTable be the multiboard? What should arr[i], arr[j] = arr[j], arr[i] be?
Can't tell if sarcasm or a genuine question. If genuine, speedTable would be the Array that contains all of the Battle Unit's Speed values.
Lua:
function GetTurnOrder(speedTable)
    local total = #speedTable

    table.sort(speedTable) --This sorts the table values so that they are in ascending order (1 -> 10)
    ReverseTable(speedTable) --Thos reverse your table, in this case changing it to descending order (10 -> 1)
   
    --I'm not sure how your system works but I figured I'd give an example of what you could do next:
    --udg_BattleSystem_Units is a Unit Array of the units in battle
    --udg_Speed is the Speed stat for your units, kept track using a Unit Indexer
    --udg_BattleSystem_TurnOrder is the Unit Array that will get filled with the correct order of Units

    local n = 0 --the New turn order of your unit
    local s --used to Shuffle units with matching speeds
    local r --a Random integer
    local cv --custom value of the current unit
    for a=1,total do
        s = n + 1
        for b=1,total do
            cv = GetUnitUserData(udg_BattleSystem_Units[b])
            if udg_Speed[cv] == speedTable[a] then
                n = n + 1
                udg_BattleSystem_TurnOrder[n] = udg_BattleSystem_Units[b]
            end
        end
        --This shuffles and randomizes the order of units with matching speeds
        if s < n then
            for c=s,n do
                r = GetRandomInt(s, n)
                udg_BattleSystem_TurnOrder[c], udg_BattleSystem_TurnOrder[r] = udg_BattleSystem_TurnOrder[r], udg_BattleSystem_TurnOrder[c]
            end
        end
    end
end

function ReverseTable(arr)
    local i, j = 1, #arr
    while i < j do
        arr[i], arr[j] = arr[j], arr[i]
        i = i + 1
        j = j - 1
    end
end
Edit: Got it working, attached an example map.

After doing this I imagine you'll only need to adjust some variables to make it work with the rest of your triggers.
 

Attachments

  • Turn Order example 1.w3m
    17.6 KB · Views: 41
Last edited:

Uncle

Warcraft Moderator
Level 65
Joined
Aug 10, 2018
Messages
6,679
Wasn't sarcasm :p

Thanks! I'll try to use that one with a few edits here and there, and will tell later if it works or not.
Ah, lol, guess I was being defensive for no real reason at all, sorry about that.

Anyway, I edited the post with a working example, I think it's a nice solution.

After setting up the variables and running the system you'll end up with an Array of units (BattleSystem_TurnOrder) which are ordered from fastest to slowest. It also shuffles units that have matching Speed values, so they're chosen at random. That can be removed by deleting this:
Lua:
--This shuffles and randomizes the order of units with matching speeds
        if s < n then
            for c=s,n do
                r = GetRandomInt(s, n)
                udg_BattleSystem_TurnOrder[c], udg_BattleSystem_TurnOrder[r] = udg_BattleSystem_TurnOrder[r], udg_BattleSystem_TurnOrder[c]
            end
        end
 
Last edited:
Ah, lol, guess I was being defensive for no real reason at all, sorry about that.

Anyway, I edited the post with a working example, I think it's a nice solution.

After setting up the variables and running the system you'll end up with an Array of units (BattleSystem_TurnOrder) which are ordered from fastest to slowest. It also shuffles units that have matching Speed values, so they're chosen at random. That can be removed by deleting this:
Lua:
--This shuffles and randomizes the order of units with matching speeds
        if s < n then
            for c=s,n do
                r = GetRandomInt(s, n)
                udg_BattleSystem_TurnOrder[c], udg_BattleSystem_TurnOrder[r] = udg_BattleSystem_TurnOrder[r], udg_BattleSystem_TurnOrder[c]
            end
        end

The shuffle should add some nice RNG to the game. However, I couldn't open the test map. What's the version?
 

Uncle

Warcraft Moderator
Level 65
Joined
Aug 10, 2018
Messages
6,679
The shuffle should add some nice RNG to the game. However, I couldn't open the test map. What's the version?
I use the latest version, a new patch was released tonight.

Anyway, here are the triggers.
  • Setup
    • Events
      • Time - Elapsed game time is 0.10 seconds
    • Conditions
    • Actions
      • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Unit - Set the custom value of (Last created unit) to 1
      • Set VariableSet Speed[1] = 5.00
      • Set VariableSet BattleSystem_Speed[1] = Speed[1]
      • Set VariableSet BattleSystem_Units[1] = (Last created unit)
      • -------- --------
      • Unit - Create 1 Knight for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Unit - Set the custom value of (Last created unit) to 2
      • Set VariableSet Speed[2] = 7.00
      • Set VariableSet BattleSystem_Speed[2] = Speed[2]
      • Set VariableSet BattleSystem_Units[2] = (Last created unit)
      • -------- --------
      • -------- Mortar Team + Rifleman are tied in Speed so one of them will get priority at random --------
      • -------- --------
      • Unit - Create 1 Mortar Team for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Unit - Set the custom value of (Last created unit) to 3
      • Set VariableSet Speed[3] = 3.00
      • Set VariableSet BattleSystem_Speed[3] = Speed[3]
      • Set VariableSet BattleSystem_Units[3] = (Last created unit)
      • -------- --------
      • Unit - Create 1 Rifleman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Unit - Set the custom value of (Last created unit) to 4
      • Set VariableSet Speed[4] = 3.00
      • Set VariableSet BattleSystem_Speed[4] = Speed[4]
      • Set VariableSet BattleSystem_Units[4] = (Last created unit)
  • Demo
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • Game - Display to (All players) for 1.00 seconds the text: ===Run===
      • Custom script: GetTurnOrder(udg_BattleSystem_Speed)
      • For each (Integer A) from 1 to 4, do (Actions)
        • Loop - Actions
          • Set VariableSet BattleSystem_Units[(Integer A)] = BattleSystem_TurnOrder[(Integer A)]
          • Game - Display to (All players) for 60.00 seconds the text: (# + ((String((Integer A))) + :) + (Name of BattleSystem_TurnOrder[(Integer A)]))))
I assume you have your own versions of these variables already. BattleSystem_Speed contains each Battle unit's speed values. BattleSystem_Units contains the actual units. BattleSystem_TurnOrder is what contains the proper order of Units after you run the GetTurnOrder() function.

Note that BattleSystem_Units and BattleSystem_Speed don't have to be setup in any particular order, so don't get fooled by my Setup trigger.

Also, in the For Loop in the Demo trigger I'm setting BattleSystem_Units[IntA] = BattleSystem_TurnOrder[IntA]. This is totally unnecessary, I just wanted to make it so you could reference either Array to get the current turn order.
 
Last edited:
I use the latest version, a new patch was released tonight.

Anyway, here are the triggers.
  • Setup
    • Events
      • Time - Elapsed game time is 0.10 seconds
    • Conditions
    • Actions
      • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Unit - Set the custom value of (Last created unit) to 1
      • Set VariableSet Speed[1] = 5.00
      • Set VariableSet BattleSystem_Speed[1] = Speed[1]
      • Set VariableSet BattleSystem_Units[1] = (Last created unit)
      • -------- --------
      • Unit - Create 1 Knight for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Unit - Set the custom value of (Last created unit) to 2
      • Set VariableSet Speed[2] = 7.00
      • Set VariableSet BattleSystem_Speed[2] = Speed[2]
      • Set VariableSet BattleSystem_Units[2] = (Last created unit)
      • -------- --------
      • -------- Mortar Team + Rifleman are tied in Speed so one of them will get priority at random --------
      • -------- --------
      • Unit - Create 1 Mortar Team for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Unit - Set the custom value of (Last created unit) to 3
      • Set VariableSet Speed[3] = 3.00
      • Set VariableSet BattleSystem_Speed[3] = Speed[3]
      • Set VariableSet BattleSystem_Units[3] = (Last created unit)
      • -------- --------
      • Unit - Create 1 Rifleman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Unit - Set the custom value of (Last created unit) to 4
      • Set VariableSet Speed[4] = 3.00
      • Set VariableSet BattleSystem_Speed[4] = Speed[4]
      • Set VariableSet BattleSystem_Units[4] = (Last created unit)
  • Demo
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • Game - Display to (All players) for 1.00 seconds the text: ===Run===
      • Custom script: GetTurnOrder(udg_BattleSystem_Speed)
      • For each (Integer A) from 1 to 4, do (Actions)
        • Loop - Actions
          • Set VariableSet BattleSystem_Units[(Integer A)] = BattleSystem_TurnOrder[(Integer A)]
          • Game - Display to (All players) for 60.00 seconds the text: (# + ((String((Integer A))) + :) + (Name of BattleSystem_TurnOrder[(Integer A)]))))
I assume you have your own versions of these variables already. BattleSystem_Speed contains each Battle unit's speed values. BattleSystem_Units contains the actual units. BattleSystem_TurnOrder is what contains the proper order of Units after you run the GetTurnOrder() function.

Note that BattleSystem_Units and BattleSystem_Speed don't have to be setup in any particular order, so don't get fooled by my Setup trigger.

Also, in the For Loop in the Demo trigger I'm setting BattleSystem_Units[IntA] = BattleSystem_TurnOrder[IntA]. This is totally unnecessary, I just wanted to make it so you could reference either Array to get the current turn order.
You use custom value as an array, but in @xYours Trulyx's system he uses custom value as the speed stat. How can we adapt your system to such a solution?
 

Uncle

Warcraft Moderator
Level 65
Joined
Aug 10, 2018
Messages
6,679
You use custom value as an array, but in @xYours Trulyx's system he uses custom value as the speed stat. How can we adapt your system to such a solution?
Get rid of the Speed variable and change this line:
Lua:
if udg_Speed[cv] == speedTable[a] then
To:
Lua:
if cv == speedTable[a] then
If you wish to have multiple Stats beyond Speed I would highly recommend using a Unit Indexer as it expands Custom Value to be able to store an infinite number of stats per unit. Like:
  • Create 1 Paladin
  • Set Variable cv = Custom value of created unit
  • Set Variable Speed[cv] = 10.00
  • Set Variable Charisma[cv] = 13.00
  • Set Variable CritChance[cv] = 25.00
Note that the Unit Indexer would have to be made compatible with Lua. I think I have a Lua-friendly Unit Indexer that I made a while back if you'd like it.


Also, Unit Indexing is unnecessary if you're working in purely Lua, but it seems like you're using a hybrid Lua/GUI approach. If you were to code in entirely Lua you could skip the Unit Indexer because Lua allows you to save just about anything as the Index in your Arrays:
Lua:
Speed = {} --Create the Speed Array, in Lua you don't have to define the types of your variables
Charisma = {}
CritChance = {}

local u = CreateUnit(Player(0), FourCC("Hpal"), 0, 0, 270) --Create a Paladin and store it as a local unit variable
Speed[u] = 10.00 --See how "u" is the Index in the Array, this makes things so much easier
Charisma[u] = 13.00
CritChance[u] = 25.00

--An example of using this, let's say when a Hero levels up it gains +2.00 Speed:
--A unit gains a level -> local u = GetTriggerUnit() -> Speed[u] = Speed[u] + 2.00
So Speed, Charisma, and CritChance are all Arrays, and I'm plugging my unit directly into the Index for them. So as long as you have reference to a unit, you'll have reference to all of it's stats, no extra steps needed.
 
Last edited:
Status
Not open for further replies.
Top