Kind of messy and verbose, but good enough
import Data.List
-- Note, for the sake of definition, the center point is a 1D ring
-- Given a ring index, returns the max value in that ring
getRingMax :: Int -> Int
getRingMax = (^ 2) . (1 +) . (2 *)
-- Given a ring index, returns the min value in that ring
getRingMin :: Int -> Int
getRingMin 0 = 1
getRingMin n = 1 + (getRingMax $ n - 1)
-- Given a value, returns the distance from its ring's midpoints to the center
getNormalDistance :: Int -> Int
getNormalDistance n = dist
where mins = map getRingMin [0..]
next = findIndex (\y -> y > n) mins
dist = case next of
Just a -> a - 1 -- a represents the next rings index, and we want this rings index
Nothing -> error "Oh geeze how did you mess up this bad? This shouldn't be possible"
-- Given a value, returns the midpoints of its ring
getRingMidpoints :: Int -> [Int]
getRingMidpoints n = [bottom, left, top, right]
where dist = getNormalDistance n
maxRingVal = getRingMax dist
sideLength = 2 * dist + 1
subtrahend = sideLength - 1
bottom = (-) maxRingVal $ div subtrahend 2
left = (-) bottom subtrahend
top = (-) left subtrahend
right = (-) top subtrahend
-- Given a value, returns the distance to its ring's nearest midpoint
getTangentDistance n = minDist
where midpoints = getRingMidpoints n
distances = map (abs . (-) n) midpoints
minDist = minimum distances
-- Given a value, returns the manhattan distance to the center
getDistance :: Int -> Int
getDistance n = getTangentDistance n + getNormalDistance n
main = do
contents <- readFile "../input.txt"
let value = read contents :: Int
putStrLn $ "Solution 1:" ++ (show $ getDistance value)
putStrLn $ "Solution 2:" ++ "TODO"
No thanks I spent enough time on part 1