Batch IF statement variable inside another variable - variables

I'm having a problem with a framework for a batch game I'm trying to create. I want to check if a variable is something other than a blank space. However, which variable I'm trying to check is in itself defined by two variables. For example:
if not %px%xplayerlocation%y%yplayerlocation%%==%blank% goto wherever
As you can see, the variable to be checked is determined by the values of %xplayerlocation% and %yplayerlocation%. To my knowledge, only the outermost %% signs are being read as being a variable and the inner ones are being read as literal percent signs. Does anyone know a way around this problem? I'll give any additional information if anyone needs it. Thanks.

#ECHO OFF
SETLOCAL
SET "blank= "
SET xplayerlocation=3
SET yplayerlocation=4
ECHO test with blank================
SET px3y4=%blank%
CALL SET varval=%%px%xplayerlocation%y%yplayerlocation%%%
if not "%varval%"=="%blank%" ECHO goto wherever - NOT blank
ECHO test with "Q"================
SET px3y4=Q
CALL SET varval=%%px%xplayerlocation%y%yplayerlocation%%%
if not "%varval%"=="%blank%" ECHO goto wherever - NOT blank
GOTO :EOF
This should get you out of trouble.

You have recognized the source of the problem, but your description of the behavior is incorrect. The parser will attempt to expand variables named px and y, and it will convert the final %% into %.
The Magoo solution will work, but using CALL is quite slow. That may not be a problem for many small scripts, but for a batch game it can kill performance.
You want delayed expansion. Include setlocal enableDelayedExpansion near the beginning of your script. Then use the following:
if not !px%xplayerlocation%y%yplayerlocation%!==%blank% goto wherever
Normal %var% expansion occurs early at parse time, and !var! expansion occurs late at execution time, so you get the proper result.
The above will not work if the value of %blank% is a space. The simplest solution would be to use delayed expansion for !blank! as well.
You might find yourself in a situation where the coordinate values need to be set and expanded within the same block of code, like in a FOR loop or IF statement:
setlocal enableDelayedExpansion
...
REM This does not work
for ... in (...) do (
...
set /a "xPlayerLocation+=xChange, yPlayerLocation+=yChange"
if not !px%xplayerlocation%y%yplayerlocation%!==!blank! REM doSomething
...
)
The above will not work because %var% expansion occurs at parse time, and the entire parenthesized block of code is parsed before any code is executed. So the expanded value is constant - it will expand to the value that existed before the loop started.
The solution is to transfer the coordinate values to FOR variables using delayed expansion:
setlocal enableDelayedExpansion
...
REM This works
for ... in (...) do (
...
set /a "xPlayerLocation+=xChange, yPlayerLocation+=yChange"
for %%X in (!xPlayerLocation!) do for %%Y in (!yPlayerLocation!) do (
if not !px%%X%y%%Y!==%blank% REM doSomething
)
...
)
or
setlocal enableDelayedExpansion
...
REM This also works
for ... in (...) do (
...
set /a "xPlayerLocation+=xChange, yPlayerLocation+=yChange"
for /f "tokens=1,2" %%X in ("!xPlayerLocation! !yPlayerLocation!") do (
if not !px%%X%y%%Y!==%blank% REM doSomething
)
...
)
If you are serious about developing a high quality game using batch, then you may be interested in studying the techniques I used in developing SNAKE.BAT - an arcade style game using only native batch commands. The beginning of the post is a bunch of code, but afterwards I describe a number of techniques I used for improving performance. It is advanced stuff, so don't try to absorb everything at once. Absorb what you can, and then revisit the post later on after you gain more experience.

Related

Batch - How to Echo %%Variable%%

I have a small problem with a batch file I'm working on.
Here's a simple sample:
I would like to get the string "THERE" as my result.
But the result I get is just "HELLO"
set hello=there
set a=h
set b=ello
set result=%a%%b%
echo %result%
I already tried something like this:
Echo %%result%%
And Sadly, it just gets me the result %HELLO%
Any help would be great. Thanks!
The reason that %%result%% gives you %result% on the output is that the %% tokens are interpreted first, and further interpretation is not done.
However, you can use that to your advantage, doing a second level of indirection with the following trick:
#echo off
set result=hello
echo %result%
call :iset second %%result%%
echo %second%
goto :eof
:iset
for /F "usebackq tokens=*" %%i in (`echo %%%2%%`) do set %1=%%i
goto :eof
The secret lies in passing the thing you want interpreted (in this case, %%result%% passes in the literal %result% as per the rules stated in the first paragraph, not the interpretation of it).
The for loop then echos the interpretation of that (hello) surrounded by %...% (again, the double %% reduces to %), so that it is also interpreted, and it uses that to set the target variable you also passed in.
The upshot is that it effectively gives you:
%(%result%)%
which is what you're after.
Might I suggest, however, that you start looking into Powershell, so that you don't have to perform these batch-gymnastics in future :-)

batch file difference between %variable% and %variable % or variable call with spaces

I've just started with batch file programming and testing variable usage momentary.
Does anyone know what is the difference between the 2 variable calls, the space before the last %
#echo off
set pathOS1="\\o1511\Pcs7ProjectO1511\OS1511\GraCS\"
ECHO We're working with %pathOS1%
ECHO We're working with %pathOS1 %
since the echo is different:
We're working with "\\o1511\Pcs7ProjectO1511\OS1511\GraCS\"
We're working with \\o1511\Pcs7ProjectO1511\OS1511\GraCS\
Delayed expansion is not enabled.
Spaces are allowed in the variable name.
set "data=100"
set "data =101"
echo %data%
echo %data %
set data
You have two variables with two similar values
So I summarize your answers:
This can happen if there's no setlocal at the start of the routine, any values set in the environment remain set, so if "pathos1 " was set previously, its value will be retained until it's cleared by a set "pathos1 =" statement or cmd is closed.
So, since I didn't close the cmd and didn't set setlocal, I called the previously set variable "pathos1 =".
It is good example for setlocal :)

Batch - set variable error; variable(%)(string)(%%a)(%)?

On the batch script I'm currently working on, I've encountered a bit of a problem.
What I want to happen is to set a variable as itself, but after it to be followed by a percentage mark, a letter, a number from a for /l (#,#,#) loop, then another percentage sign.
My code is currently as follows;
set value1=1
set value2=10
for /l %%a in (%value1%,1,%value2%) do (
set variable1=%variable1%%b%%a%
)
This doesn't give me a value for %variable1% at all, even after looping this 10 times. I've tried adding 'variable1= ' (without apostrophes) to the top, however that gave me the same result. After searching around, I figured that I should try and cancel out the %'s which are before the 'b' and after the 'a' - using %'s in front of them -,and my code ended up like this;
set value1=1
set value2=10
set variable1=
for /l %%a in (%value1%,1,%value2%) do (
set variable1=%variable1%%b%%a%
)
This is the closest I got, however %variable1% would change the value to '%b1%', then '%b2%', then '%b3%', etc. Instead of tiling them up next to each other.
My desired result would be for, by the end of the loop, %variable1% to have a value of; %b1%%b2%%b3%%b4%%b5%%b6%%b7%%b8%%b9%%b10%
Where is my code going wrong? It seems that it should give me my desired output, however quite clearly, it's not.
Thanks in advanced,
Try this:
#echo off
setlocal EnableDelayedExpansion
set value1=1
set value2=10
set variable1=
for /l %%a in (!value1!,1,!value2!) do (
set percent=%%
set variable1=!variable1!!percent!b%%a!percent!
)
echo !variable1!
You will have to add setlocal EnableDelayedExpansion as this will helps to expand the variable at execution time rather than at parse time.

(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.

windows command line giving error on calculation

I found this nice little tidbit of code here: https://stackoverflow.com/a/5262637/2128987
#echo off
set starttime=%TIME%
set startcsec=%STARTTIME:~9,2%
set startsecs=%STARTTIME:~6,2%
set startmins=%STARTTIME:~3,2%
set starthour=%STARTTIME:~0,2%
set /a starttime=(%starthour%*60*60*100)+(%startmins%*60*100)+(%startsecs%*100)+(%startcsec%)
:TimeThis
robocopy /e /NFL /NDL /NJH /NJS /nc /ns /np folder%rndfolder% %drvltr%:\f%dirnew%\
set endtime=%time%
set endcsec=%endTIME:~9,2%
set endsecs=%endTIME:~6,2%
set endmins=%endTIME:~3,2%
set endhour=%endTIME:~0,2%
if %endhour% LSS %starthour% set /a endhour+=24
set /a endtime=(%endhour%*60*60*100)+(%endmins%*60*100)+(%endsecs%*100)+(%endcsec%)
set /a timetaken= ( %endtime% - %starttime% )
set /a timetakens= %timetaken% / 100
set timetaken=%timetakens%.%timetaken:~-2%
echo.
echo Took: %timetaken% sec.
As a standalone program it works great. I am using it with a robocopy command basically to determine how long it takes to write a file.
I add one extra variable in it because I want to keep the raw seconds for calculation purposes. So I add the extra line set timeraw=%timetaken%:
set /a timetaken= ( %endtime% - %starttime% )
***set timeraw=%timetaken%***
set /a timetakens= %timetaken% / 100
set timetaken=%timetakens%.%timetaken:~-2%
My batch file also uses setlocal enabledelayedexpansion
Well sometimes it does not properly calculate the "starttime" or "endtime". It's keeps it as the raw time in 08:30:22.35 type format and results in the error:
Invalid number. Numeric constants are either decimal (17),hexadecima (0x11), or octal (021)
Well obviously because it contains non-numeric characters like the : character.
My batch file goes in a continuous loop forever as I am using it to read, write, delete files and folders for a specific torture test condition.
Any idea why it would intermittently not calculate the starttime or endtime variables?
edit:
I made some changes to my overall script. I no longer need enabledelayedexpansion, cleaned up some if then statements, and simplified code a little. But I still occasionally get it where the starttime or endtime variables remain as the raw time format of HH:MM:SS.CS and causes error in calculation.
Old question, but there are probably blocks of parentheses and when you change a variable within parentheses then you need to use delayed expansion.
Run this and examine the differences.
#echo off
set a=nothing
if z==z (
set a=b
echo %a%
)
pause
setlocal enabledelayedexpansion
set a=nothing
if z==z (
set a=b
echo !a!
)
pause
Gee - a question nearly a year old, with no answer.
I'll assume that the problem has now been solved, so as a matter of record, I'd conclude that the
"sometimes it does not properly calculate" is because the hour/minute/second/hundredths will contain "08" or "09" which are not octal numbers.
The solution is
set /a startcsec=1%STARTTIME:~9,2% - 100
and repeat with each of the other 3 start time-segments; then repeat again with the end parts.
In addition, it could be that the hour is being presented with 0s suppressed. In this case, I'd suggest
set starttime=0%TIME: =%
set starttime=%startTIME:~-11%
set /a startcsec=1%STARTTIME:~9,2% - 100
where the first line prefixes the time with a '0', and replaces Space with [nothing]
the second selects just the last 11 characters of the result
and the last is the familiar form, using the resultant hh:mm:ss.cc format.
(obviously, the remainder of the substring-and-calculate method needs also to be implemented)