1 import Data.List (subsequences, (\\), sort, sortBy)
2 import Data.Ord (comparing)
4 data Item = Generator String | Microchip String deriving (Show, Eq)
6 data Building = Building Int [Floor] deriving (Show, Eq)
8 instance Ord Item where
9 compare (Generator a) (Generator b) = compare a b
10 compare (Microchip a) (Microchip b) = compare a b
11 compare (Generator _) (Microchip _) = LT
12 compare (Microchip _) (Generator _) = GT
14 instance Ord Building where
15 compare b1 b2 = comparing estimateCost b1 b2
17 building1 = Building 0 [
18 (sort [Generator "polonium", Generator "thulium",
19 Microchip "thulium", Generator "promethium", Generator "ruthenium",
20 Microchip "ruthenium", Generator "cobalt", Microchip "cobalt"]),
21 (sort [Microchip "polonium", Microchip "promethium"]),
26 building2 = Building 0 [
27 (sort [Generator "polonium", Generator "thulium",
28 Microchip "thulium", Generator "promethium", Generator "ruthenium",
29 Microchip "ruthenium", Generator "cobalt", Microchip "cobalt",
30 Generator "elerium", Microchip "elerium",
31 Generator "dilithium", Microchip "dilithium"]),
32 (sort [Microchip "polonium", Microchip "promethium"]),
38 buildingTest = Building 0 [
39 sort([Microchip "hydrogen", Microchip "lithium"]),
40 [Generator "hydrogen"],
41 [Generator "lithium"],
51 -- part1 = print $ length $ init $ extractJust $ aStar [[buildingTest]] []
52 part1 = print $ length $ init $ extractJust $ aStar [[building1]] []
55 part2 = print $ length $ init $ extractJust $ aStar [[building2]] []
58 extractJust :: Maybe [a] -> [a]
59 extractJust Nothing = []
60 extractJust (Just x) = x
62 aStar :: [[Building]] -> [Building] -> Maybe [Building]
64 aStar (currentTrail:trails) closed =
65 if isGoal (head currentTrail) then Just currentTrail
66 else aStar newAgenda ((head currentTrail): closed)
68 sortBy (\t1 t2 -> (head t1) `compare` (head t2)) $
69 trails ++ (candidates currentTrail closed)
71 candidates :: [Building] -> [Building] -> [[Building]]
72 candidates currentTrail closed = newCandidates
74 (candidate:trail) = currentTrail
75 succs = legalSuccessors $ successors candidate
76 nonloops = (succs \\ trail) \\ closed
77 newCandidates = map (\n -> n:candidate:trail) nonloops
79 isGoal :: Building -> Bool
80 isGoal (Building f floors) =
81 f+1 == height && (all (null) $ take f floors)
82 where height = length floors
84 isLegal :: Building -> Bool
85 isLegal (Building f floors) =
88 not (any (isGenerator) floor)
91 where floor = floors!!f
92 pairs = [(i, j) | i <- floor, j <- floor, isGenerator i]
93 safePair (Generator e, Microchip f) = e == f
94 safePair (Generator _, Generator _) = False
96 isGenerator :: Item -> Bool
97 isGenerator (Generator _) = True
98 isGenerator (Microchip _) = False
100 successors :: Building -> [Building]
101 successors (Building f floors) = [updateBuilding f floors nf is | nf <- nextFloors, is <- items]
104 items = filter (\is -> length is == 1 || length is == 2) $ subsequences floor
105 nextFloors = if f == 0 then [1]
106 else if f+1 == length floors then [f-1]
109 legalSuccessors :: [Building] -> [Building]
110 legalSuccessors = filter (isLegal)
112 updateBuilding :: Int -> [Floor] -> Int -> [Item] -> Building
113 updateBuilding oldF oldFloors newF items = Building newF newFloors
114 where newFloors = map (updateFloor) $ zip [0..] oldFloors
116 | f == oldF = sort $ fl \\ items
117 | f == newF = sort $ items ++ fl
120 estimateCost :: Building -> Int
121 estimateCost (Building _ floors) =
122 sum $ map (\(c, f) -> c * length f) $ zip [0..] $ reverse floors