Haskell
It took me an embarrassingly long time to figure out what was going on with this one.
You could go a bit faster by splitting the list into beginning/middle/end parts, but I like the simplicity of this approach.
import Control.Monad (forM_)
import Data.Char (toUpper)
import Data.IntMap.Strict qualified as IntMap
import Data.List (elemIndices)
import Data.Map qualified as Map
{-
f is a function which, given a lookup function and an index
returns the number of mentors for the novice at that position.
The lookup function returns the number of knights up to but
not including a specified position.
-}
countMentorsWith f input = Map.fromList [(c, go c) | c <- "abc"]
where
go c =
let knights = elemIndices (toUpper c) input
counts = IntMap.fromDistinctAscList $ zip knights [1 ..]
preceding = maybe 0 snd . (`IntMap.lookupLT` counts)
in sum $ map (f preceding) $ elemIndices c input
part1 = (Map.! 'a') . countMentorsWith id
part2 = sum . countMentorsWith id
part3 d r = sum . countMentorsWith nearby . concat . replicate r
where
nearby lookup i = lookup (i + d + 1) - lookup (i - d)
main =
forM_
[ ("everybody_codes_e2025_q06_p1.txt", part1),
("everybody_codes_e2025_q06_p2.txt", part2),
("everybody_codes_e2025_q06_p3.txt", part3 1000 1000)
]
$ \(input, solve) -> readFile input >>= print . solve