Skip to content

Instantly share code, notes, and snippets.

@GolezTrol
Created December 10, 2022 20:00
Advent of Code 2022 Day 5, batch file with ASCII art animation
@echo off
setlocal EnableDelayedExpansion
REM CONFIG
REM number of stacks supported
set maxstack=20
REM line on which to draw the stacks
set baseline=23
REM Offset from the left of drawing stacks
set lpad=5
REM animation: 0=none, 1=snap, 2=fast, 3=full
set animation=3
REM #$E# makes prompt echo the escape character, which is then captured and stored in a variable.
REM The escape is used for ANSI escape sequences for color an positioning
REM ANSI escape codes cheat sheet https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
for /F "delims=#" %%a in ('prompt #$E# ^& for %%a in ^(1^) do rem') do set "esc=%%a"
REM #$H# makes prompt echo a backspace character, which is used twice in a string, to backspace
REM the . and the second space, leaving just a signle space to be echoed.
for /F "delims=#" %%a in ('prompt #$H# ^& for %%a in ^(1^) do rem') do set "space=.%%a %%a"
REM input file or defaults
set file=%1
if "%file%"=="" set file=Day5.test.txt
call :simulate 9000
set part1=%result%
echo That was the 9000 bare. Now the 9000 will simulate the 9001. Standby.
timeout /t 3
call :simulate 9001
echo.
echo Part 1: %part1%
echo Part 2: %result%
exit /b
:simulate
set result=
set CrateMoverVersion=%1
set laststack=0
for /l %%a in (0,1,%maxstack%) do (
set stack%%a=
set len%%a=0
)
REM hide caret
echo %esc%[?25l
for /f "tokens=* delims=" %%a in (%file%) do (
set line=%%a
if "!line:~0,2!"==" 1" (
call :init_stack
call :start
) else if "!line!"=="" (
REM Doesn't work? This was supposed to be the empty line before the commands
) else if "!line:~0,4!"=="move" (
call :execute "!line!"
) else (
call :stack "!line!"
)
)
for /l %%a in (1,1,%laststack%) do (
set /a len=!len%%a!-1
set stack=stack%%a
call set box=%%!stack!:~!len!%%
set result=!result!!box!
)
REM Position below animation, and restore cursor and color
echo %esc%[%baseline%H%esc%[1E%esc%[?25h%esc%[0m
exit /b
:start
REM Before the first move
cls
call :drawcrane
exit /b
:init_stack
for /l %%a in (1,1,%maxstack%) do (
if "!stack%%a!"=="" ( if !laststack!==0 set /a laststack=%%a-1
) else echo Stack %%a = !stack%%a! with a height of !len%%a!
)
echo %laststack% stacks
cls
exit /b
:stack
set line=%~1
echo !line!
for /l %%a in (1,1,%maxstack%) do (
set /a position=%%a*4-3
call set box=%%line:~!position!,1%%
if "!box!" neq " " (
call set /a len%%a+=1
call set stack%%a=!box!!stack%%a!
)
)
exit /b
:execute
for /f "tokens=2,4,6" %%b in ("%~1") do (
set /a from=%%c-1
set /a to=%%d-1
if %CrateMoverVersion%==9001 (
REM Simulating the 9001 using the 9000, by doing all the multi-moves
REM through a separate stack, reversing the sub-stacks twice
REM So this is actually slower, but since we're just simulating the result
if %%b==1 (
call :single %%c %%d
) else (
for /l %%e in (1,1,%%b) do call :single %%c 0
for /l %%e in (1,1,%%b) do call :single 0 %%d
)
) else (
for /l %%e in (1,1,%%b) do call :single %%c %%d
)
)
exit /b
:single
set /a len%1-=1
call set box=%%stack%1:~!len%1!%%
if %animation% geq 1 call :animate %1 %2 !box! !len%1! !len%2!
call set stack%1=%%stack%1:~0,!len%1!%%
call set /a len%2+=1
set stack%2=!stack%2!!box!
if %animation% geq 1 call :drawstacks
exit /b
:animate
rem from, to, box, height1 (new), height2 (old)
call :drawstacks
if %animation% lss 2 exit /b
set /a x=%1*4-3+%lpad%
set /a x2=%2*4-3+%lpad%
set step=1
if %x% geq %x2% set step=-1
set /a y=%baseline% - %4
echo %esc%[!y!;!x!f%esc%[0m[%3]
REM How high to lift (max height of stacks between, +2)
set lift=0
for /l %%a in (%1,%step%,%2) do (
if !lift! lss !len%%a! set lift=!len%%a!
)
set /a lift=%baseline%-%lift%-2
REM lower the cable
if %animation% geq 3 for /l %%a in (1,1,!y!) do (
call :waitalittle
call :drawcable !x! %%a
)
REM Lift up
for /l %%a in (%y%,-1,%lift%) do (
if %animation% geq 3 (
call :waitalittle longer
call :drawcable !x! %%a
)
set /a p=%%a+1
if %%a geq 1 echo %esc%[%%a;!x!f%esc%[0m[%3]
if !p! leq %y% echo %esc%[!p!;!x!f%esc%[30m%space%%space%%space%
)
REM Move over
for /l %%a in (%x%,%step%,%x2%) do (
if %animation% geq 3 (
call :waitalittle
call :drawcable %%a %lift%
)
if %lift% geq 1 (
set /a p=%%a-%step%
echo %esc%[%lift%;%%af%esc%[0m[%3]
echo %esc%[%lift%;!p!f%esc%[30m[ ]
echo %esc%[%lift%;%%af%esc%[0m[%3]
)
)
REM Lower on to target stack
set /a y=%baseline% - %5
for /l %%a in (%lift%,1,%y%) do (
if %animation% geq 3 (
call :waitalittle longer
call :drawcable !x2! %%a
)
set /a p=%%a-1
if %%a geq 1 echo %esc%[%%a;!x2!f%esc%[0m[%3]
if %animation% lss 3 if !p! geq %lift% if !p! geq 1 echo %esc%[!p!;!x2!f%esc%[30m[ ]
)
REM pull up the cable
if %animation% geq 3 for /l %%a in (%y%,-1,1) do (
call :waitalittle
call :drawcable !x2! %%a
if %%a lss %y% echo %esc%[%%a;!x2!f%esc%[0m%space%%space%
)
echo %esc%[24;1H%esc%[0m
exit /b
:drawcable
REM draw cable X, Length
set /a cy=%2-1
for /l %%a in (%cy%,-1,1) do (
if %%a==%cy% (
echo %esc%[%%a;%1f%esc%[0m%space%A%space%
) else (
echo %esc%[%%a;%1f%esc%[0m%space%^|%space%
)
)
exit /b
:drawcrane
set x=50
set init=%esc%[38;5;8m%esc%[H
for /l %%y in (1,4,20) do (
echo !init!%esc%[%x%C ^|/ \^|/ \^|/ \^|
echo %esc%[%x%C ^|\ /^|\ /^|\ /^|
echo %esc%[%x%C ^| \/ ^| \/ ^| \/ ^|
echo %esc%[%x%C ^| /\ ^| /\ ^| /\ ^|
set init=
)
echo %esc%[%x%C ^|/ \^|/ \^|/ \^|
echo %esc%[%x%C /^|\ /^|\ /^|\ /^|\
echo %esc%[%x%C^| ^| ^|^| ^|^| ^|^| ^|
echo %esc%[93m%esc%[14;56HCrateMover%esc%[15;59H9000
if %CrateMoverVersion%==9001 echo %esc%[93m%esc%[17;55Hrunning 9001%esc%[18;55Hcompatibility%esc%[19;59Hmode
exit /b
:drawstacks
setlocal
for /l %%s in (1,1,%laststack%) do (
set /a max=!len%%s!
set /a x=%%s*4-3+%lpad%
for /l %%b in (0,1,!max!) do (
set /a y=%baseline%-%%b
REM always end with an empty black box, to erase a previous one
if !y! geq 1 if %%b==!max! ( echo %esc%[!y!;!x!H%esc%[30m[!stack%%s:~%%b,1!]
) else echo %esc%[!y!;!x!H%esc%[0m[!stack%%s:~%%b,1!]
)
set /a y=%baseline%+2
echo %esc%[!y!;!x!H%esc%[0m!len%%s!.
)
echo %esc%[0m
exit /b
:waitalittle
set delay=5000
if "%1"=="longer" set delay=20000
for /l %%b in (0,1,%delay%) do rem de nada. #todo time a bigger loop to determine a reasonable loop number, nibbles style
exit /b
[D]
[N] [C]
[Z] [M] [P]
1 2 3
move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment