I made a batch file game, and it works, but it is choppy and ugly. I already know about threading, but I don't want to implement that in my first version. I am hoping to have the optimization down before I start doing more advanced things with this game. my question is this: What optimizations can I make to this game, so that it will 1. not be choppy and 2. not be quite so annoying in the display. any ideas or comments about how to make it faster clearer or take less memory are welcome, however, please do not post answers like: "don't use batch" "rewrite it in (insert language here)" "do this part with vb-script" etc... as they are not helpful, nor do they answer the question. any and all non batch hating criticism is welcomed.
here is the code:
#setlocal enableextensions enabledelayedexpansion
#echo off
color 0a
mode con lines=35 cols=50
cls
set instructions=use a and d to move left and right, w to fire. use q to quit and p to pause.
set height=30
set length=
set screen=50
set swidth=20
set amo=8
set lives=3
set 1=0
set 2=1
set 3=2
set 4=3
set 5=4
set 6=0
set 7=1
set 8=2
set 9=3
set 10=4
echo. What quality would you like?
echo. 1. fast, but the graphics suck!
echo. 2. medium both ways.
echo. 3. slow, but the graphics are better!
choice /n /c:123
set firequal=%errorlevel%00
cls
echo %instructions%
echo.
pause
cls
::main
:controls
cls
if %height% EQU 2 goto gameover
if %lives% LSS 1 goto gameover
cls
set /a shouldbomb+=1
set /a whenbomb=shouldbomb%%15
if %whenbomb% == 9 call :bomb
if '%ret%'=='1' exit /b
set ret=
cls
set alive=
for /l %%i in (1,1,10) do if defined %%i set alive=true
if not defined alive goto win
cls
for /l %%i in (1,1,5) do (
if defined %%i (
set /p a=[_] <nul
) else (
set /p a=... <nul
)
)
echo.
for /l %%i in (6,1,10) do (
if defined %%i (
set /p a=[_] <nul
) else (
set /p a=... <nul
)
)
for /l %%a in (1,1,%height%) do echo.
echo %length%[]
echo.
for /l %%i in (1,1,%amo%) do set /p a=^|<nul
echo.
choice /c adwqp0 /n /t 1 /d 0
if %errorlevel% equ 1 goto :left
if %errorlevel% equ 2 goto :right
if %errorlevel% equ 3 goto :fire
if %errorlevel% equ 4 (cls&exit /b)
if %errorlevel% equ 5 pause&goto controls
if %errorlevel% equ 6 goto :inactive
goto controls
::move player left
:left
if '!length!' NEQ '' set length=!length:~0,-1!
goto controls
::move player right
:right
call :strlen shiplen length
if %shiplen% GTR %swidth% goto controls
set length=%length%
goto controls
::fire a shot upwards
:fire
if '!amo!' LSS '1' goto controls
cls
set /a amo-=1
for /l %%i in (%height%,-1,2) do (
cls
for /l %%i in (1,1,5) do (
if defined %%i (
set /p a=[_] <nul
) else (
set /p a=... <nul
)
)
echo.
for /l %%i in (6,1,10) do (
if defined %%i (
set /p a=[_] <nul
) else (
set /p a=... <nul
)
)
for /l %%j in (1,1,%%i) do echo.
echo %length% ^
set /a ship=height-%%i-1
for /l %%b in (1,1,!ship!) do echo.
echo %length%[]
echo.
for /l %%i in (1,1,%amo%) do set /p a=^|<nul
echo.
for /l %%a in (1,1,%firequal%) do call >nul 2>&1
)
call :checkshot
set /a shouldbomb+=1
set /a whenbomb=shouldbomb%%2
if %whenbomb% == 0 call :bomb
goto controls
:inactive
if %amo% LSS 10 set /a amo+=1
if !height! NEQ 2 set /a height-=1
call :bomb
goto controls
:bomb
:btop
set bombx=
for /l %%a in (1,1,10) do (
if defined %%a (
set /a randomnum=%random%%%5
if '!%%a!'=='%randomnum%' (
set /a "bombx=5*(!%%a!)"
)
)
)
)
if not defined bombx goto btop
cls
set bomb=
for /l %%b in (1,1,!bombx!) do (
set bomb=!bomb!
)
set /a bombh=height-1
for /l %%c in (1,1,!bombh!) do (
cls
for /l %%i in (1,1,5) do (
if defined %%i (
set /p a=[_] <nul
) else (
set /p a=... <nul
)
)
echo.
for /l %%i in (6,1,10) do (
if defined %%i (
set /p a=[_] <nul
) else (
set /p a=... <nul
)
)
for /l %%b in (1,1,%%c) do echo.
echo !bomb!x
set /a ship=height-%%c-1
for /l %%b in (1,1,!ship!) do echo.
echo %length%[]
echo.
for /l %%i in (1,1,%amo%) do set /p a=^|<nul
echo.
for /l %%a in (1,1,%firequal%) do call >nul 2>&1
)
if "%bomb%" == "%length%" call :looselife
if "%bomb% " == "%length%" call :looselife
if "%bomb%" == "%length% " call :looselife
if "%bomb% " == "%length%" call :looselife
if "%bomb%" == "%length% " call :looselife
exit /b
:strlen <resultVar> <stringVar>
(
setlocal EnableDelayedExpansion
set "s=!%~2!#"
set "len=0"
for %%P in (1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
)
(
endlocal
set "%~1=%len%"
exit /b
)
:checkshot
call :strlen slen length
for /l %%i in (0,5,20) do (
if '!slen!' == '%%i' (
set /a hit=%%i
set /a hit=hit/5+1
set /a hit2=hit+5
if not defined !hit2! set !hit!=
if defined !hit2! set !hit2!=
)
)
exit /b
:looselife
set /a lives-=1
set length=
set 1=0
set 2=1
set 3=2
set 4=3
set 5=4
if %lives% GTR 1 timeout /nobreak 1 >nul 2>&1
exit /b
:win
cls
echo YOU WIN^!^!^!^!
echo.
echo GOOD JOB^!^!^!
echo.
pause
cls
exit /b
:gameover
cls
echo YOU LOOSE.
echo.
echo PLEASE TRY AGAIN.
echo.
pause
cls
set ret=1
thank you in advance for any help.
P.S. I am writing this game to convince a friend to learn something besides html, and while batch isn't the best, he uses windows, and he will only do something simple for now. He is twelve, so I think batch is best option.
You can definitely improve things considerably. I know, because I have already produced a very smooth and playable version of SNAKE using pure Windows batch! Give it a try - I think you will be surprised and impressed with what musty old batch can do :-)
Of course the link has the code, but it also has pointers on some of the techniques I used to make the came perform so well. Read the entire first post carefully, and read the remainder of the thread for some additional important developments.
Optimization is a large topic. Rather than repeat all the information here, I will simply summarize. Follow the link for more details.
1) Minimize GOTO and CALL statements.
For major speed improvements over traditional batch function calls, we developed batch macros with arguments at DosTips. That first macro link develops a number of important concepts. However, the macro form I actually used in the game uses a more elegant solution with arguments appended.
A GOTO loop can be replaced by an infinite FOR loop that runs in a new process. You break out of the loop by EXITing the child process.
2) Greatly improve key press detection in a non-blocking way.
A major limitation of batch is the inability to easily detect a keypress without blocking progress of the game. The problem can be solved by using two processes, both running in the same console window. The controller process reads keypresses and sends them to the main game process via a text file. The controller has multiple modes of operation. The game process sends commands to the controller via another text file. This technique requires careful coordination of input and output redirection.
The CHOICE command is not available on XP. Some folks at DosTips discovered how to use XCOPY to simulate most of the features of CHOICE, and it works on all versions of Windows. Very cool!
3) Screen painting
Building the screen character by character is extremely slow. It is much faster to build the initial screen once, using an "array" of strings with fixed length. Each character within a string represents one "pixel". The position within a string represents the X coordinate, and the string row number represents the Y coordinate. Generally, only a few pixels change for any given screen refresh. Pixels can be "plotted" by using SET with simple substring operations. The entire screen can then be quickly refreshed using CLS followed by ECHO of each line in the screen array.
4) Smooth animation
The amount of work required to perform game logic and screen plotting can vary significantly depending on the current game context. But you want the animation to be smooth. Rather than have a fixed delay between each round of movement, you can instead measure the time since the screen was last updated. Only continue when a pre-determined amount of time has elapsed. As long as all game logic and plotting can occur within the delay time period, then the animation will always be smooth.
Here is pseudo code that describes the timing logic:
initialize delayTime
initialize previousTime
loop (
get currentTime
set diffTime = currentTime - previousTime
if diffTime >= delayTime (
set previousTime = currentTime
perform user input, game logic, and screen refresh
)
)
And here is actual code that computes the elapsed time since last movement. The currentTime (t2) is measured as centiseconds (1/100 second) since midnight. It is parsed and computed using FOR /F and basic math. The diffTime (tDiff) will be negative if the previousTime (t1) is before midnight and the currentTime (t2) is after midnight. If negative, then 1 day is added to diffTime to get the correct time interval.
%=== compute time since last move ===%
for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "t2=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100, tDiff=t2-t1"
if !tDiff! lss 0 set /a tDiff+=24*60*60*100
There is so much more that can be discussed. Try the SNAKE.BAT game, study the post and the code, and see where your imagination can take you.
Some optimizations
1) It's better to use good variable names.
Names like 1, 2 ... 10 are really bad, nobody knows what they are good for, even you self will not remember in a month.
And then it's also a bad idea as it can have many side effects to use variables begining with digits, in batch there are many where these will simply fail.
2) You should combine your output to complete lines before outputting it.
Then you don't need set/p only echo and it's faster.
3) calls to functions like :strlen should be avoided, calls at all are expensive and in your case it should be possible to solve the same without strlen at all.
4) The function :checkshot don't need a for loop.
I don't understand what you try to do there, but you test slen if it is a muliple of 5.
This could be solved with
set /a remainder=slen %% 5
if !remainder! EQU 0 (
...
5) Follow the tips of dbenham :-)
Related
I have a batch file which scans the computer taking various readings and storing each in to a variable. During this time I would like a little more than a simple message telling the user to 'Please wait - Gathering system information'.
I've tried the quick, easy, (and poor), way of doing this, as follows, as I want this to be a true incremental progress bar.
cls
echo.
echo.
echo Please wait - Gathering system information
echo ----------------------------------
echo Progress: ░░░░░░░░░░░░░░░░░░░░ 0%%
echo ----------------------------------
>nul timeout /t 1 &cls
cls
REM Do something here.
echo.
echo.
echo Please wait - Gathering system information...
echo ----------------------------------
echo Progress: █░░░░░░░░░░░░░░░░░░░ 5%%
echo ----------------------------------
>nul timeout /t 1 &cls
cls
REM Do something else here.
echo.
echo.
echo Please wait - Gathering system information.
echo ----------------------------------
echo Progress: ██░░░░░░░░░░░░░░░░░░ 10%%
echo ----------------------------------
>nul timeout /t 1 &cls
cls
REM And something else..
echo.
echo.
echo Please wait - Gathering system information..
echo ----------------------------------
echo Progress: ███░░░░░░░░░░░░░░░░░ 15%%
echo ----------------------------------
>nul timeout /t 1 &cls
cls
REM Etc...
However, the effect is not exactly great, as the cls command causes the command window to flicker, and there has to be a 'false' pause after each update, which slows the whole process… Also not great.
I have the following that looks great, but I have no idea how to 'increment' the bar after every section of code, as per my initial example, and having it play from 0-100% before running anything else is a bit pointless.
#echo off &cls
mode con: cols=70 lines=5 &color f0
call :setESC
chcp 65001 >nul
set progress=
set/a progressnum=0
:gettingdata
set progress=%progress%%ESC%[96m█%ESC%[30m
cls
echo.
echo. Please wait - Gathering system information...
echo. %progress% (%progressnum%/20)
ping localhost -n 2 >nul
set/a progressnum=%progressnum% +1
if %progressnum%==20 goto finished
goto gettingdata
:finished
echo.
echo. Finished
echo. %ESC%[92m████████████████████%ESC%[30m (20/20)
echo. Press any key to exit &>nul timeout /t -1 &exit /B
:setESC
REM Define escape char
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
set ESC=%%b
exit /B 0
)
So, my question is, how can I get the effect of the second script, but actually increment the bar after each section of my batch script has executed?
Ok I figured it out (with the help of this post).
Below is how I set up and used the script with only small additions to make it work with Unicode symbols.
First, set up your environment however you please. For the purpose of this post we will use the following:
#echo off &cls
mode con: cols=70 lines=4 &color f0
setlocal
Now in between each segment of your code you'll need to define the percentage of the progress bar by calling :drawProgressBar like so:
REM Your code here (segment 1)
REM - Progress Bar 0% - 33% ------------------------------------------------------
echo. Please wait - Gathering system information...
for /l %%f in (0 1 33) do (
call :drawProgressBar %%f "Code segment 1 finished"
)
REM ------------------------------------------------------------------------------
Repeat as required increasing the percentage each time like so:
REM Your code here (segment 2)
REM - Progress Bar 34% - 66% -----------------------------------------------------
for /l %%f in (34 1 66) do (
call :drawProgressBar %%f "Code segment 2 finished"
)
REM ------------------------------------------------------------------------------
REM Your code here (segment 3)
REM - Progress Bar 67% - 100% ----------------------------------------------------
for /l %%f in (67 1 100) do (
call :drawProgressBar %%f "Code segment 2 finished"
)
REM ------------------------------------------------------------------------------
REM The rest of your script goes here
endlocal
echo. &echo. All done. Press any key to Exit. &>nul timeout /t -1 &exit /B
Addendum: If you would like each segment to show 0-100% (as opposed to counting up from 0-100% overall) then you can use the following each time and simply update the description:
REM - Progress Bar 0% - 100% -----------------------------------------------------
for /l %%f in (0 1 100) do (
call :drawProgressBar %%f "Code segment finished"
)
REM ------------------------------------------------------------------------------
Ok to continue and this is personal preference here, but I then like to resize the CMD Window with another cls &mode con: cols=70 lines=60 &color f0 (or whatever size and/or colour you need) then continue to the output. As I say, this is just personal choice but I think it looks better with the progress meter in a smaller windows while the information is gathered before moving on to the 'main output' of the script.
Finally, add the following to the end of your batch file:
:drawProgressBar value [text]
if "%~1"=="" goto :eof
if not defined pb.barArea call :initProgressBar
setlocal enableextensions enabledelayedexpansion
set /a "pb.value=%~1 %% 101", "pb.filled=pb.value*pb.barArea/100", "pb.dotted=pb.barArea-pb.filled", "pb.pct=1000+pb.value"
set "pb.pct=%pb.pct:~-3%"
if "%~2"=="" ( set "pb.text=" ) else (
set "pb.text=%~2%pb.back%"
set "pb.text=!pb.text:~0,%pb.textArea%!"
)
<nul set /p "pb.prompt= !pb.fill:~0,%pb.filled%!!pb.dots:~0,%pb.dotted%! [%pb.pct%%%] %pb.text%!pb.cr!"
endlocal
goto :eof
:initProgressBar [fillChar] [dotChar]
chcp 65001 >nul
if defined pb.cr call :finalizeProgressBar
for /f %%a in ('copy "%~f0" nul /z') do set "pb.cr=%%a"
if "%~1"=="" ( set "pb.fillChar=▓" ) else ( set "pb.fillChar=%~1" )
if "%~2"=="" ( set "pb.dotChar=▒" ) else ( set "pb.dotChar=%~2" )
set "pb.console.columns="
for /f "tokens=2 skip=4" %%f in ('mode con') do if not defined pb.console.columns set "pb.console.columns=%%f"
set /a "pb.barArea=pb.console.columns/2-2", "pb.textArea=pb.barArea-9"
set "pb.fill="
setlocal enableextensions enabledelayedexpansion
for /l %%p in (1 1 %pb.barArea%) do set "pb.fill=!pb.fill!%pb.fillChar%"
set "pb.fill=!pb.fill:~0,%pb.barArea%!"
set "pb.dots=!pb.fill:%pb.fillChar%=%pb.dotChar%!"
set "pb.back=!pb.fill:~0,%pb.textArea%!
set "pb.back=!pb.back:%pb.fillChar%= !"
endlocal & set "pb.fill=%pb.fill%" & set "pb.dots=%pb.dots%" & set "pb.back=%pb.back%"
chcp 1252 >nul
goto :eof
:finalizeProgressBar [erase]
if defined pb.cr (
if not "%~1"=="" (
setlocal enabledelayedexpansion
set "pb.back="
for /l %%p in (1 1 %pb.console.columns%) do set "pb.back=!pb.back! "
<nul set /p "pb.prompt=!pb.cr!!pb.back:~1!!pb.cr!"
endlocal
)
)
for /f "tokens=1 delims==" %%v in ('set pb.') do set "%%v="
goto :eof
This is probably an easy task for the seasoned guys & gals here but I struggled with how to do this so I thought that I would share what worked for me and how I used the script.
Full credit goes to MC ND who is the original author of the script used. Please see the original post for more customisation options.
At the moment I am trying to make a fan-made Pokemon game. I'm making a man who sells poke balls. Here is the code I used to make it. But for some reason, the file fails to execute the last part. Btw I am also a noob at coding. To some extent, at least. Also, I use windows Batch.
#echo off
:setvariables
cls
set/a pokeballs=0
set/a pokemondollar=1000
set/a manprice=500
:Pokeball_Sale
cls
echo How many do you want to buy?
set/p pokeballamount=
set/a totalpokeballprice="pokeballamount * manprice"
echo The price = %totalpokeballprice%
pause
echo Do you want to buy it?
echo Press y for yes
echo Or press n for no
set/p ha=Choose
if %ha% == y goto Payment
if %ha% == n goto Upstairs_House3
:Payment
cls
if "pokemondollar%" GEQ "pokeballprice" set/a pokemondollar-=totalpokeballprice
set/a pokeballs+=pokeballamount
echo You spent %totalpokeballprice% on %pokeballamount%. You now have %pokeballs%.
Aside from the advice you've already been given in the comments, I have decided to post this example, showing a methodology which would make more sense.
#Echo Off
:SetVariables
ClS
Set /A PokeBalls=0,PokemonDollar=1000,ManPrice=500
:PokeBall_Sale
ClS
Set /A PokeBallAmount=0,MaxPokeBalls=PokemonDollar/ManPrice
Set /P "PokeBallAmount=How many Poke Balls do you want to buy [Maximum %MaxPokeBalls%]? "
If %PokeBallAmount% Equ 0 GoTo Upstairs_House3
If %PokeBallAmount% Gtr %MaxPokeBalls% (
Echo You do not have enough funds!
"%__AppDir__%timeout.exe" 3 /NoBreak>NUL
GoTo PokeBall_Sale
)
Set /A TotalPokeBallPrice=PokeBallAmount*ManPrice
"%__AppDir__%choice.exe" /M "The cost is %TotalPokeBallPrice%. Do you want to buy it"
If ErrorLevel 2 GoTo Upstairs_House3
:Payment
ClS
If %PokemonDollar% GEq %TotalPokeBallPrice% (
Echo You spent %TotalPokeBallPrice% on %PokeBallAmount% Poke Balls.
Set /A PokemonDollar-=TotalPokeBallPrice
Set /A PokeBalls+=PokeBallAmount
SetLocal EnableDelayedExpansion
Echo You now have !PokeBalls! Poke Balls and !PokemonDollar! Pokemon Dollars.
EndLocal
"%__AppDir__%timeout.exe" 5 /NoBreak>NUL
)
:Upstairs_House3
ClS
Yes, two Integer variables can indeed be used in Comparisons, so long as they are expanded. Quoting integers for the sake of comparisons is one means to safeguard against invalid variables, another is to make use of Delayed Expansion (Enabled)
There's a great explanation of integer comparison syntax issues here
The below MathCro can be used to assign and modify variables if your interested. In the event an attempt to operate on the 1st Argument with an undeclared variable is made, No change to the variable will occur.
#Echo Off
%= Establish Macros =%
setlocal DisableDelayedExpansion
(set LF=^
%= Newline =%)
Set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
(Set "Operate=Endlocal ^& Set /A ""%%G%%H=%%I"""
%= 'Tunnels' variable value, Allows Definition of Arithmetic within Macro =%)
Set #M=for /L %%n in (1 1 2) do if %%n==2 (%\n%
for /F "tokens=1,2,3 delims=, " %%G in ("!argv!") do (%\n%
%Operate%%\n%
%= Display value of variable. Optional. If not syntax is required to constrain expansion =%
If Not "!%%G!"=="" Echo(%%G: !%%G!%\n%
) %\n%
) ELSE setlocal enableDelayedExpansion ^& set argv=,
%= script main body =%
%= Facilitate modification of variables within codeblocks. =%
Setlocal EnableDelayedExpansion
REM macro can be used to define as well as modify variables
REM output of macro can be redirected to nul (hidden) like so:
REM (%#M% hp + 50)>nul
%#M% hp + 50
(%#M% heal + 40)>nul
%#M% hp - 30
%#M% hp + 25
%#M% hp * 3
%#M% hp / 2
%#M% hp + heal
%#M% heal - 10
%#M% hp + heal
%= Demonstrates use of an equation beyond the initial Operator. Spaces and parentheses in equation must be ommited =%
%= Increments variable by a random amount in the range of 10 to 20 =%
For /L %%A in (1,1,50) do (%#M% hp + !random!%%10+10)>nul
Echo(hp: %hp%
For /L %%A in (1,1,50) do IF Not !hp! LSS !heal! (%#M% hp - !random!%%15+10)
pause >nul
Exit /B
REM ************************ HIGH SCORES TABLE
**********************************************
:highscorestable
set /a count = 0
for /f "tokens=1,2,3 delims=-" %%i in (highscores.txt) do (
set hs=%%i
set hsn=%%j
set hsv=%%k
set hst=%%jscored %%iusing%%k
set hsn1=!hsn!
set hsv1=!hsv!
set hs1=!hs!
set hsn1= %hsn1%
set hsv1= %hsv1%
set hs1= %hs1%
echo %hsn1:~-15% %hsv1:~-15% %hs1:~-15%
set /a count+=1
if "!count!"=="5" goto :end
)
:end
echo.
pause
I'm pulling the first 5 lines from a text file using a For loop. My variables populate fine, however I'm struggling with the required alignment.
My ultimate end result should be:
James Commitment 300
Markos Excellence 290
Jeremy Si Party 50
What obvious thing am I missing here?
You could try this:
SetLocal EnableDelayedExpansion
REM **************************** HIGH SCORES TABLE ****************************
:highscorestable
Set "count=0"
For /F "UseBackQTokens=1-3Delims=-" %%i In ("highscores.txt") Do (
Set "hs=%%i"
Set "hsn=%%j"
Set "hsv=%%k"
Set "hst=%%jscored %%iusing%%k"
Set "hs= %%i "
Set "hsn1=%%j "
Set "hsv1=%%k "
Echo !hsn1:~,15!!hsv1:~,15!!hs:~-15!
Set/A count+=1
If "!count!"=="5" GoTo :end
)
:end
Echo(
Pause
Or without the possibly unnecessary variables:
SetLocal EnableDelayedExpansion
REM **************************** HIGH SCORES TABLE ****************************
:highscorestable
Set "count=0"
For /F "UseBackQTokens=1-3Delims=-" %%i In ("highscores.txt") Do (
Set "hs= %%i "
Set "hsn=%%j "
Set "hsv=%%k "
Set "hst=%%jscored %%iusing%%k"
Echo !hsn:~,15!!hsv:~,15!!hs:~-15!
Set/A count+=1
If "!count!"=="5" GoTo :end
)
:end
Echo(
Pause
In both cases, I've added the necessary SetLocal EnableDelayedExpansion line just in case it isn't in your script prior to your provided code.
Edit
You can also alter the code a little forego delayed expansion: (my preferred option)
REM **************************** HIGH SCORES TABLE ****************************
:highscorestable
For /F "Tokens=1-4Delims=:-" %%A In ('FindStr/N $ "highscores.txt"'
) Do If %%A LEq 5 (Set "hst=%%Cscored %%Busing%%D"
Set "hss= %%B"
Set "hsn=%%C "
Set "hsv=%%D "
Call Echo %%hsn:~,15%%%%hsv:~,15%%%%hss:~-10%%)
Echo(
Pause
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q46747991.txt"
REM ************************ HIGH SCORES TABLE
REM **********************************************
:highscorestable
set /a count = 0
SET "manyspaces= "
for /f "tokens=1,2,3 delims=-" %%i in (%filename1%) do (
set hs=%%k&CALL :align hs -8
set hsn=%%i&CALL :align hsn 15
set hsv=%%j&CALL :align hsv 10
ECHO !hsn!!hsv!!hs!
set /a count+=1
if "!count!"=="5" goto end
)
:end
echo.
GOTO :EOF
:align
IF %2 gtr 0 (
CALL SET "%1=%%%1%%%manyspaces%"
CALL SET "%1=%%%1:~0,%2%%"
) ELSE (
CALL SET "%1=%manyspaces%%%%1%%"
CALL SET "%1=%%%1:~%2%%"
)
GOTO :eof
I edited your results for a source file which I named to suit my system, hence the sequence of coulmns is different from your unpublished source. I changed the metavariable-assignment to suit.
The :align routine peels potatoes by recognising the second argument as the required column-width, positive for left-align and negative for right-align.
The variable manyspaces is set to an obvious value, of sufficient length to cope with the widest column required. Obviously, since it won't change once established, it's best set in the very beginning of the batch.
The routine uses the call set %%var%% method so that it will work regardless of whether delayedexpansion is invoked or not.
The mechanics are, for instance
CALL SET "%1=%%%1%%%manyspaces%"
with %1=fred
First, parse the command. %1 is replaced by fred and %% by %, yielding
set
"fred=
%fred%[spaces]"
So appends the space-string to the current value of the environment variable specified as %1
The second set - analyse similarly; result is assigned to the environment variable specified as %1
So the routine can be used to generate a fixed-width string, appropriately aligned using any ordinary variable, even if the variable has a value of nothing (ie. is undefined)
I'm a little confused on why in this code, %rem% always comes back as 0 (even when tested with prime numbers). Can someone please help me? Thanks :D
:PRIME
cls
echo What number would you like to check?
set /p num=
set num2=%num%-1
for /l %%i in (2 1 %num2%) do (
set /a rem=%num% %% %%i
)
if %rem% equ 0 goto NOT_PRIME
goto YES_PRIME
:YES_PRIME
echo %num% is a prime number.
goto AGAIN_PRIME
:NOT_PRIME
echo %num% is not a prime number.
goto AGAIN_PRIME
:AGAIN_PRIME
echo Would you like to check another number? (y/n)
set /p ans=
if '%ans%'=='y' goto PRIME
if '%ans%'=='n' goto START
This is only a portion of the code. The problem is that every number that I test, I get "%num% is not a prime number."
There are two problems in this section:
set num2=%num%-1
for /l %%i in (2 1 %num2%) do (
set /a rem=%num% %% %%i
)
if %rem% equ 0 goto NOT_PRIME
First, you need to use set /a to do calculations on a variable, so it should be:
set /a num2=%num%-1
Second, your for loop runs through all your calculations correctly, but your if line ends up checking only the results of the very last calculation. You need to enable delayed expansion and then include the if statement inside the for loop, like this:
for /l %%i in (2 1 %num2%) do (
set /a rem=%num% %% %%i
if !rem! equ 0 goto NOT_PRIME
)
The multithreading part was already replied here and works great (thanks a lot to Magoo)
Main code
SET /a instances=%NUMBER_OF_PROCESSORS%
:: make a tempfile
:maketemp
SET "tempfile=%temp%\%random%"
IF EXIST "%tempfile%?" (GOTO maketemp) ELSE (ECHO.>"%tempfile%a")
::
:loop
SET "nextfile=%~1"
IF NOT DEFINED nextfile (
DEL "%tempfile%a*" >NUL 2>NUL
::ECHO all done
exit
)
FOR /L %%a IN (1,1,%instances%) DO (
IF NOT EXIST "%tempfile%a%%a" (
>"%tempfile%a%%a" ECHO.
START /B "Instance %%a" oneconversion.bat "%~1" "%tempfile%a%%a" %%a
SHIFT
GOTO loop
)
)
timeout /t 1 >NUL
GOTO loop
Code example of oneconversion.bat
#ECHO OFF
SETLOCAL
CALL truepng.exe "%1"
CALL pngwolf.exe "%1"
DEL "%~2" >NUL 2>NUL
cls
exit
This works up to now.
But when I reserve first 10 parameters in use of commands.How I reserve commands
FOR /f "TOKENS=1-11*" %%a in ("%*") DO (
SET filelist=%%l
)
SET varresize=%1
SHIFT
SET varincsmall=%1
SET varwidth=%2
SET varheight=%3
SET varjpegqa=%4
SET varjpegpr=%5
SET varjpegex=%6
SET varpngqa=%7
SET varpngcl=%8
SET varpngqt=%9
I don't know how can I use %filelist% inside main code. And sure replacing %~1 with %filelist% doesn't work. Looks like I missed a point and couldn't find a way out.
Thanks for everyone will help or at least try to.
Add your variable initialization code at the start of the main script and shift the command line parameters additionally 9 times:
SET varresize=%1
SHIFT
SET varincsmall=%1
SET varwidth=%2
SET varheight=%3
SET varjpegqa=%4
SET varjpegpr=%5
SET varjpegex=%6
SET varpngqa=%7
SET varpngcl=%8
SET varpngqt=%9
for /L %%a in (1,1,9) do shift
Thus you won't need filelist, I think.