Task

Implement a 2D sliding block puzzle game where blocks with numbers are combined to add their values.



Rules of the game

   The rules are that on each turn the player must choose a direction   (up, down, left or right).
   All tiles move as far as possible in that direction, some move more than others. 
   Two adjacent tiles (in that direction only) with matching numbers combine into one bearing the sum of those numbers. 
   A move is valid when at least one tile can be moved,   if only by combination. 
   A new tile with the value of   2   is spawned at the end of each turn at a randomly chosen empty square   (if there is one). 
   Adding a new tile on a blank space.   Most of the time,   a new   2   is to be added,   and occasionally   (10% of the time),   a   4.
   To win,   the player must create a tile with the number   2048. 
   The player loses if no valid moves are possible.


The name comes from the popular open-source implementation of this game mechanic, 2048.



Requirements

   "Non-greedy" movement.   
  The tiles that were created by combining other tiles should not be combined again during the same turn (move).   
  That is to say,   that moving the tile row of:
               [2][2][2][2] 

 to the right should result in: 
               ......[4][4] 

 and not:
               .........[8] 

   "Move direction priority".   
  If more than one variant of combining is possible,   move direction shall indicate which combination will take effect. 
  For example, moving the tile row of:
               ...[2][2][2] 

 to the right should result in:
               ......[2][4] 

 and not:
               ......[4][2] 



   Check for valid moves.   The player shouldn't be able to skip their turn by trying a move that doesn't change the board.
   Check for a  win condition.
   Check for a lose condition.


:: 2048 Game Task from RosettaCode
:: Batch File Implementation v2.0.1
 
@echo off
setlocal enabledelayedexpansion

rem initialization
:begin_game
set "size=4"   %== board size ==%
set "score=0"   %== current score ==%
set "won=0"   %== boolean for winning ==%
set "target=2048"   %== as the game title says ==%
for /l %%R in (1,1,%size%) do for /l %%C in (1,1,%size%) do set "X_%%R_%%C=0"

rem add two numbers in the board
call :addtile
call :addtile

rem main game loop
:main_loop
call :display
echo(
echo(Keys: WASD (Slide Movement), N (New game), P (Exit)

rem get keypress trick
set "key="
for /f "delims=" %%? in ('xcopy /w "%~f0" "%~f0" 2^>nul') do if not defined key set "key=%%?"
set "key=%key:~-1%"
 
set "changed=0"   %== boolean for changed board ==%
set "valid_key=0"   %== boolean for pressing WASD ==%
rem process keypress
if /i "!key!" equ "W" (set "valid_key=1" & call :slide "C" "1,1,%size%" "X")
if /i "!key!" equ "A" (set "valid_key=1" & call :slide "R" "1,1,%size%" "X")
if /i "!key!" equ "S" (set "valid_key=1" & call :slide "C" "%size%,-1,1" "X")
if /i "!key!" equ "D" (set "valid_key=1" & call :slide "R" "%size%,-1,1" "X")
if /i "!key!" equ "N" goto begin_game
if /i "!key!" equ "P" exit /b 0
if "%valid_key%" equ "0" goto main_loop

rem check if the board changed
if %changed% neq 0 call :addtile

rem check for win condition
if %won% equ 1 (
    set "msg=Nice one... You WON^!^!"
    goto gameover
)

rem check for lose condition
if %blank_count% equ 0 (
    for /l %%R in (1,1,%size%) do for /l %%C in (1,1,%size%) do set "LX_%%R_%%C=!X_%%R_%%C!"
    set "save_changed=%changed%" & set "changed=0"   %== save actual changed for test ==%
    call :slide "C" "1,1,%size%" "LX"
    call :slide "R" "1,1,%size%" "LX"
    if !changed! equ 0 (
        set "msg=No moves are possible... Game Over :("
        goto gameover
    ) else set "changed=!save_changed!"
)
goto main_loop

rem add number to a random blank tile
:addtile
set "blank_count=0"   %== blank tile counter ==%
set "new_tile="   %== clearing ==%
rem create pseudo-array blank_tiles
for /l %%R in (1,1,%size%) do (
    for /l %%C in (1,1,%size%) do (
        if !X_%%R_%%C! equ 0 (
            set "blank_tiles[!blank_count!]=X_%%R_%%C"
            set /a "blank_count+=1"
        )
    )
)
if %blank_count% equ 0 goto :EOF
set /a "pick_tile=%random%%%%blank_count%"
set "new_tile=!blank_tiles[%pick_tile%]!"
set /a "rnd_newnum=%random%%%10"
rem 10% chance new number is 4, 90% chance it's 2
if %rnd_newnum% equ 5 (set "%new_tile%=4") else (set "%new_tile%=2")
set /a "blank_count-=1"   %== to be used for checking lose condition ==%
goto :EOF

rem display the board
:display
cls
echo(2048 Game in Batch
echo(
set "wall=+"
for /l %%C in (1,1,%size%) do set "wall=!wall!----+"
for /l %%R in (1,1,%size%) do (
    set "disp_row=|"
    for /l %%C in (1,1,%size%) do (
        if "!new_tile!" equ "X_%%R_%%C" (set "DX_%%R_%%C=  +!X_%%R_%%C!") else (
            set "DX_%%R_%%C=!X_%%R_%%C!" 
            if !X_%%R_%%C! lss 1000 set "DX_%%R_%%C= !DX_%%R_%%C!"
            if !X_%%R_%%C! lss 100 set "DX_%%R_%%C= !DX_%%R_%%C!"
            if !X_%%R_%%C! lss 10 set "DX_%%R_%%C= !DX_%%R_%%C!"
            if !X_%%R_%%C! equ 0 set "DX_%%R_%%C=    "
        )
        set "disp_row=!disp_row!!DX_%%R_%%C!|"
    )
    echo(%wall%
    echo(!disp_row!
)
echo(%wall%
echo(
echo(Score: %score%
goto :EOF

rem the main slider of numbers in tiles
:slide
rem %%A and %%B are used here because sliding direction is variable
for /l %%A in (1,1,%size%) do (
    rem first slide: removing blank tiles in the middle
    set "slide_1="
    set "last_blank=0"   %== boolean if last tile is blank ==%
    for /l %%B in (%~2) do (
        if "%~1" equ "R" (set "curr_tilenum=!%~3_%%A_%%B!"
        ) else if "%~1" equ "C" (set "curr_tilenum=!%~3_%%B_%%A!")
        if !curr_tilenum! equ 0 (set "last_blank=1") else (
            set "slide_1=!slide_1! !curr_tilenum!"
            if !last_blank! equ 1 set "changed=1"
            set "last_blank=0"
        )
    )
    rem second slide: addition of numbered tiles
    rem slide_2 would be pseudo-array
    set "slide_2_count=0"
    set "skip=1"   %== boolean for skipping after previous summing ==%
    if "!slide_1!" neq "" for %%S in (!slide_1! 0) do (
        if !skip! equ 1 (
            set "prev_tilenum=%%S" & set "skip=0"
        ) else if !skip! equ 0 (
            if %%S equ !prev_tilenum! (
                set /a "sum=%%S+!prev_tilenum!"
                if "%~3" equ "X" set /a "score+=sum"
                set "changed=1" & set "skip=1"
                rem check for winning condition!
                if !sum! equ !target! set "won=1"
            ) else (
                set "sum=!prev_tilenum!"
                set "prev_tilenum=%%S"
            )
            set "slide_2[!slide_2_count!]=!sum!"
            set /a "slide_2_count+=1"
        )
    )
    rem new values of tiles
    set "slide_2_run=0"   %== running counter for slide_2 ==%
    for /l %%B in (%~2) do (
        if "%~1" equ "R" (set "curr_tile=%~3_%%A_%%B"
        ) else if "%~1" equ "C" (set "curr_tile=%~3_%%B_%%A")
        for %%? in ("!slide_2_run!") do (
            if %%~? lss !slide_2_count! (set "!curr_tile!=!slide_2[%%~?]!"
            ) else (set "!curr_tile!=0")
        )
        set /a "slide_2_run+=1"
    )
)
goto :EOF

rem game over xD
:gameover
call :display
echo(
echo(!msg!
echo(
echo(Press any key to exit . . .
pause>nul
exit /b 0