ways to implement while loop into FOR loop

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
einstein1969
Expert
Posts: 976
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

ways to implement while loop into FOR loop

#1 Post by einstein1969 » 06 Aug 2014 15:48

Hi,

I have a cicle For and i have to implement a while cicle into this.

How to do?

example code:

Code: Select all

For /L %%N in (1,1,100) do (

  rem implement a while ...
  set /a k=1
  rem while k<10 do .....

)


the original code: not finished...

Code: Select all

::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Explorer3D by Francesco Poscetti aka einstein1969
::
:: Developed on monocore seven 32bit system.
::
::::::::::::::::::::::::::::::::::::::::::::::::::::

@echo off

setlocal EnableDelayedExpansion

rem resolution
set /a screen_W=120, screen_H= 80

rem FOV=0.6 = 6/10 real to integer
set /a FOV=6*10000/10

rem PI
set /a PI=31416

rem StepD=MovSpeed=0.1, RotationSpeed=0.05
set /a MovSpeed=1*10000/10, RotSpeed=5*10000/100

rem init frame/canvas
set /a frame.width=scree_W, frame.heigth=screen_H

rem initial state
set /a state.posx=3
set /a state.posy=3
set /a state.dirx=-1
set /a state.diry=0
set /a state.camx=0
set /a state.camy=FOV

call :LoadWorld

call :SetFullScreen


:loop
call :RenderScene
call :DrawScene
goto :loop

exit /b

:SetFullScreen

exit /b

:RenderScene

set /a tot=frame.width*frame.height-1

rem empty
For /L %%i in (0,1,!tot!) do set /a frame.data[%%i].r=0, frame.data[%%i].g=0, frame.data[%%i].b=0

set /a lastcol=frame.width-1

rem for each column
For /L %%c in (0,1,!lastcol!) do (

  set /a cameraX=2*%%c * 10000 / frame.width - 1

  set /a rayPosX=state.posx, rayPosY=state.posy

  rem CHECK cameraX on second line!
  set /a rayDirX=state.dirx + state.camx + cameraX
  set /a rayDirY=state.diry + state.camy + cameraX

  rem integer part
  set /a mapX=rayPosX/10000*10000, mapY=rayPosY/10000*10000

  rem ray length
  set /a _deltaDistX= 1 + (rayDirY*rayDiry)/(rayDirX*rayDirX)
  set /a _deltaDistY= 1 + (rayDirX*rayDirX)/(rayDirY*rayDirY)

  rem CALCOLARE la radice quadrata dei due precedenti.
  set deltaDistX= _deltaDistX
  set deltaDistY= _deltaDistY


  if !rayDirX! lss 0 (
    set /a stepX=-1
    set /a sideDistX=(rayPosX-mapX) * deltaDistX

  ) else (
    set /a stepX=1
    set /a sideDistX=(mapX + 1 - rayPosX) * deltaDistX
  )

  if !rayDirY! lss 0 (
    set /a stepY=-1
    set /a sideDistY=(rayPosY-mapY) * deltaDistY

  ) else (
    set /a stepY=1
    set /a sideDistY=(mapY + 1 - rayPosY) * deltaDistY
  )

  rem ray casting



)

exit /b

:LoadWorld


exit /b

:DrawScene

exit /b


einstein1969


einstein1969
Expert
Posts: 976
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: ways to implement while loop into FOR loop

#3 Post by einstein1969 » 06 Aug 2014 23:17

Thanks Squashman for post it.

Aacini had completed the while loop in this thread.

But is not clear how to implement inner a for /L...

more help appreciated...

einstein1969

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: ways to implement while loop into FOR loop

#4 Post by penpen » 07 Aug 2014 04:11

This may help you:

Code: Select all

@echo off
setlocal DisableDelayedExpansion
set LF=^


rem Previous two empty lines required - do not remove
set ";=%%LF%%^"
rem set "while=echo\|cmd /V:ON /D /Q /C for /L %%? in ^(0, 0, 0^) do if"
set "while=cmd /V:ON /D /Q /C for /L %%? in ^(0, 0, 0^) do if"
set "endwhile=^) else exit !whileResult!"

set /A index=1, whileResult=0
for /L %%a in (3, 1, 4) do (
   %while% !index! leq %%~a ^( %;%
      echo !index! %;%
      set /a whileResult+=index, index+=1 ^>nul %;%
    %endwhile%
   call echo Returned value: %%errorlevel%%
   echo(
)
endlocal
exit /b 0
Btw: I have removed the "echo\|" part, too; is it needed for anything?
If yes just use the rem line above.

The issue is caused by the unescaped '(' and ')' characters:

Code: Select all

:: this works because the cmd line interpreter doesn't search for ending )'s
for /L %%b in (3, 1, 4) do cmd /c for /l %%a in (1,1,2) do ( echo %%a %%b )

:: this doesn't works because the cmd line interpreter expects ending )'s (and searches for them)
for /L %%b in (3, 1, 4) do ( cmd /c for /l %%a in (1,1,2) do ( echo %%a %%b ) )

:: fix: escape all cmd parameter ('s and )'s , so it works
for /L %%b in (3, 1, 4) do ( cmd /c for %%a in ^(1,1,2^) do ^( echo %%a %%b ^) )

penpen

einstein1969
Expert
Posts: 976
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: ways to implement while loop into FOR loop

#5 Post by einstein1969 » 07 Aug 2014 05:07

thanks penpen,

I have added the code in my batch,

1- but the code in the inner while is not executed... (look at RenderScene subroutine)
2- And it is slow... There'is another faster method?

Code: Select all

::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Explorer3D by Francesco Poscetti aka einstein1969
::
:: Developed on monocore seven 32bit system.
::
::::::::::::::::::::::::::::::::::::::::::::::::::::

@echo off


setlocal DisableDelayedExpansion
set LF=^


rem Previous two empty lines required - do not remove
set ";=%%LF%%^"
set "while=cmd /V:ON /D /Q /C for /L %%? in ^(0,0,0^) do if"
set "endwhile=^) else exit !whileResult!"

setlocal EnableDelayedExpansion

rem resolution
set /a screen_W=120, screen_H= 80

rem FOV=0.6 = 6/10 real to integer
set /a FOV=6*10000/10

rem PI
set /a PI=31416

rem StepD=MovSpeed=0.1, RotationSpeed=0.05
set /a MovSpeed=1*10000/10, RotSpeed=5*10000/100

rem init frame/canvas
set /a frame.width=screen_W, frame.heigth=screen_H

rem initial state
set /a state.posx=3
set /a state.posy=3
set /a state.dirx=-1
set /a state.diry=0
set /a state.camx=0
set /a state.camy=FOV

call :LoadWorld

call :SetFullScreen


:loop
call :RenderScene
call :DrawScene
goto :loop

exit /b

:SetFullScreen

exit /b

:RenderScene

set /a tot=frame.width*frame.height-1


rem empty
For /L %%i in (0,1,!tot!) do set /a frame.data[%%i].r=0, frame.data[%%i].g=0, frame.data[%%i].b=0

set /a lastcol=frame.width-1

rem for each column
For /L %%c in (0,1,!lastcol!) do (

  set /a cameraX=2*%%c * 10000 / frame.width - 1

  set /a rayPosX=state.posx, rayPosY=state.posy

  rem CHECK cameraX on second line!
  set /a rayDirX=state.dirx + state.camx + cameraX
  set /a rayDirY=state.diry + state.camy + cameraX

  rem integer part
  set /a mapX=rayPosX/10000*10000, mapY=rayPosY/10000*10000

  rem ray length
  set /a "_deltaDistX= 1 + (rayDirY*rayDiry)/(rayDirX*rayDirX)"
  set /a "_deltaDistY= 1 + (rayDirX*rayDirX)/(rayDirY*rayDirY)"

  rem CALCOLARE la radice quadrata dei due precedenti.
  set deltaDistX= _deltaDistX
  set deltaDistY= _deltaDistY


  if !rayDirX! lss 0 (
    set /a stepX=-1
    set /a "sideDistX=(rayPosX-mapX) * deltaDistX"

  ) else (
    set /a stepX=1
    set /a "sideDistX=(mapX + 1 - rayPosX) * deltaDistX"
  )

  if !rayDirY! lss 0 (
    set /a stepY=-1
    set /a "sideDistY=(rayPosY-mapY) * deltaDistY"

  ) else (
    set /a stepY=1
    set /a "sideDistY=(mapY + 1 - rayPosY) * deltaDistY"
  )

  rem ray casting

  set /a cc=mapX + mapY * world.width
  for %%C in ("!cc!") do set /a wd=!world.data[%%C]!

  rem DEBUG
  set wd=0
  echo !wd!

  %while% !wd! equ 0 ( %;%

    echo inner: !wd! %;%
    set /a whileResult+=wd, wd=3 ^>nul %;%

  %endWhile%

  echo echo Returned value: %errorlevel%
 
)

exit /b



64
64
111111111111111111111111111   
1                 4       1
1 4 4 4 4 4 4 4 4 4 4 4 4 1
1                         1
1  3     22222   4        1
1        2       4        1
1  3     2       44444 4441
1  3   2222222       4    1
1            2            1
1444444444   2    222222221
14       4   2    2      21
14  222      2       44  21
14       4        2      21
1444444444        222222221
111111111111111111111111111


:LoadWorld

set /a world.width=64, world.height=64, ww=world.width-1, wh=world.height-1
For /L %%h in (0,1, !wh!) do (

  For /L %%w in (0,1, !ww!) do (

    set /a cc=%%w + %%h * world.width

    for %%C in ("!cc!") do if !random! lss 16384 (set world.data[%%C]=0) else set world.data[%%C]=1

  )

)

exit /b

:DrawScene

exit /b



einstein1969

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: ways to implement while loop into FOR loop

#6 Post by penpen » 07 Aug 2014 09:46

einstein1969 wrote:1- but the code in the inner while is not executed... (look at RenderScene subroutine)
The while loop was written for diabled delayed expansion.
You have to escape the !'s if you want to run with enabled delayed expansion.
Also you have forgotten to escape the ( in the "%while% ... ( %;%" line.
In addition you cannot use empty lines in the while loop, as this breaks the cmd parameter string; just avoid them, or use "%;%".

So this should help:

Code: Select all

:: ...
setlocal DisableDelayedExpansion
:: ...
set "endwhile=^) else exit ^^^!whileResult^^^!"
:: ...
setlocal EnableDelayedExpansion
:: ...
   %while% ^^^!wd^^^! equ 0 ^( %;%
      %;%
      echo inner: ^^^!wd^^^! %;%
      set /a whileResult+=wd, wd=3 ^>nul %;%
      %;%
   %endWhile%
   echo Returned value: !errorlevel!
)
:: ...


einstein1969 wrote:2- And it is slow... There'is another faster method?
It is mainly slow, i think, because you are execute "cmd" (!lastcol!+1) times per call to function ":RenderScene".

The faster method depends on what you want to do within the while loop.
Actually you could replace the while loop and the echo after with:

Code: Select all

   echo Returned value: 0
But i'm sure, that is not what you are looking for.

My favorite in such things is (adapted to your program; i'm not using the while macro) something like this:

Code: Select all

@echo off

if "%~1" == "/processLoop" ( goto :processLoop
) else ( goto :initialize
)
echo(
echo(Unexpected error occured.
exit /b 0


:initialize
setlocal disableDelayedExpansion
set "lastcol=180"
cmd /C "%~f0" /processLoop
endlocal
exit /b 0


:processLoop
setlocal enableDelayedExpansion

:: counter has the role of the outer %%c
set "counter=0"

:: if wLoop is defined
:: then perform your while loop body,
:: else perform for /L %%c in (0,1,!lastcol!) body
set "wLoop=0"

echo(!counter!, !lastcol!, !wLoop!

for /L %%a in (0, 0, 0) do if "!wLoop!" == "1" (
   echo(while loop body  !counter!, !lastcol!, "!wLoop!"
   echo(
   rem set wLoop to 0, if you want to leave the while part, and increase the counter
   if "true" == "true" (
      set "wLoop=0"
      set /A "counter+=1"
   )
) else if !counter! EQU !lastcol!  (
   echo for loop invariant handling  !counter!, !lastcol!, "!wLoop!"
   exit 0

   rem as you have used an endless loop, you may replace the exit 0 by: set "counter=0" to restart
) else (
   echo(for   loop body  !counter!, !lastcol!, "!wLoop!"

   rem define wLoop as 1, if you want to do a while loop
   if "true" == "true" (
      set "wLoop=1"
   ) else (
      rem increase the counter if not within a
      set /A "counter+=1"
   )
)

echo(
echo(Unexpected error occured.
endlocal
exit /b 0
In this case you running the "cmd" just 1 additional time.
The drawback is, that all variables get lost after performing exit to get back into the starting process.
But i think that is not a problem for you as you seem to do an endless loop:

Code: Select all

:loop
call :RenderScene
call :DrawScene
goto :loop
So you could also perform this loop in the upper construct with only some minor changes (just add another variable to define in which call body you are: set "body=RenderScene" or set "body=DrawScene" or just the first char)

penpen

Aacini
Expert
Posts: 1932
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: ways to implement while loop into FOR loop

#7 Post by Aacini » 07 Aug 2014 10:17

I understand you want to execute a nested while loop that will end when a certain condition arises. However, if you may set a maximum number of iterations for the while loop, then you may use the break loop technique that is simpler and faster. This is an example of such method that allows you to "CALCOLARE la radice quadrata dei due precedenti" :wink:

Code: Select all

@echo off
setlocal EnableDelayedExpansion

set number=%1
set /A iter1=number/2

rem The maximum number of iterations to calculate sqrt of a 32 bits integer number is 20
set "sqrt="
for /L %%i in (1,1,20) do if not defined sqrt (
   set /A "iter2=number/iter1, iter1=(iter1+iter2)/2"
   if !iter2! geq !iter1! set /A "sqrt=(iter1+iter2)/2"
)
echo Sqrt(%number%) = %sqrt%


Antonio

einstein1969
Expert
Posts: 976
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: ways to implement while loop into FOR loop

#8 Post by einstein1969 » 07 Aug 2014 15:14

@penpen

I have seen the error and correct with your information with the while loop this work.

For the second variant i need to "explorer" the algorithm that use a single loop, and think that is close to the Aacini's version of break small for loop . My mind is not flexible too much :?

Thanks for your help!

@Aacini
I think the the approch of break a short loop is very fast and the use of "defined" is another faster istruction. Thanks!

Thanks for the calculous of square root. I will studies this and compare with this for choice the faster.

example:

Sqrt(40.00) = 6.324 :

Code: Select all

set /a "x=4000, g0=2*10+1, z0=g0=(g0+x/g0)/2, z1=g0=g0*(g0*g0+3*x)/((3*g0*g0+x)), g0=g0*(g0*g0+3*x)/((3*g0*g0+x)), g1=g0*(g0*g0+3*x*1)*100/((3*g0*g0+x*1))"
6324


einstein1969

Post Reply