How to create nested variables in batch? - variables

I am trying to get nested variables in my batch game i am creating.
I want it so that it will choose a random variable and change it to X, but if it is already chosen, it should go back and choose a different number.
set 1=a
set 2=b
set 3=c
set 4=d
set 5=e
those were the variables, here is the code
setlocal enabledelayedexpansion
:eliminator
set /a eliminate=(%random * 5) / 32767 + 1
if %%eliminate%%==X goto eliminator
echo The letter !!eliminate!! was chosen
timeout 5
set %%eliminate%%=X
goto eliminator
Now, the thing is, when I try to echo it, it writes the name of the variable instead of the value. Also, variables that have already been chosen are being chosen again. Any way I could fix this? Thanks.

try this:
#echo off&setlocal
set "var1=a"
set "var2=b"
set "var3=c"
set "var4=d"
set "var5=e"
:loop
set /a rd=%random%%%5+1
if defined var%rd% call echo %%var%rd%%%
set "var%rd%="
set "var" >nul 2>&1 && goto:loop
..output (may vary):
d
a
c
b
e

Your posted code is missing the closing % around random - it should read %random%.
Your formula for a random number between 1 and 5 is more complicated than need be. I would use:
set /a eliminate=%random% %% 5 + 1
To expand a "nested variable" you need !%eliminate%!
But I would completely rewrite your algorithm. I think the following does what you want:
#echo off
setlocal enableDelayedExpansion
set "chars=abcde"
set charCnt=5
:loop
set /a "pos=%random% %% charCnt, pos2=pos+1, charCnt-=1"
set "letter=!chars:~%pos%,1!"
echo The letter %letter% was chosen
set "chars=!chars:~0,%pos%!!chars:~%pos2%!"
if defined chars goto loop
The script is optimized to always pick a valid unused letter on each iteration.

Related

Batch: variable definition useable in count loop

In my batch I want to copy a variable amount of source- to target destinations.
I want to define like this:
#setlocal EnableDelayedExpansion
set source1="C:\folder1"
set target1="f:\folder1"
set source2="C:\folder2"
set target2="f:\folder2"
...
set sourcen="C:\foldern"
set targetn="f:\foldern"
Dependently from a defined amount of folders
set numFolder=5
I want to go through the folders in a loop:
set /a COUNT=0
:LOOP
echo %COUNT%
set /a COUNT+=1
rem write the NAME of the parameter variable (source1,source2 etc.) in nameor
set "nameor=source%COUNT%"
rem write the VALUE of the parameter variable (source1,source2 etc.) into origin ("C:\folder1", "C:\folder2")
set "origin=%nameor%"
echo %origin%
if %COUNT% lss %numFolder% goto LOOP
When I show
echo %nameor%
I get what I expectet: source1, source2 etc.
but
echo %%%origin%%%
only provides
source1
instead of the expected value
"C:\folder1"
I thought, that I could resolve this by using DelayedExpansion but what did I miss?
To avoid confusion for me, I change the "origin" to "source". E.g. set "origin=%nameor%" changed to set "source=%nameor%".
To print out "C:\folder1" to "C:\foldern", you should use echo !%source%!, else you will just see "source1" to "sourcen".
Your problem is just about array element management. Try this:
#echo off
setlocal EnableDelayedExpansion
rem Define the two arrays
set i=0
for %%a in ("C:\folder1=f:\folder1"
"C:\folder2=f:\folder2"
"C:\foldern=f:\foldern") do (
set /A i+=1
for /F "tokens=1,2 delims==" %%b in (%%a) do (
set source!i!="%%a"
set target!i!="%%b"
)
)
rem Show up to numFolder elements of both arrays
set numFolder=5
for /L %%i in (1,1,%numFolder%) do (
echo %%i- Source%%i=!source%%i!, Target%%i=!target%%i!
)
The first part is equivalent to your series of individual element assignments. In this way is easier to add new pairs of values.
For further description on array management in Batch files, see: Arrays, linked lists and other data structures in cmd.exe (batch) script

Variable set in batch file won't show

When I do this in Notepad, the command prompt doesn't show ping %ip% -t -l %package%, but it shows ping %ip% -t -l and doesn't show the package variable.
#echo off
set data=0
set package = -600
IF %data% == 0 (
set /a package= %package% + 1600
#echo ping %ip% -t -l %package%
)
echo %package%
pause
What am I doing wrong?
Batch is sensitive to spaces in a SET statement. SET FLAG = N sets a variable named "FLAGSpace" to a value of "SpaceN"
The set "var=value" syntax ensures that any trailing spaces on the batch line are not included in the value assigned to var.
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).
Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.
Note therefore the use of CALL ECHO %%var%% which displays the changed value of var.
So:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set /A data=0
set /A package=-600
IF %data% == 0 (
set /a package=!package!+1600
echo ping %ip% -t -l !package!
)
echo %package%
pause
Noting: The setlocal statement should normally be placed at the start of the code. Your posted code is evidently a snip, since you do not appear to be setting ip.
Spaces are irrelevant in a set /a but even so, removing them fosters a habit
Set /a uses the run-time value, not the parse-time value of var when the syntax set /a var=var+1 is used within a loop, so set /a var=var+1 and set /a var=!var!+1 are equivalent but set /a var=%var%+1 uses the value of var at the time the loop is parsed.
Since echoing is set to off by the initial statement, the leading # on the second echo is redundant.

Windows Batch Variable within variable

In windows batch could you set a variable within a variable?
Explained:
So the %num% is within the variable.
set num=5
set cnum=test
set h1=%c%num%%
Is it possible to make the percents work like parenthesis?
The output should be h1=test
Any help would be appreciated.
Your example in your question is a mess, but I think I understand what you are looking for:
#echo off
setlocal
set C1=apple
set C2=orange
set C3=banana
set num=2
:: Inefficient way without delayed expansion
:: This will be noticeably slow if used in a tight loop with many iterations
call echo %%C%num%%%
:: The remaining methods require delayed expansion
setlocal enableDelayedExpansion
:: Efficient way 1
echo(
echo !C%num%!
:: Efficient way 2 - useful if inside parenthesized block
:: where %num% will not give current value
echo(
for %%N in (!num!) do echo !C%%N!
:: Showing all values via a loop
echo(
for /l %%N in (1 1 3) do echo !C%%N!
You may be looking for the Call Set command. This sets cnum to string c1, c2, c3 etc. It changes each time %num% changes. You can then use Call Set to assign any variable (h1 for example) to the value of the variable cnum stands for.
#echo off
setlocal enabledelayedexpansion
set c5=test
set num=5
:: set cnum to string "c5"
set cnum=c%num%
:: set h1 to to the existing variable c5
call set "h1=%%%cnum%%%"
echo %h1%
Are you after something like this?
cls
#echo off
setlocal EnableDelayedExpansion
Set num=5
Set "c!num!=test"
Echo !c5!
See http://ss64.com/nt/delayedexpansion.html for more info on delayed expansion.
cls
#echo off
setlocal EnableDelayedExpansion
set num=4
set c1=this is a demo
set c2=second example
set c3=third
set c4=this is the one I want
set c5=last
Echo !c%num%!

Variable Substring Assistance

Here's a snippet of my file:
set checkVal=0
:checkforStart
call set checkSub=%%_input:~%checkVal%,1%%
if /i %checkSub% EQU ( goto parenthesisCheck
if /i %checkSub% EQU ' goto noPar
set /a checkVal=%checkVal% + 1
goto checkforStart
:parenthesisCheck
set /a checkVal=%checkVal% + 1
set _startPar=%checkVal%
:checkforEnd
call set checkSub=%%_input:~%checkVal%,1%%
if /i %checkSub% EQU ) goto endParenthesis
if /i %checkSub% EQU ( goto parenthesisCheck
set /a checkVal=%checkVal% + 1
goto checkforEnd
:endParenthesis
set /a _endPar= %checkVal% - %_startPar%
call set parContents=%%_input=:~%_startPar%,%_endPar%%%
echo Parenthesis: %parContents%
:noPar
pause >nul
goto end
The problem I'm having is that when I try to return what was in the parenthesis, it returns:
~1,4
Or something along those lines, instead of the numbers or letters that are supposed to be isolated from the original input.
Thank you for any and all help.
Here is the full file, if you need it: https://www.dropbox.com/s/2lljcy5t7qme0nb/substring%20parenthesis%20finder.txt
That is the value you will get if you attempt to do something like set val=%var:~1,4% when var is undefined. Substring and search/replace operations do not work properly if the variable is undefined - instead they return the instructions after the colon. This is exactly the situation you have because your final assignment has an unwanted = in the expression.
You have:
call set parContents=%%_input=:~%_startPar%,%_endPar%%%
So it is looking to extract a substring from a variable named _input=, which cannot possibly exist - user defined variable names cannot contain =.
Easily fixed by removing the =
call set parContents=%%_input:~%_startPar%,%_endPar%%%
You should instead enable delayed expansion at the start of your script and use ! in place of the surrounding % symbols.
setLocal enableDelayedExpansion
set checkSub=!_input:~%checkVal%,1!
set parContents=!_input=:~%_startPar%,%_endPar%!
Note, you set the checkSub variable twice, it's exactly the same both times, so I didn't include it twice. There are three lines you need to change, and one to add.
....
:endParenthesis
set /a _endPar= %checkVal% - %_startPar%
CALL :inpar %_startPar% %_endPar%
echo Parenthesis: %parContents%
:noPar
GOTO :EOF
:inpar
CALL set parContents=%%_input:~%1,%2%%
GOTO :eof
This worked for me...
It seems that a line that used && that I did not post on here was causing the problem. Sorry for the trouble.

(Batch) How can I use two variables to refer to another variable in an IF statement?

This is part of the code for a game I'm making.
:south
set "message=You take a step South"
set /a "posY=%posY%+1" //Moves player down one tile
if "%p%posX%%posY%%"=="#" set /a "posY=%posY%-1" //checks if the player has hit a wall. If this is the case, bring him back one tile.
goto renderMap
Assuming %posX%==1 and %posY%==3 I'm trying to get the program to read the IF statement as:
if "p13"=="#" set /a "posY=%posY%-1"
But nothing I've tried seems to work. I was wondering if anyone could show me a proper way to do it.
#ECHO OFF
SETLOCAL
SET p13=#
SET posy=2
SET posx=1
SET /a posy=posy+1
CALL SET destsq=%%p%posx%%posy%%%
IF "%destsq%"=="#" (
ECHO hit wall - ouch!&SET /a posy-=1
) ELSE (ECHO moved south.)
This is probably easiest.
note that what is CALLed is
set destsq=
%% - % escapes the special meaning of %
p
%posx% - evaluated as 1
%posy% - evaluated as 3
%% - % escapes the special meaning of %
so the result is
set destq=%p13%
Note that set/a allows you to do operations without the % and also allows the form set /a var+=something to add %something% to var.
Of course,
set /a var += something
set /a var += %something%
set /a var = var + something
set /a var = %var% + something
set /a var = %var% + %something%
set /a var = var + %something%
all do precisely the same thing. Your choice about which style you use...
see
set /?
from the prompt for docco.
If you are writting a game in Batch, then the speed of your program is important.
There are three ways to use a variable as part of the name of another one: with CALL command, with FOR command, or using Delayed Expansion. CALL is the slowest one and Delayed Expansion the fastest.
Always try to use the shortest way to write any command. The fastest way to increment a variabe is set /A var+=1.
I strongly suggest you to use the standard array notation enclosing the subscripts in square braquets; this form is much clearer. You may read Arrays, linked lists and other data structures in cmd.exe (batch) script for further explanations on this point.
Below is your same code above, but including the previous points:
setlocal EnableDelayedExpansion
:south
set "message=You take a step South"
set /a posY+=1 //Moves player down one tile
if "!p[%posX%][%posY%]!" == "#" set /a posY-=1 //checks if the player has hit a wall. If this is the case, bring him back one tile.
goto renderMap
OK, here is an simple example where you can see, how it works:
#echo off&setlocal enabledelayedexpansion
set "posX=1"
set "posY=3"
set "p13=#"
set "pos=p%posX%%posY%"
if "!%pos%!"=="#" echo "#" found.
.. output is:
"#" found.