How we built it!

@echo off if "%~1" == "startGame" goto :game if "%~1" == "startController" goto :controller

::--------------------------------------------------------------------- :: setup some global variables used by both the game and the controller

setlocal disableDelayedExpansion :getSession if defined temp (set "tempFileBase=%temp%\") else if defined tmp set "tempFileBase=%tmp%\" set "tempFileBase=%tempFileBase%Snake%time::=_%" set "keyFile=%tempFileBase%_key.txt" set "cmdFile=%tempFileBase%_cmd.txt" set "gameLock=%tempFileBase%_gameLock.txt" set "gameLog=%tempFileBase%_gameLog.txt" set "signal=%tempFileBase%_signal.txt" set "saveLoc=%userprofile%\Snake" set "userPref=%saveLoc%\SnakeUserPref.txt" set "hiFile=%saveLoc%\Snake!growth!Hi" set "keyStream=9" set "cmdStream=8" set "lockStream=7"

::------------------------------------------ :: Lock this game session and launch. :: Loop back and try a new session if failure. :: Cleanup and exit when finished

call :launch %lockStream%>"%gameLock%" || goto :getSession del "%tempFileBase%*" exit /b

::------------------------------------------ :launch the game and the controller

call :fixLogs copy nul "%keyFile%" >nul copy nul "%cmdFile%" >nul start "" /b cmd /c ^""%~f0" startController %keyStream%^>^>"%keyFile%" %cmdStream%^<"%cmdFile%" 2^>nul ^>nul^" cmd /c ^""%~f0" startGame %keyStream%^<"%keyFile%" %cmdStream%^>^>"%cmdFile%" ^<nul^" echo(

::-------------------------------------------------------------- :: Upon exit, wait for the controller to close before returning

:close 2>nul (>>"%keyFile%" call )||goto :close if not exist "%~dp0CursorPos.exe" ( echo Game play can be improved by installing echo Aacini's CursorPos.exe, available at echo http://goo.gl/hr6Kkn echo( echo %cmdcmdline%|find /i "%~f0">nul&&pause ) exit /b 0

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :game title %~nx0 cls

::--------------------------------------- :: Playfield size :: max playing field: (width-2)*(height-2) <= 1365

set "width=40" max=99 set "height=25" max=99

::---------------------------- :: resize the console window

set /a cols=width+1, lines=height+10, area=(width-2)*(height-2) if %area% gtr 1365 ( echo ERROR: Playfield area too large %sendCmd% quit exit ) if %lines% lss 14 set lines=14 if %cols% lss 46 set cols=46 mode con: cols=%cols% lines=%lines%

::---------------------------- :: define variables

set "configOptions=diffCode difficulty growth moveKeys up down left right" set "configOptionCnt=9"

set "moveKeys=4"

set "up=W" set "down=S" set "left=A" set "right=D" set "pause=P"

set "space= " set "bound=#" set "food=+" set "head=@" set "body=O" set "death=X"

set "growth=1"

if exist "%userPref%" for /f "usebackq delims=" %%V in ("%userPref%") do set "%%V"

set "spinner1=-" set "spinner2=\" set "spinner3=|" set "spinner4=/" set "spinner= spinner1 spinner2 spinner3 spinner4 "

set "delay1=20" set "delay2=15" set "delay3=10" set "delay4=7" set "delay5=5" set "delay6=3"

set "desc1=Sluggard" set "desc2=Crawl" set "desc3=Slow" set "desc4=Normal" set "desc5=Fast" set "desc6=Insane"

set "spinnerDelay=3"

set /a "width-=1, height-=1"

:: define LF as a Line Feed (newline) character set ^"LF=^

^" Above empty line is required - do not remove

:: define CR as a Carriage Return character for /f %%A in ('copy /Z "%~dpf0" nul') do set "CR=%%A"

:: define BS as a BackSpace character for /f %%A in ('"prompt $H&for %%B in (1) do rem"') do set "BS=%%A"

set "upper=A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" set "invalid=*~="

::--------------------------- :: define macros

if exist "%~dp0CursorPos.exe" ( set "cls=CursorPos 0 0" set "ClearLine=echo( &CursorPos 0 -1" set "ClearPrev=CursorPos 0 -0&echo( " ) else ( set "cls=cls" set "ClearLine=" set "ClearPrev=" )

:: define a newline with line continuation set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

:: setErr ::: Sets the ERRORLEVEL to 1 set "setErr=(call)"

:: clrErr ::: Sets the ERRORLEVEL to 0 set "clrErr=(call )"

:: sendCmd command ::: sends a command to the controller set "sendCmd=>&%cmdStream% echo"

:: getKey [ValidKey] [ValidKey...] ::: Check for keypress from the controller. Only accept a ValidKey. ::: Token delimiters and poison characters must be quoted. ::: Accept any key if no ValidKey specified. ::: Return result in Key variable. Key is undefined if no valid keypress. set getKey=%\n% for %%# in (1 2) do if %%#==2 (%\n% set key=%\n% set inKey=%\n% set keyTest=%\n% ^<^&%keyStream% set /p "inKey="%\n% if defined inKey (%\n% set inKey=!inKey:~0,-1!%\n% for %%C in (!args!) do set /a keyTest=1^&if /i !inKey! equ %%~C set key=!inKey!%\n% )%\n% if not defined keyTest set key=!inKey!%\n% ) else set args=

:: draw ::: draws the board set draw=%\n% set screen=%\n% for /l %%Y in (0,1,%height%) do set screen=!screen!!line%%Y!!LF!%\n% set screen=!screen!Speed = !Difficulty! !replay!!LF!Growth Rate = !growth! HighScore = !hi!!LF!Score = !score! Time = !m!:!s!%\n% if defined replay if not defined replayFinished (%\n% set screen=!screen!!LF!!LF!Press a key to abort the replay%\n% )%\n% %cls%^&echo(!screen!

:: test X Y ValueListVar ::: tests if value at coordinates X,Y is within contents of ValueListVar set test=%\n% for %%# in (1 2) do if %%#==2 (for /f "tokens=1-3" %%1 in ("!args!") do (%\n% for %%A in ("!line%%2:~%%1,1!") do if "!%%3:%%~A=!" neq "!%%3!" %clrErr% else %setErr%%\n% )) else set args=

:: plot X Y ValueVar ::: places contents of ValueVar at coordinates X,Y set plot=%\n% for %%# in (1 2) do if %%#==2 (for /f "tokens=1-3" %%1 in ("!args!") do (%\n% set "part2=!line%%2:~%%1!"%\n% set "line%%2=!line%%2:~0,%%1!!%%3!!part2:~1!"%\n% )) else set args=

::-------------------------------------- :: start the game setlocal enableDelayedExpansion if not exist "%saveLoc%\" md "%saveLoc%" set "replay= Aborting... " set "replayAvailable=" call :loadHighScores call :mainMenu

::-------------------------------------- :: main loop (infinite loop) for /l %%. in () do (

%=== check for and process abort signal if in replay mode ===% if defined replay if exist "%signal%" ( del "%signal%" set "replayFinished=1" %draw% echo( %ClearLine% nul <&%keyStream% for %%A in (!configOptions!) do set "%%A=!%%ASave!" call :mainMenu )

%=== 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

if !tDiff! geq !delay! ( %=== delay has expired, so time for movement ===% set /a t1=t2

%=== compute game time ===%
if not defined gameStart set "gameStart=!t2!"
set /a "gameTime=(t2-gameStart)"
if !gameTime! lss 0 set /a "gameTime+=24*60*60*100"
set /a "gameTime=(gameTime-pauseTime)/100, m=gameTime/60, s=gameTime%%60"
if !m! lss 10 set "m=0!m!"
if !s! lss 10 set "s=0!s!"

%=== get keypress ===%
%getKey% !keys!
if /i !key! equ !pause! (

  %=== pause game ===%
  echo(
  call :ask "PAUSED - Press a key to continue..."
  %ClearPrev%
  %sendCmd% go
  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
  set /a pauseTime+=tDiff

) else (

  %=== establish direction ===%
  if not defined replay (echo(!key!.) >>"!gameLog!"
  for %%K in (!key!) do if !moveKeys! equ 2 (
    set /a "xDiff=xTurn%%K*!yDiff!, yDiff=yTurn%%K*!xDiff!"
  ) else if "!%%KAxis!" neq "!axis!" (
    set /a "xDiff=xDiff%%K, yDiff=yDiff%%K"
    set "axis=!%%KAxis!"
  )

  %=== erase the tail ===%
  set "TX=!snakeX:~-2!"
  set "TY=!snakeY:~-2!"
  set "snakeX=!snakeX:~0,-2!"
  set "snakeY=!snakeY:~0,-2!"
  %plot% !TX! !TY! space

  %=== compute new head location and attempt to move ===%
  set /a "X=PX+xDiff, Y=PY+yDiff"
  set "X= !X!"
  set "Y= !Y!"
  set "X=!X:~-2!"
  set "Y=!Y:~-2!"
  (%test% !X! !Y! playerSpace) && (

    %=== move successful ===%

    %=== remove the new head location from the empty list ===%
    for %%X in ("!X!") do for %%Y in ("!Y!") do set "empty=!empty:#%%~X %%~Y=!"

    %=== eat any food that may be present ===%
    (%test% !X! !Y! food) && (
      %=== initiate growth ===%
      set /a grow+=growth

      %=== locate and draw new food ===%
      if defined replay (
        <&%keyStream% set /p "F="
      ) else (
        set /a "F=(!random!%%(emptyCnt-1))*6+1"
        (echo !F!) >>"!gameLog!"
      )
      for %%F in (!F!) do (%plot% !empty:~%%F,5! food)
    )

    if !grow! gtr 0 (
      %=== restore the tail ===%
      %plot% !TX! !TY! body
      set "snakeX=!snakeX!!TX!"
      set "snakeY=!snakeY!!TY!"
      set /a emptyCnt-=1

      %=== manage score ===%
      set /a "score+=1, grow-=1"
      if not defined replay if !score! gtr !hi! set /a "hi+=1, newHi=1"

    ) else (
      %=== add the former tail position to the empty list ===%
      set "empty=!empty!#!TX! !TY!"
    )

    %=== draw the new head ===%
    if defined snakeX (%plot% !PX! !PY! body)
    %plot% !X! !Y! head

    %=== Add the new head position to the snake strings ===%
    set "snakeX=!X!!snakeX!"
    set "snakeY=!Y!!snakeY!"
    set "PX=!X!"
    set "PY=!Y!"

    %draw%

  ) || (

    %=== failed move - game over ===%
    set "replayFinished=1"
    %plot% !TX! !TY! body
    call :spinner !PX! !PY! death
    %draw%
    if defined newHi (
      echo(
      echo New High Score - Congratulations^^!
      set "hi!diffCode!=!score!"
      copy "!gameLog!" "%hiFile%!diffCode!.txt" >nul
      >>"%hiFile%!diffCode!.txt" echo ::!score!
    )
    echo(
    %ClearLine%
    call :ask "Press a key to continue..."
    for %%A in (!configOptions!) do set "%%A=!%%ASave!"
    call :mainMenu
  )
)

) )

::------------------------------------- :getString Prompt Var MaxLen :: Prompt for a string with max lengh of MaxLen. :: Valid keys are alpha-numeric, space, underscore, and dash :: String is terminated by Enter :: Backspace works to delete previous character :: Result is returned in Var set /a "maxLen=%3" set "%2=" %sendCmd% prompt <nul set /p "=%~1 " call :purge :getStringLoop (%getKey% !upper! 0 1 2 3 4 5 6 7 8 9 " " _ - {Enter} !BS!) if defined key ( if !key! equ {Enter} ( echo( exit /b ) if !key! neq !BS! if !maxLen! gtr 0 ( set /a maxLen-=1 <nul set /p "=.!BS!!key!" set "%2=!%2!!key! ) if !key! equ !BS! if defined %2 ( set /a maxLen+=1 <nul set /p "=!BS! !BS!" set "%2=!%2:~0,-1!" ) ) if defined inKey %sendCmd% one goto :getStringLoop

::------------------------------------- :ask Prompt ValidKey [Validkey]... :: Prompt for a keypress. :: Wait until a ValidKey is pressed and return result in Key variable. :: Token delimiters, ), and poison characters must be quoted. %sendCmd% prompt <nul set /p "=%~1 " (set validKeys=%*) (set validKeys=!validKeys:%1=!) call :purge :getResponse (%getKey% !validKeys!) if not defined key ( if defined inKey %sendCmd% one goto :getResponse ) exit /b

:purge set "inKey=" for /l %%N in (1 1 1000) do ( set /p "inKey=" if "!inKey!" equ "{purged}." exit /b )<&%keyStream% goto :purge

::------------------------------------- :spinner X Y ValueVar set /a d1=-1000000 for /l %%N in (1 1 5) do for %%C in (%spinner%) do ( call :spinnerDelay %plot% %1 %2 %%C %draw% ) call :spinnerDelay (%plot% %1 %2 %3) exit /b

::------------------------------------- :delay centiSeconds setlocal for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "spinnerDelay=%1, d1=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100" :: fall through to :spinnerDelay

::------------------------------------- :spinnerDelay for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "d2=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100, dDiff=d2-d1" if %dDiff% lss 0 set /a dDiff+=24*60*60*100 if %dDiff% lss %spinnerDelay% goto :spinnerDelay set /a d1=d2 exit /b

::------------------------------------- :mainMenu cls set "loadAvailable=" echo Growth rate = !growth! echo( echo Main Menu: echo( echo N - New game if defined replayAvailable echo R - Replay previous game if defined saveAvailable echo S - Save a game if exist *.snake.txt echo L - Load and watch a saved game&set "loadAvailable=L"

echo C - Control options echo G - Graphic options echo Q - Quit echo( set "hiAvailable=" for /l %%N in (1 1 6) do if defined hi%%N ( if not defined hiAvailable ( echo Replay High Score: echo( ) set "desc=!desc%%N!........" set "hiAvailable=!hiAvailable! %%N" echo %%N - !desc:~0,8! !hi%%N! ) if defined hiAvailable echo( set "keys=N C G Q !hiAvailable! !replayAvailable! !saveAvailable! !loadAvailable!" call :ask ">" !keys! if /i !key! equ Q ( %sendCmd% quit cls exit ) if /i !key! equ N ( set "replay=" set "replayAvailable=R" set "saveAvailable=S" goto :initialize ) if /i !key! equ S ( if defined replayAvailable ( call :ask "HighScore # or P for Previous:" !hiAvailable! P ) else ( call :ask "HighScore #:" !hiAvailable! ) echo !key! if /i !key! equ P (set "src=!gameLog!") else set "src=%hiFile%!key!.txt" call :getString "Save file name:" file 20 copy "!src!" "!file!.snake.txt" call :ask "Press a key to continue..." ) if /i !key! equ L ( call :getString "Load file name:" file 20 if exist "!file!.snake.txt" ( set "replay=!file!.snake.txt" goto :initialize ) echo Error: File "!file!.snake.txt" not found call :ask "Press a key to continue..." ) if /i !key! equ R ( set "replay=!gameLog!" goto :initialize ) if !key! geq 1 if !key! leq 6 ( set "replay=%hiFile%!key!.txt" goto :initialize ) if /i !key! equ C call :controlOptions if /i !key! equ G call :graphicOptions goto :mainMenu

::------------------------------------- :controlOptions cls set "keys={Enter} T L R P" if !moveKeys! equ 4 set "keys=!keys! U D" echo Control Options: echo( echo T - Type... = !moveKeys! keys echo( echo L - Left... = !left! echo R - Right.. = !right! if !moveKeys! equ 4 echo U - Up..... = !up! if !moveKeys! equ 4 echo D - Down... = !down! echo( echo P - Pause.. = !pause! echo( echo {Enter} - Return to Main Menu echo( call :ask ">" !keys! if !key! equ {Enter} goto :saveUserPrefs if /i !key! equ T ( if !moveKeys! equ 2 (set "moveKeys=4") else set "moveKeys=2" goto :controlOptions ) set "option= LLeft RRight UUp DDown PPause" for /f %%O in ("!option:* %key%=!") do ( call :ask "Press a key for %%O:" for %%K in (0 1 2) do if "!key!" equ "!invalid:~%%K,1!" goto :controlOptions for %%C in (!upper!) do set "key=!key:%%C=%%C!" set "%%O=!key!" ) goto :controlOptions

::------------------------------------- :graphicOptions cls echo Graphic Options: echo( echo B - Border...... = !bound! echo E - Empty space. = !space! echo H - snake Head.. = !head! echo S - Snake body.. = !body! echo F - Food........ = !food! echo D - Death....... = !death! echo( echo G - Growth rate. = !growth! echo( echo {Enter} - Rturn to Main Menu echo( call :ask ">" B E H S F D G {Enter} if !key! equ {Enter} goto :saveUserPrefs if /i !key! equ G ( call :ask "Press a digit for growth rate (0 = 10)" 0 1 2 3 4 5 6 7 8 9 if !key! equ 0 set "key=10" set "growth=!key!" call :loadHighScores ) else ( set "option=-BBorder:bound:-EEmpty Space:space:-HSnake Head:head:-SSnake Body:body:-FFood:food:-DDeath:death:" for /f "tokens=1,2 delims=:" %%A in ("!option:*-%key%=!") do ( call :ask "Press a key for %%A" for %%K in (0 1 2) do if "!key!" equ "!invalid:~%%K,1!" goto :graphicOptions set "%%B=!key!" ) ) goto :graphicOptions

::------------------------------------ :saveUserPrefs (for %%V in (moveKeys up down left right space bound food head body death pause growth) do echo %%V=!%%V!) >"%userPref%" exit /b

::------------------------------------- :initialize cls if defined replay ( echo Replay Speed Options: ) else ( echo Speed Options: ) echo delay echo # Description (seconds) echo --- ----------- --------- for /l %%N in (1 1 6) do ( set "delay=0!delay%%N!" set "desc=!desc%%N! " echo %%N !desc:~0,11! 0.!delay:~-2! ) echo( call :ask "Pick a speed (1-6):" 1 2 3 4 5 6 set "difficulty=!desc%key%!" set "delay=!delay%key%!" set "diffCode=%key%" echo %key% - %difficulty% echo( nul') do set "hi=%%A" (%draw%) call :delay 100 ) else ( if defined hi%diffCode% (set "hi=!hi%diffCode%!") else set "hi=0" (%draw%)

"!gameLog!" ( for %%A in (!configOptions!) do (echo %%A=!%%A!) (echo END) ) echo( if !moveKeys! equ 4 ( echo Controls: !up!=up !down!=down !left!=left !right!=right !pause!=pause ) else ( echo Controls: !left!=left !right!=right !pause!=pause ) echo Avoid running into yourself (!body!!body!!head!^) or wall (!bound!^) echo Eat food (!food!^) to grow. echo( call :ask "Press a key to start..." %sendCmd% go ) set "pauseTime=0" set "xDiff!up!=+0" set "xDiff!down!=+0" set "xDiff!left!=-1" set "xDiff!right!=+1" set "yDiff!up!=-1" set "yDiff!down!=+1" set "yDiff!left!=+0" set "yDiff!right!=+0" set "!up!Axis=Y" set "!down!Axis=Y" set "!left!Axis=X" set "!right!Axis=X" set "xTurn!left!=1" set "xTurn!right!=-1" set "yTurn!left!=-1" set "yTurn!right!=1" set "playerSpace=!space!!food!" set ^"keys="!left!" "!right!" "!pause!"^" set "newHi=" set "grow=0" if !moveKeys! equ 4 set ^"keys=!keys! "!up!" "!down!"^" if exist "%~dp0CursorPos.exe" if not defined replay ( cursorpos 0 -4 for /l %%N in (1 1 5) do (echo( ) ) exit /b

::------------------------------------- :waitForSignal if not exist "%signal%" goto :waitForSignal del "%signal%" exit /b

::------------------------------------- :loadHighScores set "saveAvailable=" for /l %%N in (1 1 6) do ( set "hi%%N=" for /f "delims=:" %%A in ('findstr "^::" "%hiFile%%%N.txt" 2^>nul') do ( set "hi%%N=%%A" set "saveAvailable=S" ) ) exit /b

::------------------------------------- :fixLogs setlocal enableDelayedExpansion for %%F in (.snake) do ( ren "%%F" "%%F.txt" call :fixLog "%%F.txt" ) pushd "%SaveLoc%" for /f "delims=" %%F in ('dir /b SnakeHi.txt 2^>nul') do ( set "file=%%~nF" set "file=Snake1Hi!file:~-1!.txt" ren "%%F" "!file!" call :fixLog "!file!" ) popd exit /b

:fixLog filePath

"%~1.new" ( <"%~1" ( for %%A in (diffCode difficulty moveKeys up down left right) do ( set /p "val=" (echo %%A=!val!) ) ) (echo growth=1) (echo END) more +7 "%~1" ) move /y "%~1.new" "%~1" >nul exit /b

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :controller :: Detects keypresses and sends the information to the game via a key file. :: The controller has various modes of input that are activated by commands sent :: from the game via a cmd file. :: :: Modes: :: :: hold - No input, wait for command :: :: go - Continuously get/send key presses :: :: prompt - Send {purged} marker to allow game to purge input buffer, then :: get/send a single key press and hold :: :: one - Get/send a single key press and hold :: :: replay - Copy a game log to the key file. The next line in cmd file :: specifies name of log file to copy. During replay, the controller :: will send an abort signal to the game if a key is pressed. :: :: quit - Immediately exit the controller process :: :: As written, this routine incorrectly reports ! as ), but that doesn't matter :: since we don't need that key. Both and Enter key are reported as {Enter}. :: An extra character is appended to the output to preserve any control chars :: when read by SET /P.

setlocal enableDelayedExpansion for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a" set "cmd=hold" set "inCmd=" set "key=" for /l %%. in () do ( if "!cmd!" neq "hold" ( for /f "delims=" %%A in ('xcopy /w "%~f0" "%~f0" 2^>nul') do ( if not defined key set "key=%%A" ) set "key=!key:~-1!" if !key! equ !CR! set "key={Enter}" ) <&%cmdStream% set /p "inCmd=" if defined inCmd ( if !inCmd! equ quit exit set "cmd=!inCmd!" if !inCmd! equ replay ( <&%cmdStream% set /p "file=" type "!file!" >&%keyStream% copy nul "%signal%" ) set "inCmd=" ) if defined key ( if "!cmd!" equ "prompt" (echo {purged}.) if "!cmd!" equ "replay" ( copy nul "%signal%" >nul set "cmd=go" ) else (echo(!key!.) if "!cmd!" neq "go" set "cmd=hold" set "key=" )>&%keyStream% )

Built With

Share this project:

Updates