HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Random Unit Problems

08-22-2006, 02:45 AM#1
wonder_priest
I'm making a demo map for a tutorial real quick, and it requires making a random combination of units at 22 regions, with one at each region. Sometimes it gives repeats (not allowed), and sometimes not all spots are filled. Here is the GUI (I need to try to make as much of the map in GUI as possible):

Trigger:
Hard Units
Collapse Events
Player - Player 1 (Red) skips a cinematic sequence
Conditions
Collapse Actions
Collapse For each (Integer A) from 1 to 22, do (Actions)
Collapse Loop - Actions
Set Temp_Int = (Random integer number between 1 and 22)
Collapse If (All Conditions are True) then do (Then Actions) else do (Else Actions)
Collapse If - Conditions
ArrayPositionFilled[Temp_Int] Equal to True
Collapse Then - Actions
Collapse For each (Integer B) from 1 to 22, do (Actions)
Collapse Loop - Actions
Collapse If (All Conditions are True) then do (Then Actions) else do (Else Actions)
Collapse If - Conditions
ArrayPositionFilled[(Integer A)] Equal to False
Collapse Then - Actions
Set Temp_Int = (Integer A)
Else - Actions
Else - Actions
Set ArrayPositionFilled[Temp_Int] = True
Custom script: call CreateRandomUnit(udg_Temp_Int)
Set Guys[(Integer A)] = (Last created unit)
Unit - Move Guys[(Integer A)] instantly to (Center of Spots[(Integer A)]), facing 270.00 degrees

And my random creation function:
Collapse JASS:
function CreateRandomUnit takes integer i returns nothing //Sadly, wc3 has no function to create a random unit, so I'll make one myself
local real x=GetRectMinX(bj_mapInitialPlayableArea)-30 //We'll create the units at the bottom corner of the map, then move them later
local real y=GetRectMinY(bj_mapInitialPlayableArea)-30 
 if i==1 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'hpea',x,y,0) //Create a peasant
  elseif i==2 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'uabo',x,y,0) //Create an abomination
  elseif i==3 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'ohun',x,y,0) //Headhunter
  elseif i==4 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'ogru',x,y,0) //Grunt
  elseif i==5 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'earc',x,y,0) //Archer
  elseif i==6 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'ospw',x,y,0) //Spirit Walker
  elseif i==6 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'opeo',x,y,0) //Peon
  elseif i==7 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'ugho',x,y,0) //Ghoul                       
  elseif i==8 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'emtg',x,y,0) //Mountain Giant
  elseif i==9 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'hmpr',x,y,0) //Priest
  elseif i==10 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'hsor',x,y,0) //Sorceress
  elseif i==11 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'hkni',x,y,0) //Knight
  elseif i==12 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'hspt',x,y,0) //Spell Breaker
  elseif i==13 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'hfoo',x,y,0) //Footman
  elseif i==14 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'ewsp',x,y,0) //Wisp
  elseif i==15 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'edoc',x,y,0) //Druid of the Claw
  elseif i==16 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'uaco',x,y,0) //Acolyte
  elseif i==17 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'uban',x,y,0) //Banshee
  elseif i==18 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'edot',x,y,0) //Druid of the Talon  
  elseif i==19 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'otau',x,y,0) //Tauren
  elseif i==20 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'esen',x,y,0) //Huntress
  elseif i==21 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'hrif',x,y,0) //Rifleman
  elseif i==22 then
   set bj_lastCreatedUnit=CreateUnit(Player(11),'edry',x,y,0) //Dryad
 endif
endfunction

Maybe it's just my eyes burning from the GUI, but I just can't see what is wrong

ADD: I'd really like to release this demo soon, so help would be much appreciated :)
08-22-2006, 03:19 AM#2
Guesst
Change A to B in that loop.
Trigger:
Hard Units
Collapse Events
Player - Player 1 (Red) skips a cinematic sequence
Conditions
Collapse Actions
Collapse For each (Integer A) from 1 to 22, do (Actions)
Collapse Loop - Actions
Set Temp_Int = (Random integer number between 1 and 22)
Collapse If (All Conditions are True) then do (Then Actions) else do (Else Actions)
Collapse If - Conditions
ArrayPositionFilled[Temp_Int] Equal to True
Collapse Then - Actions
Collapse For each (Integer B) from 1 to 22, do (Actions)
Collapse Loop - Actions
Collapse If (All Conditions are True) then do (Then Actions) else do (Else Actions)
Collapse If - Conditions
ArrayPositionFilled[(Integer B!!!!!)] Equal to False
Collapse Then - Actions
Set Temp_Int = (Integer B!!!!!)
Else - Actions
Else - Actions
Set ArrayPositionFilled[Temp_Int] = True
Custom script: call CreateRandomUnit(udg_Temp_Int)
Set Guys[(Integer A)] = (Last created unit)
Unit - Move Guys[(Integer A)] instantly to (Center of Spots[(Integer A)]), facing 270.00 degrees

A nice way to do things like that, though, is to have an array with indices 1-22, with each having a value being the same as its index:
Trigger:
For each (Integer A) from 1 to 22, do (Actions)
Collapse Loop - Actions
Set UnitIndex[Integer A] = Integer A
And then do a bunch of swaps:
Trigger:
For each (Integer A) from 1 to 200, do (Actions)
Collapse Loop - Actions
Set Index1 = (Random integer number between 1 and 22)
Set Index2 = (Random integer number between 1 and 22)
Set Temp = UnitIndex[Index1]
Set UnitIndex[Index1] = UnitIndex[Index2];
Set UnitIndex[Index2] = Temp;

And then create them:
Trigger:
For each (Integer A) from 1 to 22, do (Actions)
Collapse Loop - Actions
Custom script: call CreateRandomUnit(UnitIndex[Integer A])
Set Guys[(Integer A)] = (Last created unit)
Unit - Move Guys[(Integer A)] instantly to (Center of Spots[(Integer A)]), facing 270.00 degrees

Disclaimer: None of this is tested and I don't do triggers. Believe nothing.
08-22-2006, 03:31 AM#3
The_AwaKening
To fix the unit problem, just change your JASS trigger to set all those units to an Integer array loaded at your map initilization. Then make a loop to create all those units and put them in a group. You can then remove them one at a time to place them in your random regions. This way there is no chance of having identical units.
Collapse JASS:
 set udg_integer[1] = 'hpea'
 set udg_integer[2] = 'uabo'
 set udg_integer[3] = 'ohun'
and so on.... till you have all 22
Then for your grouping:
Collapse JASS:
 local group g=CreateGroup()
 local real x=GetRectMinX(bj_mapInitialPlayableArea)-30
 local real y=GetRectMinY(bj_mapInitialPlayableArea)-30 
 local integer i=1
 local integer u
    loop
        exitwhen i>22
        set u = CreateUnit(Player(11),udg_Integer[i]x,y,0)
        call GroupAddUnit(g,u)
        set i=i+1
    endloop
    loop
        set u=GroupPickRandomUnit(g)
        exitwhen u==null
        call SetUnitPosition(u,x,y)    // or SetUnitPositionLoc if you are using location
        call GroupRemoveUnit(g,u)
    endloop
    call DestroyGroup(g)

EDIT
someone else posted while I was typing, but I think this method should be best.
08-22-2006, 03:36 AM#4
wonder_priest
Well that's a neat trick but it doesn't really help me =P

Made it in JASS, works better, but still it forgets to create one or two, and there are still some repeats. This is bugging me alot.
Collapse JASS:
function Trig_Units_Actions takes nothing returns nothing
local integer ia=1
local integer ib=1
local integer temp
local boolean array filled
 loop
  set filled[ia]=false
  exitwhen ia==22
  set ia=ia+1
 endloop
  set ia=1
 loop 
  set temp=GetRandomInt(1,22)
   if filled[temp] then
    loop 
     if filled[temp]==false then
      set temp=ib
     endif
     exitwhen ib==22
      set ib=ib+1
    endloop
   endif
  set filled[temp]=true
  call CreateRandomUnit(temp)
  set udg_Guys[ia]=bj_lastCreatedUnit
  call SetUnitPositionLoc(udg_Guys[ia],GetRectCenter(udg_Spots[ia]))
   exitwhen ia==22
   set ia=ia+1
  endloop 
endfunction

//===========================================================================
function InitTrig_Units takes nothing returns nothing
    set gg_trg_Units = CreateTrigger(  )
    call TriggerRegisterPlayerEventEndCinematic( gg_trg_Units, Player(0) )
    call TriggerAddAction( gg_trg_Units, function Trig_Units_Actions )
endfunction

Can someone please tell me what I could possibly doing wrong!?!?!
08-22-2006, 03:39 AM#5
The_AwaKening
Use mine . It shouldn't leave any out and there won't be any repeats. It looks like you know enough about JASS to be able to use mine and apply your own stuff. You really don't need to change much except for the SetUnitPostionLoc

Edit: I did it for you
Collapse JASS:
function Trig_Units_Actions takes nothing returns nothing
 local group g=CreateGroup()
 local real x=GetRectMinX(bj_mapInitialPlayableArea)-30
 local real y=GetRectMinY(bj_mapInitialPlayableArea)-30 
 local integer i=1
 local integer u
 local location L
    loop
        exitwhen i>22
        set u = CreateUnit(Player(11),udg_Integer[i]x,y,0)
        call GroupAddUnit(g,u)
        set i=i+1
    endloop
    set i=1
    loop
        set u=GroupPickRandomUnit(g)
        exitwhen u==null
        set L = GetRectCenter(udg_Spots[i])
        call SetUnitPositionLoc(u,L)
        call RemoveLocation(L)
        call GroupRemoveUnit(g,u)
        set i=i+1
    endloop
    call DestroyGroup(g)
 set L=null
 set g=null
endfunction
Just remember to setup your unit integers in your map init "udg_Integer". Change the name to your liking =)
08-22-2006, 03:49 AM#6
wonder_priest
Ooops, I didn't even see yours. I paused when writing my reply, so I thought Guesst was still the last to reply.

Argh, I can't believe I made my CreateRandomUnit function so dumb >_>, I'm trying to rush this out before I succomb to my tiredness, so I blame it on that. Thanks for the reply, and I'll see if this works in the morning.
08-22-2006, 03:52 AM#7
The_AwaKening
Make sure to check my post one more time.. I had a minor typo in the location that I think I edited while you posted.
08-22-2006, 10:19 PM#8
wonder_priest
Okay, now I'm not getting repeats, but 3 units are missing evertime. Argh, I hate GUI.

Collapse JASS:
function CreateRandomUnit takes nothing returns nothing
 local group g=CreateGroup()
 local real x=GetRectCenterX(udg_Spots[udg_IntegerA])
 local real y=GetRectCenterY(udg_Spots[udg_IntegerA])
   set bj_lastCreatedUnit=CreateUnit(Player(11),udg_UnitId[udg_Temp_Int],x,y,270) //UnitId is an integer array storing 22 different unit ids
endfunction

Trigger:
Hard Units
Collapse Events
Player - Player 1 (Red) skips a cinematic sequence
Conditions
Collapse Actions
Trigger - Run Reset Booleans <gen> (checking conditions) //Runs a loop to set all "ArrayPositionFilled" to false
Collapse For each (Integer A) from 1 to 22, do (Actions)
Collapse Loop - Actions
Set Temp_Int = (Random integer number between 1 and 22)
Collapse If (All Conditions are True) then do (Then Actions) else do (Else Actions)
Collapse If - Conditions
ArrayPositionFilled[Temp_Int] Equal to True
Collapse Then - Actions
Collapse For each (Integer B) from 1 to 22, do (Actions)
Collapse Loop - Actions
Collapse If (All Conditions are True) then do (Then Actions) else do (Else Actions)
Collapse If - Conditions
ArrayPositionFilled[(Integer B)] Equal to False
Collapse Then - Actions
Set Temp_Int = (Integer B)
Else - Actions
Else - Actions
Set ArrayPositionFilled[Temp_Int] = True
Set IntegerA = (Integer A)
Custom script: call CreateRandomUnit()
Set Guys[(Integer A)] = (Last created unit)

Included the map if you want to look.
Attached Files
File type: w3xMemorization.w3x (24.5 KB)
08-22-2006, 10:31 PM#9
PipeDream
Here's the "right" way to shuffle. I think it can be translated to GUI fairly easy- no nested loops! You can think of it as a selection sort with a random selection.

Collapse JASS:
function AssignUnitTypeToSpot takes integer utype, integer spot returns nothing
endfunction

function Jumble takes integer n returns nothing
    local integer array perm
    local integer array sidedata
    local integer i = 0
    local integer temp
    local integer s
    loop
        exitwhen i>=n
        set perm[i] = i
        set sidedata[i] = 0 //Put your unit IDs into side data
        set i = i + 1
    endloop

//inplace Fisher-Yates O(N) shuffle
    set i = 0
    loop
        exitwhen i >= n
        set s = GetRandomInt(i,n)

        set temp = perm[i]
        set perm[i] = perm[s]
        set perm[s] = temp

        set i = i + 1
    endloop

    set i = 0
    loop
        exitwhen i >= n
        call AssignUnitTypeToSpot(sidedata[perm[i]],i)    
        set i = i + 1
    endloop
endfunction

I should mention that the warcraft random generator is almost certainly not good enough for proper shuffles.
08-22-2006, 11:07 PM#10
wonder_priest
It still forgets to create five units, and one unit ends up on the same spot as another. (called Jumble() in GUI)

Collapse JASS:
function AssignUnitToSpot takes integer id, integer spot returns nothing
 local real x=GetRectCenterX(udg_Spots[spot])
 local real y=GetRectCenterY(udg_Spots[spot])
  set udg_Guys[spot]=CreateUnit(Player(11),id,x,y,270)
endfunction

function Jumble takes nothing returns nothing
    local integer array perm
    local integer array sidedata
    local integer i = 0
    local integer temp
    local integer s
        local integer n=22
    loop
        exitwhen i>=n
        set perm[i] = i
        set sidedata[i] = udg_UnitId[i]
        set i = i + 1
    endloop

//inplace Fisher-Yates O(N) shuffle
    set i = 0
    loop
        exitwhen i >= n
        set s = GetRandomInt(i,n)

        set temp = perm[i]
        set perm[i] = perm[s]
        set perm[s] = temp

        set i = i + 1
    endloop

    set i = 0
    loop
        exitwhen i >= n
        set temp=sidedata[perm[i]]
                call AssignUnitToSpot(temp,i)    
        set i = i + 1
    endloop
endfunction

I really want to get out the sledgehammer.
08-22-2006, 11:14 PM#11
PipeDream
sorry about that, I didn't look close enough in my test! Had two off by one errors, one critical.
Collapse JASS:
//inplace Fisher-Yates O(N) shuffle
    set i = 0
    loop
        exitwhen i >= n-1 //we don't need to swap the last with itself.
        set s = GetRandomInt(i,n-1) //GetRandomInt is inclusive

        set temp = perm[i]
        set perm[i] = perm[s]
        set perm[s] = temp

        set i = i + 1
    endloop
08-22-2006, 11:25 PM#12
wonder_priest
Okay, now the problem of an extra unit being created on top of another is fixed, but five spots are left empty still (five units aren't created).
08-22-2006, 11:53 PM#13
PipeDream
Try this function by PitzerMike to print out the IDs and make sure that they match up with those in the unit editor. Also try to ping each of your regions to make sure they're set up correctly.