0

in a larger program that I am attempting to create in batch format it is important that I learn how to set a variables value to that of an arrays value as determined by the number of another variable if it is possible in a simple .bat file. my current code for the testing file reads as

    set a[1]=1
    set a[0]=10000
    set c=0
    set b=%a[%%c]% 
    echo %b%

all this returns is: ECHO is off I need it to return: 10000 so I can then use similar code

    @echo off
    setlocal enabledelayedexpansion

    set Goblin[0]=5
    set Goblin[1]=20
    set Goblin[2]=7
    set Goblin[3]=Goblin

    set Zombie[0]=7
    set Zombie[1]=15
    set Zombie[2]=3
    set Zombie[3]=Zombie

    set Skeleton[0]=3
    set Skeleton[1]=11
    set Skeleton[2]=10
    set Skeleton[3]=Skeleton

    set Orc[0]=7
    set Orc[1]=25
    set Orc[2]=0
    set Orc[3]=Orc

    set Acrobat[0]=4
    set Acrobat[1]=15
    set Acrobat[2]=80
    set Acrobat[3]=Acrobat

    set PossibleEnemies[0]=!Goblin!
    set PossibleEnemies[1]=!Zombie!
    set PossibleEnemies[2]=!Skeleton!
    set PossibleEnemies[3]=!Orc!
    set PossibleEnemies[4]=!Acrobat!

    set /a Enemy1=%random% %%5
    set /a Enemy2=%random% %%5

    set /a Enemy1.MaximumHealth=%PossibleEnemies[Enemy1[1]]%
    set /a Enemy2.MaximumHealth=%PossibleEnemies[Enemy2[1]]%
    set /a Enemy1.Damage=%PossibleEnemies[Enemy1[0]]%
    set /a Enemy2.Damage=%PossibleEnemies[Enemy2[0]]%
    set /a Enemy1.Dodge=%PossibleEnemies[Enemy1[2]]%
    set /a Enemy2.Dodge=%PossibleEnemies[Enemy2[2]]%
    set Enemy1.Name=%PossibleEnemies[Enemy1[3]]%
    set Enemy2.Name=%PossibleEnemies[Enemy2[3]]%

to randomize which enemy you are fighting(you fight two at once). I have also tried

    set a[1]=1
    set a[0]=10000
    set c=0
    set b=%a[%c%]% 
    echo %b%

and

    set a[1]=1
    set a[0]=10000
    set c=0
    set b=%a[!c!]% 
    echo %b%

as well as

    set a[1]=1
    set a[0]=10000
    set c=0
    set b=%a[c]% 
    echo %b%

to no avail

DNABuster
  • 3
  • 3
  • Use `SETLOCAL EnableDelayedExpansion"` as first command after the `@ECHO OFF` and then try `!a[%c%]!` in the scripts. – Darin May 13 '22 at 17:44
  • I have that in there I dont know if I listed it – DNABuster May 13 '22 at 17:50
  • 1
    [short how-to](https://stackoverflow.com/questions/30282784/variables-are-not-behaving-as-expected/30284028#30284028) (towards the end of my answer) and [a bit more in-depth](https://stackoverflow.com/questions/10166386/arrays-linked-lists-and-other-data-structures-in-cmd-exe-batch-script) (get yourself a big cup of coffee before visiting) – Stephan May 13 '22 at 17:55
  • thx stephan but what about arrays in arrays nested with variables sample code ``` a[1]=%b% b[1]=2 c=1 d=a[c[1]] ``` ? – DNABuster May 13 '22 at 18:09

3 Answers3

1

I believe there are other tricks that can do this, but basic method would be this:

@ECHO OFF
    SETLOCAL EnableDelayedExpansion
    set a[1]=1
    set a[0]=10000
    set c=0
    set b=!a[%c%]!
    echo %b%

EDIT: Stephan's comment had a link that included the trick I couldn't remember, but I knew it used CALL (NOTE: Call cost time and can slow a script down.):

@ECHO OFF
    set a[1]=1
    set a[0]=10000
    set c=0
    CALL SET b=%%a[%c%]%%
    echo %b%
Darin
  • 1,423
  • 1
  • 10
  • 12
  • wait I found something, might edit the question slightly in the big code I have arrays inside of the main array and I am finding the proper array to use, would this cause problems? if so how should I fix it? – DNABuster May 13 '22 at 17:53
  • well I tested it on the main program, and I got the same results I have been getting so I am going to assume that is a yes :/ – DNABuster May 13 '22 at 17:55
  • @DNABuster, you might be able to merge both tricks that I now have in the answer to get an array element pointed to by an array element. Or you might save the first element to a variable and then use that variable to get the content of the other array's element. – Darin May 13 '22 at 18:17
  • yeah thats a good fix(second one) – DNABuster May 13 '22 at 20:02
  • When you run what? – Darin May 13 '22 at 20:14
  • when I echo the variables it doesnt print, fix? – DNABuster May 13 '22 at 20:17
  • I had meant my full program, and it was because of the /a for some reason – DNABuster May 13 '22 at 20:17
1

There are no arrays in cmd/batch. The one and only variable type is STRING (although Lists and Arrays can be simulated to a certain degree). I therefore would change from arrays (independent variables for each element) to lists (one variable containing all elements) and put all "array/list stuff handling" into a subroutine, making it easy in the main code to get a certain element:

@echo off
setlocal enabledelayedexpansion

REM define readable names for indexes (for more readable code later)
set "MaxHealth=2"
set "Damage=1"
set "Dodge=3"
set "Name=4"
set "Goblin=1"
set "Zombie=2"
set "Skeleton=3"
set "Orc=4"
set "Acrobat=5"

REM define a list of properties for each creature
set "_Goblin=5,20,7,little Goblin"
set "_Zombie=7,15,3,angry Zombie"
set "_Skeleton=3,11,10,bony Skeleton"
set "_Orc=7,25,0,furious Orc"
set "_Acrobat=4,15,80,fast Acrobat"

REM define a list of all creatures
set "PossibleEnemies=Goblin,Zombie,Skeleton,Orc,Acrobat"

set /a Enemy1=%random% %%5
set /a Enemy2=%random% %%5

REM get properties
call :GetProperty %Enemy1% Name Enemy1
call :GetProperty %Enemy2% Name Enemy2
call :GetProperty %Enemy1% MaxHealth Enemy1
call :GetProperty %Enemy2% MaxHealth Enemy2
call :GetProperty %Enemy1% Damage Enemy1
call :GetProperty %Enemy2% Damage Enemy2
call :GetProperty %Enemy1% Dodge Enemy1
call :GetProperty %Enemy2% Dodge Enemy2

echo You fight against:
set Enemy
goto :eof

:GetProperty Type Property VarName  ; resulting variable = [VarName].[Property]
echo DBG: EnemyType=%1
echo DBG: Property=%2
for /f "tokens=%1 delims=," %%a in ("%PossibleEnemies%") do set "Type=%%a"
echo DBG: Type=%Type%
echo DBG: ReturnVar=%3.%2
set "opt=tokens=!%2! delims=," & REM not possible to use delayed expansion in for /f "[options]"
for /f "%opt%" %%a in ("!_%Type%!") do set "%3.%2=%%a"
echo DBG: Returnval=!%3.%2!
echo DBG: -----
goto :of

Example output (without the DBG lines):

You fight against:
Enemy1=3
Enemy1.Damage=3
Enemy1.Dodge=10
Enemy1.MaxHealth=11
Enemy1.Name=bony Skeleton
Enemy2=1
Enemy2.Damage=5
Enemy2.Dodge=7
Enemy2.MaxHealth=20
Enemy2.Name=little Goblin

If you want, you can put the REM get properties (all those call ...lines) into another subroutine for even more readable main code.

To answer your actual question: you need one layer of expansion per depth-layer. You have a variable containing a variable containing a variable, so you need three layers of parsing (the original layer plus two more. One more layer is usually achieved by delayed expansion; three layers are a bit more complicated: use call for an additional layer. The innermost variable is used "as usual" %c% for the next layer you have to double each %, as one % gets consumed by the additional layer of parsing (the second call), the same is true for the outermost (third) layer (the first call), resulting in four %, because each layer of parsing halves them:

@echo off
setlocal 
set a[2]=1000
set b[1]=2
set c=1
call call set d=%%%%a[%%b[%c%]%%]%%%%
echo %d%

result: 1000

Some words on efficiency:

As @Darin already mentioned, call is slow (because it creates another instance of cmd. Of course call call is even slower. Take a look at the times the three loops take (an empty loop, a "call-call" loop and a loop using another loop to force another layer of parsing):

@echo off
setlocal 
set a[2]=1000
set b[1]=2
set c=1

echo start empty loop %time%
for /l %%i in (1,1,10000) do (
  REM
)
echo start call-call %time%
for /l %%i in (1,1,10000) do (
  call call set d=%%%%a[%%b[%c%]%%]%%%%
)
echo (proof it worked: %d%)
echo start speed-optimized %time%
setlocal enabledelayedexpansion

for /l %%i in (1,1,10000) do (
    for %%j in (!b[%c%]!) do set "x=!a[%%j]!"
)
echo (proof it worked: %x%)
endlocal
echo End %time% 

Output:

start empty loop 16:20:49,05
start call-call 16:20:49,06
(proof it worked: 1000)
start speed-optimized 16:21:02,51
(proof it worked: 1000)
End 16:21:03,06

(~0.1 seconds / ~12 seconds / ~ 0,6 seconds) - YMMV depending on hardware and processor load. Ok, it takes 10000 iterations to get to those numbers, but nevertheless - its a factor of ~20)

Stephan
  • 53,940
  • 10
  • 58
  • 91
0

I found out that using arrays was a mistake, I believe this could be because if you use them like a[0] then it names the variable a[0] instead of making an array. So I ended up finding out that there is a much better solution(for my problem at least).

call set PossibleEnemies.Name.0=Goblin
call set PossibleEnemies.Name.1=Zombie
call set PossibleEnemies.Name.2=Skeleton
call set PossibleEnemies.Name.3=Orc
call set PossibleEnemies.Name.4=Acrobat

call set PossibleEnemies.Damage.0=5
call set PossibleEnemies.Damage.1=3
call set PossibleEnemies.Damage.2=3
call set PossibleEnemies.Damage.3=7
call set PossibleEnemies.Damage.4=4

call set PossibleEnemies.Health.0=20
call set PossibleEnemies.Health.1=15
call set PossibleEnemies.Health.2=11
call set PossibleEnemies.Health.3=25
call set PossibleEnemies.Health.4=15

call set PossibleEnemies.Dodge.0=7
call set PossibleEnemies.Dodge.1=3
call set PossibleEnemies.Dodge.2=10
call set PossibleEnemies.Dodge.3=0
call set PossibleEnemies.Dodge.4=80

:EnemySetup1
set /a Enemy1.Num=%random% %%5
set /a Enemy2.Num=%random% %%5
goto EnemySetup2

:EnemySetup2
call set "Enemy1.MaximumHealth=!PossibleEnemies.Health.%Enemy1.Num%!"
call set "Enemy2.MaximumHealth=!PossibleEnemies.Health.%Enemy2.Num%!"
call set "Enemy1.Health=!PossibleEnemies.Health.%Enemy1.Num%!"
call set "Enemy2.Health=!PossibleEnemies.Health.%Enemy2.Num%!"
call set "Enemy1.Damage=!PossibleEnemies.Damage.%Enemy1.Num%!"
call set "Enemy2.Damage=!PossibleEnemies.Damage.%Enemy2.Num%!"
call set "Enemy1.Dodge=!PossibleEnemies.Health.%Enemy1.Num%!"
call set "Enemy2.Dodge=!PossibleEnemies.Dodge.%Enemy2.Num%!"
call set Enemy1.Name=!PossibleEnemies.Name.%Enemy1.Num%!
call set Enemy2.Name=!PossibleEnemies.Name.%Enemy2.Num%!

Yeah so if you need to use arrays there will probably be a better option. For me it was just using nested variables. So if you are a beginner I would suggest avoiding variables AT ALL COSTS!

DNABuster
  • 3
  • 3