content: import Data.Char (isDigit)
  import Data.List (mapAccumL)
  import qualified Data.Map.Strict as M
  import Data.Maybe (fromMaybe)
  
  ---------------- INTERSECTING NUMBER WHEELS --------------
  
  clockWorkTick ::
    M.Map Char String ->
    (M.Map Char String, Char)
  clockWorkTick = flip click 'A'
    where
      click wheels name
        | isDigit name = (wheels, name)
        | otherwise =
          ( click
              . flip
                (M.insert name . leftRotate)
                wheels
              <*> head
          )
            $ fromMaybe ['?'] $ M.lookup name wheels
  
  leftRotate :: [a] -> [a]
  leftRotate = take . length <*> (tail . cycle)
  
  --------------------------- TEST -------------------------
  main :: IO ()
  main = do
    let wheelSets =
          [ [('A', "123")],
            [('A', "1B2"), ('B', "34")],
            [('A', "1DD"), ('D', "678")],
            [('A', "1BC"), ('B', "34"), ('C', "5B")]
          ]
    putStrLn "State of each wheel-set after 20 clicks:\n"
    mapM_ print $
      fmap
        ( flip
            (mapAccumL (const . clockWorkTick))
            (replicate 20 undefined)
            . M.fromList
        )
        wheelSets
    putStrLn "\nInitial state of the wheel-sets:\n"
    mapM_ print wheelSets
  