summaryrefslogtreecommitdiff
path: root/2017/3.hs
blob: a7629656db1724a1354cdf921bff75f59086378b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import Control.Monad
import Data.Function
import Data.List
import Data.Maybe

type Ring = Int
type Index = Int
type Value = Int

righttop :: Ring -> Index
righttop ring = 1 + (2 * ring - 1) * (2 * ring)

-- rt(r) = 1 + 2r(2r-1) = 4r^2 - 2r + 1 = n
-- 4r^2 - 2r + 1 - n = 0
-- r = (2 + sqrt(4 - 4*4*(1-n))) / 8
--   = (2 + sqrt(16n - 12)) / 8
--   = (2 + 2sqrt(4n - 3)) / 8
--   = (1 + sqrt(4n - 3)) / 4

-- counted from right-top numbers (1 is also a right-top number)
ringof :: Index -> Ring
ringof n = floor $ (1 + sqrt (fromIntegral (4 * n - 3))) / 4

posof :: Index -> (Int, Int)
posof n =
    let r = ringof n
        rt = righttop r
        d = 2 * r
    in case n of
        _ | n < rt + d -> (r - (n - rt), r)
          | n < rt + 2*d -> (-r, r - (n - (rt + d)))
          | n < rt + 2*d + d+1 -> (-r + n - (rt + 2*d), -r)
          | n < rt + 2*d + 2*(d+1) -> (r + 1, -r + n - (rt + 2*d + d+1))
          | otherwise -> undefined

posidx :: (Int, Int) -> Index
posidx (x, y) =
    let ring = if x > abs y then x - 1 else (max `on` abs) x y
        rt = righttop ring
        ringlen = 8 * ring + 2
        Just ringidx = findIndex (== (x, y)) (map posof [rt .. rt+ringlen-1])
    in rt + ringidx

manhattan1 :: Index -> Int
manhattan1 = uncurry ((+) `on` abs) . posof

valueof :: Index -> Value
valueof n = cache !! (n - 1)
    where cache = map valueof' [1..]

valueof' :: Index -> Value
valueof' 1 = 1
valueof' n =
    let (x, y) = posof n
        nei = [(x + dx, y + dy) | dx <- [-1..1], dy <- [-1..1], dx /= 0 || dy /= 0]
        indices = filter (< n) (map posidx nei)
    in sum (map valueof indices)

main :: IO ()
main = do
    input <- liftM read (readFile "3.in") :: IO Int
    print (manhattan1 input)
    -- putStrLn $ intercalate "\n" [intercalate " " $ map show [valueof (posidx (x,-y)) | x <- [-3..3]] | y <- [-3..3]]
    print $ fromJust $ find (> input) $ map valueof [1..]