Created
December 14, 2022 11:35
-
-
Save GolezTrol/a4c51e76d460b687724e15527565ce88 to your computer and use it in GitHub Desktop.
Advent of Code 2022 Day 11, Monkey business
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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