Stacker, A batch interpretation of the game.
Posted: 20 Apr 2023 21:34
Yet another batch game, this time it's the classic Stacker game
Spacebar to drop the current block onto the stack, customization menu at game start for colors, characters and difficulty.
Spacebar to drop the current block onto the stack, customization menu at game start for colors, characters and difficulty.
Code: Select all
===================================
@REM Stacker by T3RRY. Save with utf-8 encoding.
@REM Requires windows 10 v10589 or newer. Some features will not work as intended in Windows Terminal.
===================================
%= Do not modify Thread Launcher =%
@Echo off
(Title )
REM execute target threads
If not "%~1"=="" Goto:%1
Set "ForceQuit="
For /F "Delims=" %%G in ('Where Powershell.exe')Do If not Defined ForceQuit Set "ForceQuit=%%G -c "$wshell = New-Object -ComObject wscript.shell; $wshell.SendKeys('{TAB}')"
If not defined ForceQuit (
Call:Error 0x01
Exit /b
)
(1>"%~f0:Status" Echo(verify.NTFS) || (
Call:Error 0x04
Exit /b
)
for /F %%a in ('Echo(prompt $E^| cmd')Do Set \E=%%a
Set "fontsize="
CD /D "%~dp0"
:LoadDefaults
(For /F "UsebackQ delims=" %%G in ("%~n0:defaults")Do Set "%%~G") 2> nul
If not defined fontsize (
(
Echo("fontSize=16"
Echo("fontType=Lucida Console"
Echo("s[1]col=1"
Echo("FGcol=255"
Echo("BGi[1]=0"
Echo("BGi[2]=1"
Echo("SPi=2"
Echo("Difficulty=1"
) >"%~n0:defaults"
Goto:LoadDefaults
)
CHCP 65001 > nul
Set "CharSet= *✚✮-+ΦΞ❦•○Δ✸✹✼✾❀❂❈❣✜✟✠✥✪✫✰▄▬▀█░▒▓▲▼►◄┼═╬❤❥♥♦♣♠◙x"
:menu
Call:Setfont %FontSize% "%fontType%"
CLS
Setlocal EnableDelayedExpansion
Echo( [0] Start the game
Echo( [P] Block Color: %\E%[5m%\E%[38;5;%s[1]col%m%s[1]col%%\E%[0m
Echo( [WS] Block Char: "!CharSet:~%SPi%,1!"
Echo( [ED] Background Char1: "!CharSet:~%BGi[1]%,1!"
Echo( [RF] Background Char2: "!CharSet:~%BGi[2]%,1!"
Echo( [C] Background Color: %\E%[5m%\E%[38;5;%FGcol%m%\E%[?12h%FGcol%%\E%[?12l%\E%[0m
Echo( [Z] Change Fontsize: !FontSize!
Echo( [1~9] Difficulty: !Difficulty!%\E%[1E%\E%7
Endlocal
For /F "delims=" %%G in ('%SystemRoot%\System32\Choice.exe /n /C:WPCFR0ZEDS123456789')Do (
If /i "%%G" == "W" Set /A "SPi=SPi %% 48 + 1"
If /i "%%G" == "P" Call:GetInt s[1]col "Block color"
If /i "%%G" == "C" Call:GetInt FGcol "Background color"
If /i "%%G" == "F" If %BGi[2]% GTR 0 Set /A "BGi[2]-=1"
If /i "%%G" == "R" Set /A "BGi[2]=BGi[2] %% 48 + 1"
If /i "%%G" == "0" Goto:Setup
If /i "%%G" == "Z" Set /a "fontsize=fontsize %%30 + 2"
If /i "%%G" == "E" Set /A "BGi[1]=BGi[1] %% 48 + 1"
If /i "%%G" == "D" If %BGi[1]% GTR 0 Set /A "BGi[1]-=1"
If /i "%%G" == "S" If %SPi% GTR 0 Set /A "SPi-=1"
2> nul Set /A "1/%%G" && Set "Difficulty=%%G"
)
If %fontsize% LSS 10 Set "fontsize=10"
Goto:menu
===================================
EXIT & Rem this line never executed
! Error Handling !
Error:1 Powershell not found in %Path%
Error:2 Xcopy not in C:\Windows\System32
Error:3 No write permission
Error:4 Non NTFS system
===================================
:GameInit %= Define any initialization variables / output pre-loop screen =%
Setlocal EnableDelayedExpansion
REM some math assigned to key array kKEY to handle directional movement.
REM uses divide by zero error to abort movement if it would take the player OOBounds
REM uses -ve & +ve Modulo to adjust Current X/Y index within Bounds.
REM E,W ; A,D ; 4,6
Set "kD="1/(P[1]x-MaxX)","P[1]x=P[1]x %%MaxX + 1""
Set "kA="1/(P[1]x-1)","P[1]x=((P[1]x - MaxX) %%MaxX) + (MaxX-1)""
Set "k6="1/(P[1]x-MaxX)","P[1]x=P[1]x %%MaxX + 1""
Set "k4="1/(P[1]x-1)","P[1]x=((P[1]x - MaxX) %%MaxX) + (MaxX-1)""
REM Initial Delay Interval.
REM If you find this game runs too fast at Difficulty 1, increase the value of ControlSpeed.
REM If you find this game runs too slow at Difficulty 1, decrease the value of ControlSpeed.
Set "ControlSpeed=75000"
Set /A "BaseDelay=ControlSpeed-(Difficulty*(ControlSpeed/10))"
REM maximum delay interval
Set "MaxDelay=82500"
Set /A "ModuloStep=ControlSpeed / 100"
REM Set "ModuloStep=1250"
REM 'Delay Time' control key definitions.
REM -ve Modulo constrained to 0 lower limit by 1/Basedelay - Divide by zero error halts execution of line.
Set "k-="1/BaseDelay","BaseDelay=((BaseDelay - MaxDelay) %%MaxDelay) + (MaxDelay-ModuloStep)""
REM +ve Modulo constrained to 80000 [ModuloStep offset is to accomadate -ve modulo.]
Set "k+="1/(MaxDelay-ModuloStep-BaseDelay)","BaseDelay=BaseDelay %%(MaxDelay-ModuloStep) + ModuloStep""
REM Field Dims
Set /A "MaxX=30","MaxY=24","oMaxX=MaxX"
mode 31,25
REM initial starting pos
Set /A "P[1]x=MaxX/2","P[1]y=MaxY"
Set "Tail=!CharSet:~%SPi%,1!!CharSet:~%SPi%,1!!CharSet:~%SPi%,1!!CharSet:~%SPi%,1!!CharSet:~%SPi%,1!!CharSet:~%SPi%,1!!CharSet:~%SPi%,1!"
Set "p[1]Sprite=%\E%[^!P[1]y^!;^!P[1]x^!H!\E![5m!\E![48;5;!S[1]col!m!\E![38;5;1m^!Tail^!"
Set "P[1]len=7"
REM ::: Set "null=%tail:@=" &Set /A "P[1]len+=1" & Set "null=%"
Set "screen=%\E%[38;5;%FGcol%m"
Set "num=0"
for /l %%y in (1,1,%MaxY%) do (
for /l %%x in (1,1,%MaxX%) do (
set /a "num=num %% 2 + 1"
if !num! equ 2 (
Set "screen=!Screen!!CharSet:~%BGi[2]%,1!"
) Else Set "screen=!Screen!!CharSet:~%BGi[1]%,1!"
)
Set "screen=!screen!!LF!"
)
REM Supress Cursor
<nul Set /P "=%\E%[?25l"
Title Place:Space Quit:TAB
REM Gameloop
Set /A "MaxX=oMaxX-P[1]len+1"
Set "Tower="
Set "Key=!k6!"
Set "LastKey=!Key!"
2> nul 1> Con (
For /L %%. in ()Do (
%= Read last key press from input buffer without waiting. aka Non blocking input =%
If not "!Lastkey!"=="Pause" If not "!Lastkey!"=="+" If not "!Lastkey!"=="-" Set "LastKey=!Key!"
Set "NewKey="
Set /P "NewKey="
If "!NewKey!"=="4" Set "Newkey="
If "!NewKey!"=="6" Set "Newkey="
If /I "!NewKey!"=="A" Set "Newkey="
If /I "!NewKey!"=="D" Set "Newkey="
If !p[1]y! LSS 1 (
Set "NewKey=quit"
For /L %%i in (-1000000 1 1000000)Do rem game won
%ForceQuit%
Call:cleanup
EXIT
)
If not "!NewKey!"=="" (
If /I "!NewKey:~-4!" == "quit" (
Call:Cleanup
)
If not "!NewKey:{ENTER}!"=="!NewKey!" (
Set "NewKey=!NewKey:~-1!"
)Else Set "NewKey={ENTER}"
For /f "delims=" %%v in ("!NewKey!")Do (
If not "!k%%v!"=="" (
%= assign key array item to key =%
Set "key=!k%%v!"
)
)
)
%= Implement Control actions. =%
If not "!Key!"=="" (
If "!NewKey!"=="+" (
REM Set /A !Key!
Set "Key=!LastKey!"
)Else If "!NewKey!"=="-" (
REM Set /A !Key!
Set "Key=!LastKey!"
)
If "!Key!"=="Pause" (
Set "NewKey="
If not "!LastDropX!"=="" (
Set "oldsprite=%p[1]sprite:[38=[48%"
Set /A "TrimLeft=0","trimRight=0","right1=(p[1]x+p[1]len)","right2=(LastDropX+LastDropLen)"
If !LastDropX! GTR !p[1]X! (
Set /A "TrimLeft=LastDropX-p[1]x","p[1]x=LastDropX","p[1]len-=TrimLeft","MaxX=oMaxX-P[1]len+1"
)Else (
If !right1! GTR !right2! (
Set /A "TrimRight=Right1-Right2","p[1]len-=TrimRight","MaxX=oMaxX-P[1]len+1"
)
)
If !p[1]len! LSS 1 (
<nul Set /p "=!\E![1;1H!screen!!Tower!!oldsprite!%\E%[0m
Echo/|Choice /n
For /L %%i in (-20000 1 20000)Do (Call )
%ForceQuit%
Call:cleanup
EXIT
)
REM Set /A "P[1]len=P[1]len-trimLeft-trimRight"
If not "!Tail!"=="" For /F "delims=" %%i in ("!P[1]len!")Do Set "Tail=!Tail:~-%%i!"
Set "Tower=!Tower!%p[1]Sprite%"
Set /A "LastDropX=p[1]x","LastDropLen=p[1]Len"
)Else (
Set /A "LastDropX=p[1]x","LastDropLen=p[1]Len"
Set "Tower=!Tower!%p[1]Sprite%"
)
Set /A "NewDir=!Random! %% 2",!k-!
If !NewDir! EQU 0 (
Set /A "p[1]y-=1","p[1]x=1"
Set "Key=!k6!"
)Else (
Set /A "p[1]y-=1","p[1]x=MaxX"
Set "Key=!k4!"
)
)
)
%= bounce horizontally =%
If "!key!"=="!k4!" If "!P[1]x!"=="1" Set "key=!k6!"
If "!key!"=="!k6!" If "!P[1]x!"=="!maxX!" Set "key=!k4!"
If not "!key!"=="" Set /A !Key!
<nul Set /p "=!\E![1;1H!screen!!Tower!%P[1]Sprite%%\E%[0m"
Set /A "Delay=BaseDelay"
Set /A "Delay=Delay*((MaxX*1000/(MaxY*1000))"
For /L %%i in (1 1 !Delay!)Do rem we go way to fast without a delay
)
)
REM the bellow line should never execute.
EXIT
==========================================================================================================
REM no changes should be made to the below utilities
==========================================================================================================
:Setup
(
Echo("fontSize=%fontSize%"
Echo("fontType=%fontType%"
Echo("s[1]col=%s[1]col%"
Echo("FGcol=%FGcol%"
Echo("BGi[1]=%BGi[1]%"
Echo("BGi[2]=%BGi[2]%"
Echo("SPi=%Spi%"
Echo("Difficulty=%Difficulty%"
) >"%~n0:defaults"
REM Define essential vars for multithreaded controller
REM Signal file that the Controller uses to pass keypress to the game via without blocking execution.
REM - Note - This is modified later to a Lockfile in format: %TEMP%\%~n0_%PID%_Signal.cmd
Set "SignalFile=%TEMP%\%~n0__Signal.cmd"
REM Control Key Definitions
Call :createChars || (
Exit /B 1
)
REM QuitKey must be defined prior to starting Multithreaded controller. TAB is recommended
Set "QUITKEY=%TAB%"
Set "kP=Pause" & Set "Pause=1" & Set "k =Pause"
(Title %~n0)
:GetSession
REM get session ID to prevent File Read / write error if multiple instances running
For /F "Skip=2 tokens=2" %%G in ('Tasklist /fi "windowtitle eq %~n0"')Do Set "Lock=%%G"
CALL Set "SignalFile=%%SignalFile:__=_%Lock%_%%"
For /F "tokens=2 Delims=:" %%G in ('CHCP')Do >"%TEMP%\%~n0_%Lock%_restore.cmd" Echo(@CHCP %%G ^> nul
Del "%SignalFile:Signal=Abort%" 2> nul
CHCP 65001 > nul
Start /Wait /B "" "%~F0" XCOPYCONTROLLER 1>"%SignalFile%" 2> nul | "%~F0" GameInit <"%SignalFile%" 2> nul
REM game has resolved; end script.
DEL "%SignalFile%" 2> nul 1> nul
Endlocal & Goto:Eof
=========================
:createChars
for /f "Delims=" %%e in ('Echo Prompt $E^|cmd') Do Set "\E=%%e"
for /f "delims= " %%T in ('robocopy /L . . /njh /njs' )do set "TAB=%%T"
for /f %%C in ('copy /Z "%~dpf0" nul') do set "CR=%%C"
for /F "delims=#" %%B in ('"prompt #$H# &echo on &for %%b in (1) do rem"') do Set "BS=%%B"
Set "BS=%BS:~0,1%"
del sub.tmp 2> nul
copy nul sub.tmp /a > nul
If not exist sub.tmp (
Call:Error 0x03
Exit /B 1
)
for /F %%a in (sub.tmp) DO (
set "sub=%%a"
)
del sub.tmp
(Set LF=^
)%= Do Not Modify. Linefeed Variable =%
exit /b
:XCOPYCONTROLLER
If not exist C:\Windows\System32\Xcopy.exe (
Call:Error 0x02 >"%SignalFile:Signal=Abort%"
<nul Set /P "=quit"
EXIT
)
Setlocal DISABLEdelayedExpansion
REM Environment handling allows use of ! key
For /l %%C in () do (
Set "Key="
for /f "delims=" %%A in ('C:\Windows\System32\xcopy.exe /w "%~f0" "%~f0" 2^>nul') do If not Defined Key (
set "key=%%A"
Setlocal ENABLEdelayedExpansion
set key=^!KEY:~-1!
If "!key!" == "!QUITKEY!" (
>"%SignalFile:Signal=Abort%" Echo(
<nul Set /P "=quit"
EXIT
)
If not "!Key!" == "%BS%" If not "!Key!" == "!CR!" (%= Echo without Linefeed. Allows output of Key and Space =%
1> %~n0txt.tmp (echo(!key!!sub!)
copy %~n0txt.tmp /a %~n0txt2.tmp /b > nul
type %~n0txt2.tmp
del %~n0txt.tmp %~n0txt2.tmp
)Else (
If "!Key!" == "%BS%" <nul Set /p "={BACKSPACE}"
If "!Key!" == "!CR!" <nul Set /p "={ENTER}"
)
Endlocal
)
)
:Cleanup
<Nul Set /P "=%\E%[1;1H%\E%[2J%\E%[?25h%\E%[0mGame over. "
<"%SignalFile:Signal=Abort%" Set /P "XcopyError="
If Defined XcopyError Echo(!XcopyError!
CALL "%TEMP%\%~n0_%Lock%_restore.cmd" > nul
Del %TEMP%\%~n0_%lock%_*.cmd 1> nul 2> nul
(Title )
EXIT
:GetInt <VarName> <"Description Text">
Set "input="
Set /p "input=%\E%8%\E%[32m%\E%[0JEnter a value for %~2: %\E%[0m"
2>nul Set /A "input+=0","1/input" || Goto:GetInt
If %input% LEQ 0 Goto:GetInt
If %input% GTR 255 Goto:GetInt
Set /A "%~1=input"
Exit /b 0
:delTemp
REM this file creates signal files to identify the session and communicate input.
REM If the game is force closed instead of Quit with TAB key
REM the files will persist. To remove them, Call this file from the command line with The argument: deltemp
Del "%TEMP%\%~n0_*_*.cmd"
Goto:Eof
:setFont <integerSize> <stringFontName>
REM from the Library created by IcarusLives
REM https://github.com/IcarusLivesHF/Windows-Batch-Library/tree/8812670566744d2ee14a9a68a06be333a27488cc
if "%~2" equ "" goto :eof
call :init_setfont
%setFont% %~1 %~2
goto :eof
:init_setfont DON'T CALL
:: - BRIEF -
:: Get or set the console font size and font name.
:: - SYNTAX -
:: %setfont% [fontSize [fontName]]
:: fontSize Size of the font. (Can be 0 to preserve the size.)
:: fontName Name of the font. (Can be omitted to preserve the name.)
:: - EXAMPLES -
:: Output the current console font size and font name:
:: %setfont%
:: Set the console font size to 14 and the font name to Lucida Console:
:: %setfont% 14 Lucida Console
setlocal DisableDelayedExpansion
set setfont=for /l %%# in (1 1 2) do if %%#==2 (^
%=% for /f "tokens=1,2*" %%- in ("? ^^!arg^^!") do endlocal^&powershell.exe -nop -ep Bypass -c ^"Add-Type '^
%===% using System;^
%===% using System.Runtime.InteropServices;^
%===% [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)] public struct FontInfo{^
%=====% public int objSize;^
%=====% public int nFont;^
%=====% public short fontSizeX;^
%=====% public short fontSizeY;^
%=====% public int fontFamily;^
%=====% public int fontWeight;^
%=====% [MarshalAs(UnmanagedType.ByValTStr,SizeConst=32)] public string faceName;}^
%===% public class WApi{^
%=====% [DllImport(\"kernel32.dll\")] public static extern IntPtr CreateFile(string name,int acc,int share,IntPtr sec,int how,int flags,IntPtr tmplt);^
%=====% [DllImport(\"kernel32.dll\")] public static extern void GetCurrentConsoleFontEx(IntPtr hOut,int maxWnd,ref FontInfo info);^
%=====% [DllImport(\"kernel32.dll\")] public static extern void SetCurrentConsoleFontEx(IntPtr hOut,int maxWnd,ref FontInfo info);^
%=====% [DllImport(\"kernel32.dll\")] public static extern void CloseHandle(IntPtr handle);}';^
%=% $hOut=[WApi]::CreateFile('CONOUT$',-1073741824,2,[IntPtr]::Zero,3,0,[IntPtr]::Zero);^
%=% $fInf=New-Object FontInfo;^
%=% $fInf.objSize=84;^
%=% [WApi]::GetCurrentConsoleFontEx($hOut,0,[ref]$fInf);^
%=% If('%%~.'){^
%===% $fInf.nFont=0; $fInf.fontSizeX=0; $fInf.fontFamily=0; $fInf.fontWeight=0;^
%===% If([Int16]'%%~.' -gt 0){$fInf.fontSizeY=[Int16]'%%~.'}^
%===% If('%%~/'){$fInf.faceName='%%~/'}^
%===% [WApi]::SetCurrentConsoleFontEx($hOut,0,[ref]$fInf);}^
%=% Else{(''+$fInf.fontSizeY+' '+$fInf.faceName)}^
%=% [WApi]::CloseHandle($hOut);^") else setlocal EnableDelayedExpansion^&set arg=
endlocal &set "setfont=%setfont%"
if !!# neq # set "setfont=%setfont:^^!=!%"
exit /b
:Error
<nul Set /p "=Error:" & CMD /C Set /A %1
Exit /B