1

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.

9
  • You are pushing "batch files" to the absolute limits here, so I don't believe it can be made any faster or prettier. I'd give you a point for effort, but .. as such, it's not really a good example for your friend. A 12-yr-old would be more attracted to a simple programming language that leads to a pretty game. There must be IDEs for that, certainly on Windows. Commented Nov 30, 2013 at 23:37
  • except that i offered JavaScript, and he turned me down, i offered batch and he said that no-one thinks its useful, so why should he learn it. if i can prove its useful, he might learn it. and these are not the limits. view this: dostips.com/forum/viewtopic.php?t=4741 .also, I'm only thirteen and i started with batch when i was 12, and i now love programming, so i think(hope) he could have the same result. mainly i just want to get him excited about something, since he is really depressed. he has a very amazing brain, but he doesn't see that for himself. Commented Nov 30, 2013 at 23:47
  • dostips.com/forum/viewtopic.php?f=3&t=4741 <-- show your friend this snake game. It works very well. Commented Dec 1, 2013 at 4:24
  • @foxidrive I'm sorry, but as you see in my previous comment, i already knew about that. his response was(approximate quote): "yah, but that guy abviously spent FOREVER working on it, and was a pro. i dont want to learn something that I'm not going to be able to use until I've been using it FOREVER, and have it take me FOREVER, so what is the use of learning it at all?" i am certain you see my dilema. he thinks of me as a sucky programer however, so if i can do it, then he will realize that he can too, get interested, and then go all out, like he always does when he gets interested. cont. Commented Dec 1, 2013 at 4:40
  • 2
    If your friend likes html, and you want to attract him to something written in Batch, I suggest you to show him my TextToHtml.bat conversion program. I think this program have something interesting for you both. Commented Dec 1, 2013 at 6:45

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

4 Comments

I posted this before I carefully read the comments. I now know the OP already knew about SNAKE.BAT.
that is fine, i had not understood the game as well before, and i learned more from this post than i did from reading through your code for three hours every night for a week. i had thought that the screen painting was for the snake effect, not optimization. i did wonder: is it better to set a line of enimies at the top, and execute code that changes what is displayed only if there is a hit, or is it better to re display it every time, so that the time for display always take the same amount of time. i guess you hit two birds with one stone. would it be too much to ask for you to explain cont.
how i could implement a fixed amount of time in my code? i think i can figure the rest out, but i dont quite get that.
@nephi12 - See my edited answer. If you have additional questions, then I suggest starting a new SO question for each specific programming topic. Your question here is a bit open ended for SO. Another alternative is to join DosTips and start a thread there. The format there is a bit more free form, with lots of back and forth and collaborative brain storming.
5

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 :-)

3 Comments

thank you. i like all the ideas except the echo one. i am not quite certain how that would be faster. storing it in a variable then displaying it is faster than displaying it? in any case, that you for your response. i enjoy your having an anser for any problem back on dos tips (not that i have an account, i dont ahve much to add) and i will implement the varibles names, the checkshot idea, the not calling strlen idea, and possibly the echo idea, ill have to test it.
@nephi12 - jeb is correct. Storing the characters in a string and then displaying the entire string with a single ECHO is significantly faster than using SET /P to incrementally display the line. Try it yourself.
your right! thank you both for your ideas. hopefully i get them all implemented before the ninth.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.