Read arrow keys and show color text in an efficient way

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
IcarusLives
Posts: 119
Joined: 17 Jan 2016 23:55

Re: Read arrow keys and show color text in an efficient way

#16 Post by IcarusLives » 02 Feb 2017 16:55

Aacini wrote:snip


My apologies, I should have been more clear ^-^ but that does indeed answer my question. Thank you for your patience.


EDIT:

I just stumbled upon another question while studying your code :P Your tetris, for example is displaying colors SO efficiently because input AND output are Powershell. Using powershell as direct stdin and stdout instead of batch stdin and stdout? I fear I sound foolish, I just really want to understand it.

VooDooGuito
Posts: 3
Joined: 02 Jun 2020 10:57

Re: Read arrow keys and show color text in an efficient way

#17 Post by VooDooGuito » 10 Jun 2020 12:56

Hi dude, GZ for this tetris game,

here I am getting problems with this game:

I am using a windows 10 4 bits

This is the fisrt screen of game:
tetris game pause.png
tetris game pause.png (18.45 KiB) Viewed 131 times
and this is the game lunched:
tetris game.png
tetris game.png (26.7 KiB) Viewed 131 times
can you help?



Aacini wrote:
11 Apr 2016 23:10
:arrow: The second part of this application is ready :!: :D 8)

Image

2016-04-12: Code modified to fix a synchronization problem and a bug when the window/buffer sizes are changed.

Code: Select all

@echo off
setlocal EnableDelayedExpansion

if "%~1" neq "" goto %1

rem Written by Antonio Perez Ayala
rem http://www.dostips.com/forum/viewtopic.php?f=3&t=6812
rem Reference: http://colinfahey.com/tetris/tetris.html
rem 2015/11/27 - version 1.0
rem 2016/03/31 - version 2.0: Use PowerShell to read arrow keys and mouse movements
rem 2016/04/11 - version 3.0: Use PowerShell to show text in color
rem 2016/04/12 - version 3.1: Synchronization problem fixed, change window/buffer size bug fixed
rem http://www.dostips.com/forum/viewtopic.php?f=3&t=6936&p=46206#p46206

rem The best appearance is obtained with a square font, like Raster Font 8x8

color 0F
chcp 850 > NUL
cls
echo/
echo ===  Pure .BATch-file Tetris game by Aacini  ===
echo/
echo/
echo Tetris pieces are controlled with these keys:
echo/
echo                                 rot.right
echo                                     ^^
echo rot.               rot.     move    ^|     move
echo left ^<ÄÄ A S D ÄÄ^> right    left ^<ÄÄúÄÄ^> right
echo            ^|                        ^|
echo            v                        v
echo        soft drop                hard drop
echo/
echo Mouse movements emulate arrow key presses,
echo left button pause/continue the game.
echo/
echo/
echo Press P to pause/continue; press N to end game.
echo/
echo/
pause
cls

rem Field dimensions
set /A cols=10, lines=20

set /P "=Loading PS engine..." < NUL
cd . > pipeFile.txt
"%~F0" Input >> pipeFile.txt  |  "%~F0" Main < pipeFile.txt  |  "%~F0" Output
ping localhost -n 2 > NUL
del pipeFile.txt
goto :EOF



:Input
set "letter=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
for /L %%i in (0,1,25) do (
   set /A "Letter!letter:~%%i,1!=%%i+65"
)
set /A "LeftArrow=37, UpArrow=38, RightArrow=39, DownArrow=40"
PowerShell  ^
   $mouseSens = 10;  ^
   $command = @{     ^
      %LeftArrow%  = 'Dx=-1';  ^
      %RightArrow% = 'Dx=1';   ^
      %DownArrow%  = 'del=3';  ^
      %UpArrow%    = 'R=-1';   ^
      %LetterA%    = 'R=1';    ^
      %LetterD%    = 'R=-1';   ^
      %LetterS%    = 'Dy=-1';  ^
      %LetterY%    = 'Y';      ^
      %LetterN%    = 'N=1';    ^
      %LetterP%    = 'pause=1';  ^
   };  ^
   [Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') ^| Out-Null; ^
   $lX = [System.Windows.Forms.Control]::MousePosition.X; ^
   $lY = [System.Windows.Forms.Control]::MousePosition.Y; ^
   Start-Sleep -m 1500;  ^
   Write-Output 0;  ^
   while ( $key -ne %LetterN% ) {  ^
      if ( $Host.UI.RawUI.KeyAvailable ) {  ^
         $key = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyUp').VirtualKeyCode;  ^
         Write-Output $command[$key];  ^
      } else {  ^
         $mX = [System.Windows.Forms.Control]::MousePosition.X;  ^
         $mY = [System.Windows.Forms.Control]::MousePosition.Y;  ^
         $mB = [System.Windows.Forms.Control]::MouseButtons;     ^
         $dX = $mX - $lX;  $dY = $mY - $lY;  ^
         if ( [math]::abs($dX) -gt $mouseSens ) {   ^
            $if = if ($dX -lt 0) {%LeftArrow%} else {%RightArrow%};  ^
            Write-Output $command[$if];  ^
            $lX = $mX;  $lY = $mY;  ^
         } else { if ( [math]::abs($dY) -gt $mouseSens ) {  ^
            $if = if ($dY -lt 0) {%UpArrow%} else {%DownArrow%};  ^
            Write-Output $command[$if];  ^
            $lX = $mX;  $lY = $mY;  ^
         } else { if ( $mB -eq 'Left' ) {  ^
            Write-Output $command[%LetterP%];  ^
         }}}  ^
      }  ^
      Start-Sleep -m 100;  ^
   }
%End PowerShell%
echo Ending game... > CON
exit



:Output
rem Parameters received via lines read have one of these four forms:
rem - 'cls' = Clear screen and initialize the field
rem - X,Y  =  Move cursor to X,Y position
rem - X,Y,'ColorString' = Move cursor to such position and show the "Color Codes String"
rem - X,Y,Color,Wide = Move cursor to such position and show Wide chars in given Color;
rem                    if Color is '=', show an equal-sign character Wide times
rem - 'del',Filename = Delete the given file (for synchro purposes)
PowerShell  ^
   function ClearScreen () {  ^
      cls; for ( $i=0; $i -lt $lnN; ++$i ) { Write-Host; Write-Host }  ^
      Write-Host -NoNewLine (' '*2); Write-Host "Ú$frame¿";  ^
      for ( $i=1; $i -le %lines%*$lnN; ++$i ) { Write-Host -NoNewLine (' '*2); Write-Host "³$spc³" }  ^
      Write-Host -NoNewLine (' '*2); Write-Host "À$frameÙ";  ^
      Write-Host -NoNewLine (' '*2); Write-Host ' Level: 1';  ^
      Write-Host -NoNewLine (' '*3); Write-Host ' Rows: 0';  ^
      Write-Host -NoNewLine (' '*2); Write-Host ' Score: 0';  ^
   }  ^
   $console = $Host.UI.RawUI;  ^
   $console.WindowTitle = 'Tetris.BAT by Aacini';  ^
   $curSize = $console.CursorSize;  ^
   $console.CursorSize = 0;  ^
   for ( $i=1; $i -le 3; ++$i ) {  ^
      if ( $i*(%lines%+2)+6 -le $console.MaxPhysicalWindowSize.Height ) { $lnN = $i } ^
   }  ^
   $col = $lnN*%cols%+6;  $lin = $lnN*(%lines%+2)+6;  ^
   $winSize = $console.WindowSize; $bufSize = $console.BufferSize;  ^
   $winSize.Width = $col;  $bufSize.Width = $col;  ^
   if ( $col -lt $console.WindowSize.Width ) {  ^
      $console.WindowSize = $winSize; $console.BufferSize = $bufSize;  ^
   } else {  ^
      $console.BufferSize = $bufSize; $console.WindowSize = $winSize;  ^
   }  ^
   $winSize.Height = $lin; $bufSize.Height = $lin;  ^
   if ( $lin -lt $console.WindowSize.Height ) {  ^
      $console.WindowSize = $winSize; $console.BufferSize = $bufSize;  ^
   } else {  ^
      $console.BufferSize = $bufSize; $console.WindowSize = $winSize;  ^
   }  ^
   $ln = ( ('X'), ('Úª','ÀÙ'), ('ÚÄ¿','³ ³','ÀÄÙ') )[$lnN-1];  ^
   $frame = ''; $spc = ''; for ( $i=1; $i -le $col-6; ++$i ) { $frame+='Ä'; $spc+=' '; }  ^
   $coords = $console.CursorPosition;  ^
   foreach ( $line in $input ) {  ^
      $X,$Y,$Color,$Wide = $line.Split(',');  ^
      if ( $X -eq 'cls' ) {  ^
         ClearScreen  ^
      } else { if ($X -eq 'del') {  ^
         del $Y  ^
      } else {  ^
         $coords.X = 3 + ([int]$X-1) * $lnN;  ^
         $Y = [int]$Y; $coords.Y = (%lines%+3-$Y) * $lnN ;  ^
         if ( $Y -le %lines% ) { $coords.Y -= $lnN-1 }  ^
         if ( $Y -lt 0 ) { $coords.Y += $Y*($lnN-1) }  ^
         $console.CursorPosition = $coords;  ^
         if ( $Wide ) {  ^
            $Wide = [int]$Wide;  ^
            if ( $Color -ne '=' ) {  ^
               $Color = [int]('0x'+$Color);  ^
               for ( $i=0; $i -lt $lnN; ++$i ) {  ^
                  for ( $j=0; $j -lt $Wide; ++$j ) {  ^
                     Write-Host  -BackgroundColor $Color  -ForegroundColor 'Black'  -NoNewLine  $ln[$i]  ^
                  }  ^
                  ++$coords.Y; $console.CursorPosition = $coords;  ^
               }  ^
            } else {  ^
               for ( $i=0; $i -lt $lnN; ++$i ) {  ^
                  Write-Host  ('='*($Wide*$lnN));  ^
                  ++$coords.Y; $console.CursorPosition = $coords;  ^
               }  ^
            }  ^
         } else { if ( $Color ) {  ^
            for ( $i=0; $i -lt $lnN; ++$i ) {  ^
               for ( $j=0; $j -lt $Color.Length; ++$j ) {  ^
                  $colr = [int]('0x'+$Color[$j]);  ^
                  Write-Host  -BackgroundColor $colr  -ForegroundColor 'Black'  -NoNewLine  $ln[$i]  ^
               }  ^
               ++$coords.Y; $console.CursorPosition = $coords;  ^
            }  ^
         }}  ^
      }}  ^
   }  ^
   $console.CursorSize = $curSize;
%End PowerShell%
exit /B



:Main

(
   for /F "delims==" %%v in ('set') do set "%%v="
   set /A cols=%cols%, lines=%lines%
)

rem Initialize the Field
for /L %%i in (1,1,%cols%) do set "spc=!spc!0"
for /L %%i in (1,1,%lines%) do set "F%%i=³%spc%³"
set /A "Level=1, Rows=0, Score=0,  top=lines+3, delay=50"

rem The pieces are defined via 3 groups of values:  Piece : Color : Orientations
rem The ":orientations:" (piece positions) are defined via "triplets":
rem (offset Y . offset X . length X); one "triplet" for each horizontal line
rem See: http://colinfahey.com/tetris/tetris.html
set "pcs="
for %%t in ( "O:9:0.-1.2 -1.-1.2"
             "I:C:0.-2.4:1.0.1 0.0.1 -1.0.1 -2.0.1"
             "S:A:0.0.2 -1.-1.2:1.0.1 0.0.2 -1.1.1"
             "Z:B:0.-1.2 -1.0.2:1.1.1 0.0.2 -1.0.1"
             "L:D:0.-1.3 -1.-1.1:1.0.1 0.0.1 -1.0.2:1.1.1 0.-1.3:1.-1.2 0.0.1 -1.0.1"
             "J:7:0.-1.3 -1.1.1:1.0.2 0.0.1 -1.0.1:1.-1.1 0.-1.3:1.0.1 0.0.1 -1.-1.2"
             "T:E:0.-1.3 -1.0.1:1.0.1 0.0.2 -1.0.1:1.0.1 0.-1.3:1.0.1 0.-1.2 -1.0.1" ) do (
   set "pc=%%~t" & set "i=-2"
   for /F "delims=" %%p in (^"!pc::^=^
% New line %
!^") do (
      if !i! equ -2 (
         set "pc=%%p" & set "pcs=!pcs!%%p"
      ) else if !i! equ -1 (
         set "!pc!R=%%p"
      ) else (
         set "!pc!!i!=%%p"
      )
      set /A i+=1
   )
   set "!pc!N=!i!"
)

:WaitPS for PowerShell Input part start signal
   set /P "com="
if not defined com goto WaitPS
set "com="

set "init=1"
for /L %%# in () do (

   if defined init (
      setlocal EnableDelayedExpansion
      set "init="
      echo cls

      rem Create the first "previous" -hidden- piece
      for /L %%i in (0,1,!time:~-1!) do set /A p=!random!%%7
      for %%p in (!p!) do set "p2=!pcs:~%%p,1!"
      for %%p in (!p2!) do set "p3=!%%p0!" & set "p4=!%%pN!" & set "p5=!%%pR!!%%pR!!%%pR!!%%pR!"

      set "new=1"
   )

   if defined new (
      set "new="

      rem Take the "previous" piece as current one
      set "pc=!p2!" & set "p0=!p3!" & set "pN=!p4!" & set "pR=!p5!"

      rem Create a new "previous" piece
      for /L %%i in (1,1,2) do (
         set /A p=!random!*7/32768
         for %%p in (!p!) do (
            set "p=!pcs:~%%p,1!"
            if !p! neq !pc! set "p2=!p!"
         )
      )
      for %%p in (!p2!) do set "p3=!%%p0!" & set "p4=!%%pN!" & set "p5=!%%pR!!%%pR!!%%pR!!%%pR!"

      rem Show the new "previous" piece in its place, above Field
      set /A x=cols/2-1
      for %%p in (!p3!) do (
         for /F "tokens=1-3 delims=." %%i in ("%%p") do (
            set /A yp=top+%%i, xp=2+%%j, xL=xp+%%k
            for /F "tokens=1,2" %%a in ("!xp! !xL!") do (
               set "pce=0000"
               set "pce=!pce:~0,%%a!!p5:~0,%%k!!pce:~%%b!"
               echo !x!,!yp!,!pce!
            )
         )
      )
      if !p2! equ I set /A yp=top-1 & echo !x!,!yp!,0000

      rem Try to insert the new current piece in the Field...
      set /A x=cols/2+1, y=lines,   b=1
      for %%p in (!p0!) do (
         for /F "tokens=1-3 delims=." %%i in ("%%p") do (
            set /A yp=y+%%i, xp=x+%%j, xL=xp+%%k
            for /F "tokens=1-3" %%a in ("!yp! !xp! !xL!") do (
               if "!F%%a:~%%b,%%k!" neq "!spc:~0,%%k!" set     "b="
               set "F%%a=!F%%a:~0,%%b!!pR:~0,%%k!!F%%a:~%%c!"
               echo %%b,%%a,!F%%a:~%%b,1!,%%k
            )
         )
      )

      rem ... if that was not possible:
      if not defined b call :endGame & endlocal

      set "p1=!p0!"
      set /A "pI=0, del=delay, b=1!time:~-2!"

   )

   rem Control module: move the piece as requested via a key, or down one row each %del% centiseconds
   set "move="
   set /A "Dy=Dx=0"
   set /P "com="
   if defined com (
      set /A "!com!, move=1"
      set "com="
      if defined N exit
      if "!pause!" equ "1" call :Pause & set "move="
      set "b=1!time:~-2!"
   ) else (
      set /A "e=1!time:~-2!, elap=e-b, elap-=(elap>>31)*100"
      if !elap! geq !del! set /A b=e, Dy=move=-1
   )

   if defined move (

      rem Delete the piece from its current position in the field, and store current coordinates
      set i=0
      for %%p in (!p0!) do for /F "tokens=1-3 delims=." %%i in ("%%p") do (
         set /A yp=y+%%i, xp=x+%%j, xL=xp+%%k
         for /F "tokens=1-3" %%a in ("!yp! !xp! !xL!") do (
            set "F%%a=!F%%a:~0,%%b!!spc:~0,%%k!!F%%a:~%%c!"
            set /A i+=1
            set "c!i!=%%a %%b %%c %%k"
         )
      )

      rem If move is Rotate: get rotated piece
      if defined R (
         set /A "p=(pI+R+pN)%%pN"
         for /F "tokens=1,2" %%i in ("!pc! !p!") do set "p1=!%%i%%j!"
      )

      rem Test if the piece can be placed at the new position, and store new coordinates
      set j=0
      for %%p in (!p1!) do if defined move (
         for /F "tokens=1-3 delims=." %%i in ("%%p") do (
            set /A yp=y+%%i+Dy, xp=x+%%j+Dx, xL=xp+%%k
            for /F "tokens=1-3" %%a in ("!yp! !xp! !xL!") do (
               if "!F%%a:~%%b,%%k!" equ "!spc:~0,%%k!" (
                  set /A j+=1
                  set "n!j!=%%a %%b %%c %%k"
               ) else (
                  set "move="
               )
            )
         )
      )

      if defined move (

         rem Clear the piece from its current position, on the screen
         for /L %%i in (1,1,!i!) do (
            for /F "tokens=1-4" %%a in ("!c%%i!") do (
               echo %%b,%%a,!F%%a:~%%b,1!,%%d
            )
         )

         rem Place the piece at the new position, both in field and screen
         for /L %%j in (1,1,!j!) do (
            for /F "tokens=1-4" %%a in ("!n%%j!") do (
               set "F%%a=!F%%a:~0,%%b!!pR:~0,%%d!!F%%a:~%%c!"
               echo %%b,%%a,!F%%a:~%%b,1!,%%d
            )
         )

         rem Update any changes in the piece
         set /A y+=Dy, x+=Dx
         if defined R set "p0=!p1!" & set "pI=!p!" & set "R="

      ) else (   rem The piece can not be moved

         rem Recover the piece at its current position, in the field
         for /L %%i in (1,1,!i!) do (
            for /F "tokens=1-4" %%a in ("!c%%i!") do (
               set "F%%a=!F%%a:~0,%%b!!pR:~0,%%d!!F%%a:~%%c!"
            )
         )
         if defined R set "p1=!p0!" & set "R="

         if !Dy! neq 0 (   rem The piece "lands"

            rem Check completed lines
            set "j=" & set "m=0"
            for /L %%i in (1,1,!i!) do for /F %%a in ("!c%%i!") do (
               if "!F%%a:0=!" equ "!F%%a!" (
                  set "F%%a=X"
                  set "j=!j! %%a"
                  set /A m+=1
               )
            )

            if !m! gtr 0 (

               rem Blink completed lines on screen
               for %%i in (!j!) do (
                  echo 1,%%i,=,%cols%
               )

               rem Update level and scores
               rem See: N-Blox at http://www.tetrisfriends.com/help/tips_appendix.php#rankingsystem
               set /A "xp=Level*(40+((j-2>>31)+1)*60+((j-3>>31)+1)*200+((j-4>>31)+1)*900)"
               set /A "Score+=xp, Rows+=j, xL=Level, Level=(Rows-1)/10+1"
               if !Level! neq !xL! if !delay! gtr 5 set /A delay-=5
               rem BEL Ctrl-G Ascii-7:
               set /P "=" < NUL > CON

               rem Remove completed lines from field
               set "i=1"
               for /L %%i in (1,1,%lines%) do (
                  set "F!i!=!F%%i!"
                  if "!F%%i!" neq "X" set /A i+=1
               )
               for /L %%i in (!i!,1,%lines%) do set "F%%i=³%spc%³"

               rem Update scores and the whole field on screen
               echo 1,-1
               call :Delay 95
               (
               echo Level: !Level!
               echo     Rows: !Rows!
               echo    Score: !Score!
               ) > CON
               echo X > PSbusy.txt
               for /L %%i in (%lines%,-1,1) do echo 1,%%i,!F%%i:~1,-1!
               echo del,PSbusy.txt
               call :WaitPSbusy

            )

            rem Request to show a new piece
            set "new=1"

         )

      )

   )

)


:endGame
echo 1,-4
echo X > PSbusy.txt
echo del,PSbusy.txt
call :WaitPSbusy
set /P "=Play again? " < NUL > CON
:choice
   set /P "com="
if not defined com goto choice
if "%com%" equ "Y" exit /B
if "%com:~0,1%" neq "N" set "com=" & goto choice
echo N > CON
exit


:WaitPSbusy
   if exist PSbusy.txt goto WaitPSbusy
exit /B


:Pause
title PAUSED
:wait
   set /P "com="
   if not defined com goto wait
if "%com%" neq "pause=1" set "com=" & goto wait
set "com="
set "pause="
title Tetris.BAT by Aacini
exit /B


:Delay centisecs
set "b=1%time:~-2%"
:wait2
   set /A "e=1%time:~-2%, elap=e-b, elap-=(elap>>31)*100"
if %elap% lss %1 goto wait2
set "b=1%time:~-2%"
exit /B
Antonio

Post Reply