Skip to content

Instantly share code, notes, and snippets.

@GolezTrol
Created December 14, 2022 11:35
Show Gist options
  • Save GolezTrol/a4c51e76d460b687724e15527565ce88 to your computer and use it in GitHub Desktop.
Save GolezTrol/a4c51e76d460b687724e15527565ce88 to your computer and use it in GitHub Desktop.
Advent of Code 2022 Day 11, Monkey business
@echo off
setlocal EnableDelayedExpansion
set debug=rem
REM Embed by clearing this variable and copying the contents of lib.bat to the end of this file
set lib=lib.bat
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
cls
call %lib% initspecialvars
call %lib% getTimestamp start
echo %start%
REM input file or defaults
set file=%1
if "%file%"=="" set file=Day11.test.txt
call :execute part1 1
echo Part1: %part1%
call :execute part2 2
echo Part1: %part1%
echo Part2: %part2%
call lib getTimeSince result %start%
echo %result% ms have passed
exit /b
:execute
REM %1 = result var, %2 = round#
setlocal
REM Parsing monkey observations
for /f "tokens=* delims=" %%a in (%file%) do (
set line=%%a
if "!line:~0,6!"=="Monkey" (
echo ------------------------------------------------
set Monkey=!line:~7,-1!
echo Parsing monkey !monkey!
set Items!monkey!=
set Formula!monkey!=
set Divisor!monkey!=
set IfTrue!monkey!=
set IfFalse!monkey!=
set Inspections!monkey!=0
)
if "!line:~0,17!"==" Starting items:" (
REM remove spaces, and always end with a ",". This makes parsing easier later on, I hope
for %%i in (!line:~17!) do call set Items!monkey!=%%Items!monkey!%%%%i,
set Items!monkey!
)
if "!line:~0,12!"==" Operation:" set Formula!monkey!=!line:~13! & set Formula!monkey!
if "!line:~0,20!"==" Test: divisible by" set Divisor!monkey!=!line:~21! & set Divisor!monkey!
if "!line:~0,28!"==" If true: throw to monkey" set IfTrue!monkey!=!line:~29! & set IfTrue!monkey!
if "!line:~0,29!"==" If false: throw to monkey" set IfFalse!monkey!=!line:~30! & set IfFalse!monkey!
)
set lastMonkey=!monkey!
set modulo=1
for /l %%m in (0,1,%lastMonkey%) do set /a modulo=!modulo!*!Divisor%%m!
echo !modulo!
set rounds=20
if %2==2 set rounds=10000
REM Part 1: 20 rounds
for /l %%r in (1,1,%rounds%) do (
echo === ROUND %%r ===============================
call :round %2 !modulo!
)
call :getPart1 result
endlocal & set %1=%result%
exit /b
:round
rem %1 = part, %2 = modulo
set inspections=0
for /l %%m in (0,1,%lastMonkey%) do (
call :turn %%m %1 %2
)
exit /b
:turn
REM %1 is monkey#, %2 = part, %3=modulo
%debug% Monkey %1:
for %%i in (!items%1!) do (
set /a Inspections+=1
set /a Inspections%1+=1
set old=%%i
call :calc%2 new %1 %%i %3
%debug% Monkey inspects item with worry level !old!.
%debug% Old worry level !old! changes: !formula%1! = !new!
rem if %2==1 set /a new=!new!/3
rem set /a new = !new! %% %3
%debug% Monkey bored, worry level drops to !new!
set /a "remainder=!new! %% !divisor%1!"
if "!remainder!"=="0" (
%debug% Current item is divisible by !divisor%1!
call :throw !IfTrue%1! !new!
) else (
%debug% Current item is not divisible by !divisor%1!
call :throw !IfFalse%1! !new!
)
)
set items%1=
exit /b
:calc1
REM %1 result var, %2 monkey#, %3 old value %4 modulo
for /F "tokens=3,4,5" %%i in ("!Formula%2!") do (
set op1=%%i
set op2=%%k
if "!op1!"=="old" set op1=%3
if "!op2!"=="old" set op2=%3
REM Execute the formula. If a multiplication would cross the 32 bit signed limit, use the alternative bigint functions (slower)
set /a %1=!op1!%%j!op2!
set /a %1=!%1!/3
)
exit /b
:calc2
REM %1 result var, %2 monkey#, %3 old value %4 modulo
for /F "tokens=3,4,5" %%i in ("!Formula%2!") do (
set op1=%%i
set op2=%%k
if "!op1!"=="old" set op1=%3
if "!op2!"=="old" set op2=%3
REM Execute the formula. If a multiplication would cross the 32 bit signed limit, use the alternative bigint functions (slower)
set alt=no
if "%%j"=="*" (
set /a check=2100000000 / !op1!
if !check! lss !op2! (
set alt=yes
)
)
if "!alt!"=="no" (
rem echo calculating !op1! %%j !op2!
set /a %1=!op1!%%j!op2!
rem set /a new = !new! %% %4
) else (
echo alternative multiply !op1! %%j !op2!
call %lib% :multiply tmp !op1! !op2!
call %lib% :divide tmp new !tmp! %4
)
)
exit /b
:throw
REM %1=target monkey, %2 = new item
REM add item to end of list, with comma
%debug% Item with value %2 is thrown to monkey %1
set Items%1=!Items%1!%2,
exit /b
:getPart1
setlocal
rem %1 = result var
call :sortrev Inspections %lastMonkey%
echo Result = !Inspections0! * !Inspections1!
rem set /a result=!Inspections0! * !Inspections1!
call %lib% :multiply result !Inspections0! !Inspections1!
endlocal & set %1=%result%
exit /b
:sort
REM %1 array name, %2 max, %3=operator, default grt
REM assuming array vars are named ARRAYNAMEX where X is a number from 0 to max
set cmp=gtr
if "%3" neq "" set cmp=%3
for /l %%i in (%2,-1,0) do (
for /l %%j in (1,1,%%i) do (
set /a n=%%j-1
call set /a a=%%%1!n!%%+0
call set /a b=%%%1%%j%%+0
if !a! %cmp% !b! (
set %1!n!=!b!
set %1%%j=!a!
)
)
)
exit /b
:sortrev
REM %1 array name, %2 max, %3=operator, default grt
call :sort %1 %2 lss
exit /b
Monkey 0:
Starting items: 79, 98
Operation: new = old * 19
Test: divisible by 23
If true: throw to monkey 2
If false: throw to monkey 3
Monkey 1:
Starting items: 54, 65, 75, 74
Operation: new = old + 6
Test: divisible by 19
If true: throw to monkey 2
If false: throw to monkey 0
Monkey 2:
Starting items: 79, 60, 97
Operation: new = old * old
Test: divisible by 13
If true: throw to monkey 1
If false: throw to monkey 3
Monkey 3:
Starting items: 74
Operation: new = old + 3
Test: divisible by 17
If true: throw to monkey 0
If false: throw to monkey 1
rem call lib.bat function [%1 %2 %3 %4]
set lib_label=%1
shift /1
goto %lib_label%
:initspecialvars
REM No params. Sets various variables
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.
rem for /F "delims=#" %%a in ('prompt #$H# ^& for %%a in ^(1^) do rem') do set "space=.%%a %%a"
rem Or this seems to work as well :|
set "space= "
goto :eof
:toDecimal
REM Trims any leading zeroes of %2 and returns the result in %1
set /a %1=1%2-^(11%2-1%2^)/10
goto :eof
:getTimestamp
REM returns time in milliseconds since midnight
REM %1 result variable name
setlocal
for /F "tokens=1,2,3,4 delims=:., " %%a in ("%time%") do (
call :toDecimal a %%a
call :toDecimal b %%b
call :toDecimal c %%c
call :toDecimal d %%d
set /a result=!d!*10+^(!c!*1000^)+^(!b!*60000^)+^(!a!*3600000^)
)
endlocal & set %1=%result%
goto :eof
:getTimeDiff
REM returns milliseconds between %2 and %3 and returns it in %1
REM Assumes %3 should be the later timestamp. Corrects if smaller than %1 (to work around midnight).
REM Assumes difference won't be more than a day
if %3 geq %2 (
set /a %1=%3-%2
) else set /a %1=%3-%2+86400000
goto :eof
:getTimeSince
REM returns milliseconds since %2 it in %1
setlocal
call :getTimestamp now
call :getTimeDiff result %2 %now%
endlocal & set %1=%result%
goto :eof
:varlen
REM %1 result, %2 var name %3 max chars
set %1=0
for /l %%n in (0,1,%3) do if "!%2:~%%n,1!" neq "" set /a %1+=1
goto :eof
:divide
REM %1 Result
REM %2 Remainder
REM %3 Number
REM %4 Divisor
REM Divisor is expected to be less than ~200 million (1/10 of MAXINT)
setlocal EnableDelayedExpansion
set N=%3
set D=%4
set R=
set np=0
set remainder=
set result=
for /l %%n in (0,1,20) do if "!N:~%%n,1!" neq "" set /a NL=%%n+1
for /l %%n in (0,1,11) do if "!D:~%%n,1!" neq "" set /a DL=%%n+1
:divide_loop
REM take a bit of the number for building the remainder
set remainder=%remainder%!N:~%np%,1!
set /a np+=1
REM Whenever the remainder becomes bigger than the divider, divide.
REM The result becomes a digit in the result, the remainder is keps to add the next bits of the number to
if %remainder% geq %d% (
set /a bd=%remainder%/%d%
set result=!result!!bd!
set /a remainder=%remainder% %% %d%
if "!remainder!"=="0" set remainder=
) else if "!result!" neq "" set result=!result!0
Rem no more bits to take
if %np% geq %nl% goto divide_end
goto :divide_loop
:divide_end
REM echo %n%/%d% = %result%, remainder %remainder%
endlocal & set %1=%result% & set %2=%remainder%
exit /b
:add
REM %1 result, %2,%3 numbers
setlocal EnableDelayedExpansion
set A=X%2
set B=X%3
set R=0
set result=
for /l %%n in (-1,-1,-20) do (
set D=!R!
set AD=!A:~%%n,1!
set BD=!B:~%%n,1!
if "!AD!" neq "X" set /a D+=!AD!
if "!BD!" neq "X" set /a D+=!BD!
if "!R!!AD!!BD!" neq "0XX" (
set /a R=!D! / 10
set /a D=!D! %% 10
set result=!D!!result!
)
)
endlocal & set %1=%result%
exit /b
:multiply
REM %1 result, %2,%3 numbers
setlocal EnableDelayedExpansion
set A=X%2
set B=X%3
call :varlen AL A 20
call :varlen BL B 20
if !bl! gtr !al! (
set C=!B!
set B=!A!
set A=!C!
set CL=!BL!
set BL=!AL!
set AL=!CL!
)
set zeroes=
set result=0
for /l %%a in (-1,-1,-!AL!) do (
set R=0
set sub=!zeroes!
set AD=!A:~%%a,1!
if "!AD!" neq "X" (
for /l %%b in (-1,-1,-!BL!) do (
set BD=!B:~%%b,1!
if "!BD!" neq "X" (
set /a D=!R! + !AD!*!BD!
set /a R=!D! / 10
set /a D=!D! %% 10
set sub=!D!!sub!
) else if "!R!" neq "0" (
set sub=!R!!sub!
set R=0
)
)
call :add result !result! !sub!
set zeroes=!zeroes!0
)
)
endlocal & set %1=%result%
exit /b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment