{-# Language ImportQualifiedPost, OverloadedStrings #-}
{-|
Module      : Advent.Input
Description : Input file helpers
Copyright   : (c) Eric Mertens, 2021
License     : ISC
Maintainer  : emertens@gmail.com

This module provides input files in some of the most commonly
needed formats. It either automatically loads from an @inputs@
directory, or takes the input file as a command-line argument.

-}
module Advent.Input where

import Advent.Coord (Coord(..), coordLines)
import Data.Array.Unboxed qualified as A
import Data.Map (Map)
import Data.Map.Strict qualified as SMap
import System.Environment (getArgs)
import System.IO (hPutStrLn, stderr)
import Text.Printf (printf)

-- | Get the input for the given day.
--
-- If a filename is provided in the command line that will be used as the
-- input file.
--
-- If the filename is @-@ the stdin will be used as the input file.
--
-- Otherwise the input text file corresponding to the day number will be used.
getRawInput :: Int {- ^ year -} -> Int {- ^ day number -} -> IO String
getRawInput :: Int -> Int -> IO FilePath
getRawInput Int
y Int
d =
  do [FilePath]
args <- IO [FilePath]
getArgs
     case [FilePath]
args of
       []    -> FilePath -> IO FilePath
readFile (FilePath -> Int -> Int -> FilePath
forall r. PrintfType r => FilePath -> r
printf FilePath
"inputs/%d/%02d.txt" Int
y Int
d)
       FilePath
"-":[FilePath]
_ -> Handle -> FilePath -> IO ()
hPutStrLn Handle
stderr FilePath
"Ready!" IO () -> IO FilePath -> IO FilePath
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO FilePath
getContents
       FilePath
"+":FilePath
input:[FilePath]
_ -> FilePath -> IO FilePath
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure FilePath
input
       FilePath
fn:[FilePath]
_  -> FilePath -> IO FilePath
readFile FilePath
fn

-- | Default input filename given a day number
inputFileName :: Int {- ^ day -} -> FilePath
inputFileName :: Int -> FilePath
inputFileName = FilePath -> Int -> FilePath
forall r. PrintfType r => FilePath -> r
printf FilePath
"inputs/%02d.txt"

-- | Load input file as a list of lines.
getInputLines :: Int {- ^ year -} -> Int {- ^ day -} -> IO [String]
getInputLines :: Int -> Int -> IO [FilePath]
getInputLines Int
y Int
d = FilePath -> [FilePath]
lines (FilePath -> [FilePath]) -> IO FilePath -> IO [FilePath]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> Int -> IO FilePath
getRawInput Int
y Int
d

-- | Load input file as a rectangular array of characters.
getInputArray :: Int {- ^ year -} -> Int {- ^ day -} -> IO (A.UArray Coord Char)
getInputArray :: Int -> Int -> IO (UArray Coord Char)
getInputArray Int
y Int
d =
  do [FilePath]
xs <- Int -> Int -> IO [FilePath]
getInputLines Int
y Int
d
     UArray Coord Char -> IO (UArray Coord Char)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (UArray Coord Char -> IO (UArray Coord Char))
-> UArray Coord Char -> IO (UArray Coord Char)
forall a b. (a -> b) -> a -> b
$! (Coord, Coord) -> FilePath -> UArray Coord Char
forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
(i, i) -> [e] -> a i e
A.listArray (Int -> Int -> Coord
C Int
0 Int
0, Int -> Int -> Coord
C ([FilePath] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [FilePath]
xs Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (FilePath -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([FilePath] -> FilePath
forall a. HasCallStack => [a] -> a
head [FilePath]
xs) Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)) ([FilePath] -> FilePath
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [FilePath]
xs)

-- | Load input file as a 2-dimensional map of characters.
getInputMap :: Int {- ^ year -} -> Int {- ^ day -} -> IO (Map Coord Char)
getInputMap :: Int -> Int -> IO (Map Coord Char)
getInputMap Int
y Int
d =
  do [FilePath]
xs <- Int -> Int -> IO [FilePath]
getInputLines Int
y Int
d
     Map Coord Char -> IO (Map Coord Char)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Map Coord Char -> IO (Map Coord Char))
-> Map Coord Char -> IO (Map Coord Char)
forall a b. (a -> b) -> a -> b
$! [(Coord, Char)] -> Map Coord Char
forall k a. Ord k => [(k, a)] -> Map k a
SMap.fromList ([FilePath] -> [(Coord, Char)]
coordLines [FilePath]
xs)