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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83import Control.Applicative
import Control.Monad
import Data.List
import qualified Data.Map.Strict as M
import Data.Maybe
import Debug.Trace
import Text.ParserCombinators.ReadP hiding (count)
import Utils
data Tile = Tile { tileId :: Int, grid :: [[Char]] }
instance Show Tile where
show = show . tileId
instance Eq Tile where
t1 == t2 = tileId t1 == tileId t2
parseTile = Tile
<$ string "Tile "
<*> parseInt
<* string ":\n"
<*> stringSize 10 `sepBy1` char '\n'
newtype AllTiles = All [Tile]
parseAllTiles = All <$> parseTile `endBy1` string "\n\n"
instance Read AllTiles where
readsPrec _ = readP_to_S parseAllTiles
topEdge = head . grid
rightEdge = map last . grid
bottomEdge = last . grid
leftEdge = map head . grid
tf f t = t { grid = f (grid t) }
hflip = map reverse
vflip = transpose . map reverse . transpose
rotate = map reverse . transpose
transformations = liftA2 (.) [id, hflip, vflip] [id, rotate, rotate . rotate, rotate . rotate . rotate]
matches t1 t2
| topEdge t1 == bottomEdge t2 = Just (t2, (0, -1))
| rightEdge t1 == leftEdge t2 = Just (t2, (1, 0))
| bottomEdge t1 == topEdge t2 = Just (t2, (0, 1))
| leftEdge t1 == rightEdge t2 = Just (t2, (-1, 0))
| otherwise = Nothing
nextTile [] picMap = picMap
nextTile remaining picMap =
let tfs = liftA2 tf transformations remaining
connections = M.map (\t -> mapMaybe (matches t) tfs) picMap
Just ((x, y), (tile, (dx, dy)):_) = find (not . null . snd) . M.toList $ connections
in nextTile (delete tile remaining) (M.insert (x + dx, y + dy) tile picMap)
monsters = transformations <*> pure [
" # ",
"# ## ## ###",
" # # # # # # "
]
checkImage :: [[Char]] -> [[Char]] -> Int
checkImage image monster = sum $ do
cols <- windows (length monster) image
rows <- map (\c -> windows (length c) c) cols
let pairs = zipWith zip rows monster
guard $ countPred (== ('#', '#')) (concat pairs) == 15
return 15
main :: IO ()
main = do
input <- readFile "input.txt"
let All tiles@(t:ts) = read input
dim = round . sqrt . fromIntegral . length $ tiles
picTiles = chunkList dim . M.elems $ nextTile ts (M.singleton (0, 0) t)
corners = map ($ picTiles) [head . head, last . head, head . last, last . last]
grids = map (map (map (init . tail) . init . tail . grid)) picTiles
image = concatMap (map concat . transpose) grids
totalWaves = count '#' (concat image)
Just monsterWaves = find (/= 0) . map (checkImage image) $ monsters
print . product . map tileId $ corners
putStrLn $ unlines image
print $ totalWaves - monsterWaves