Skip to content

Commit bff8154

Browse files
committed
Initialize AoC template.
0 parents  commit bff8154

39 files changed

+1603
-0
lines changed

Diff for: .gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.stack-work/
2+
*~
3+
stack.yaml.lock
4+
dist-newstyle/*
5+
.gitattributes

Diff for: LICENSE

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Copyright Author name here (c) 2020
2+
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
* Redistributions of source code must retain the above copyright
9+
notice, this list of conditions and the following disclaimer.
10+
11+
* Redistributions in binary form must reproduce the above
12+
copyright notice, this list of conditions and the following
13+
disclaimer in the documentation and/or other materials provided
14+
with the distribution.
15+
16+
* Neither the name of Author name here nor the names of other
17+
contributors may be used to endorse or promote products derived
18+
from this software without specific prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Diff for: README.md

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# aoc-template
2+
3+
This is an (opinionated) Advent of Code template for solutions in Haskell.
4+
5+
To use:
6+
- Clone this repository
7+
- Set up a new branch for the year's solutions
8+
- Change the package name, update the GitHub link, etc. You'll also want to remove the .cabal file and let stack generate a new one.
9+
- Fill in the solutions and have fun!
10+
11+
When running from the command line you can pass the option `-d/--day DAY` to run a specific day's solutions. If you do this, then you can also pass `-i/--input FILE` to specify an input file; by default, the program will look for it in `input/DayXX.txt`. You can also pass the argument `--all-days` and all days will be run in order, assuming the input files are in their default places.
12+
13+
Additionally, you can specify the level of detail to print out. By default, the program will print only the answers. If you'd like it to print timing information, use the `-t/--timings` option. Alternatively, if you'd like it to print the output of the parser and error messages in full, use the `-v/--verbose` option.
14+
15+
Example usage:
16+
- `stack run -- -d 9`: Runs Day 9's solutions.
17+
- `stack run -- --day 14 --input "wibble.txt"`: Runs Day 14's solutions, using the input file "wibble.txt".
18+
- `stack run -- -d 1 -i "alex.txt" --timings`: Runs Day 1's solutions, using the input file "alex.txt". Also prints timing information for each solution.
19+
- `stack run -- --all-days`: Runs the solutions from all days.
20+
21+
This template can be used with `ghcid` to compile and run your code every time you save your files. Consider putting the following in your `.bashrc` (or equivalent):
22+
23+
```bash
24+
function day { ghcid --run="Main.performDay (Options (OneDay $1 Nothing) Timings)" }
25+
```
26+
27+
If sourced in a terminal, running the command `day 9`, for example, will, open a `ghcid` session and run your code every time you save, displaying the answers as if you ran the first example command above.
28+
29+
If you think the structure of the `Day` files needs changing to better suit your needs (before starting the project), then make the appropriate changes in `src/Days/Day01.hs` and run the `apply_changes.zsh` file. This will copy Day01 to all the other days, changing Day01 for DayXX as appropriate.
30+
31+
## Default Language Extensions
32+
33+
I've turned several language extensions on by default, including the set of stable and reasonable extensions implied by the `GHC2021` extension pack.
34+
The other extensions enabled by default are:
35+
- `GADTs`
36+
- `LambdaCase`
37+
- `MultiWayIf`
38+
- `OverloadedRecordDot`
39+
- `OverloadedStrings`
40+
- `RecordWildCards`
41+
42+
The reason for these should be pretty clear in most cases.
43+
If you want to change the default extensions, the list is in `package.yaml`.
44+
45+
## Default Dependencies
46+
47+
The default package dependencies for this project are:
48+
- `directory`: This is just for checking if the provided input file exists.
49+
- `time`: For timing the solutions.
50+
- `ansi-term`: For colourful pretty printing.
51+
- `attoparsec`: For the input parser for each day.
52+
- `containers`: For Map, Set, and so on.
53+
- `text`: Because `String`s are bad.
54+
- `optparse-applicative`: For command line parsing.
55+
- `mtl`: Mainly in anticipation that `State` might be useful. `ExceptT` is also used to catch exceptions in `runDay`.
56+
- `vector`: In anticipation that fixed-length arrays will come in handy.

Diff for: Setup.hs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Distribution.Simple
2+
3+
main = defaultMain

Diff for: app/Main.hs

+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
module Main where
2+
3+
{- ORMOLU_DISABLE -}
4+
--- Day imports
5+
import qualified Days.Day01 as Day01 (runDay)
6+
import qualified Days.Day02 as Day02 (runDay)
7+
import qualified Days.Day03 as Day03 (runDay)
8+
import qualified Days.Day04 as Day04 (runDay)
9+
import qualified Days.Day05 as Day05 (runDay)
10+
import qualified Days.Day06 as Day06 (runDay)
11+
import qualified Days.Day07 as Day07 (runDay)
12+
import qualified Days.Day08 as Day08 (runDay)
13+
import qualified Days.Day09 as Day09 (runDay)
14+
import qualified Days.Day10 as Day10 (runDay)
15+
import qualified Days.Day11 as Day11 (runDay)
16+
import qualified Days.Day12 as Day12 (runDay)
17+
import qualified Days.Day13 as Day13 (runDay)
18+
import qualified Days.Day14 as Day14 (runDay)
19+
import qualified Days.Day15 as Day15 (runDay)
20+
import qualified Days.Day16 as Day16 (runDay)
21+
import qualified Days.Day17 as Day17 (runDay)
22+
import qualified Days.Day18 as Day18 (runDay)
23+
import qualified Days.Day19 as Day19 (runDay)
24+
import qualified Days.Day20 as Day20 (runDay)
25+
import qualified Days.Day21 as Day21 (runDay)
26+
import qualified Days.Day22 as Day22 (runDay)
27+
import qualified Days.Day23 as Day23 (runDay)
28+
import qualified Days.Day24 as Day24 (runDay)
29+
import qualified Days.Day25 as Day25 (runDay)
30+
31+
--- Other imports
32+
import Data.Map (Map)
33+
import qualified Data.Map as Map
34+
import Data.Maybe (fromMaybe)
35+
import Options.Applicative
36+
import qualified Control.Applicative.Combinators as C (option)
37+
import Program.RunDay (Day, Verbosity (Quiet, Timings, Verbose))
38+
import Data.List (intercalate)
39+
import Control.Monad (unless, forM_)
40+
41+
-- Data Output
42+
import Text.Printf (printf)
43+
import Program.Color ( withColor )
44+
import System.Console.ANSI (Color(..))
45+
{- ORMOLU_ENABLE -}
46+
47+
data Days
48+
= AllDays
49+
| OneDay
50+
{ day :: Int,
51+
input :: Maybe String
52+
}
53+
deriving (Show)
54+
55+
data Options = Options Days Verbosity
56+
57+
dayParser :: Parser Days
58+
dayParser = (OneDay <$> day <*> input) <|> allDays
59+
where
60+
day =
61+
option auto $
62+
long "day" <> short 'd' <> metavar "DAY"
63+
<> help "Present the solutions for one day."
64+
65+
input =
66+
optional $
67+
strOption $
68+
long "input" <> short 'i' <> metavar "FILE"
69+
<> help "The file to read the selected day's input from."
70+
71+
allDays =
72+
flag' AllDays $
73+
long "all-days"
74+
<> help
75+
( unwords
76+
[ "Present solutions for all of the days of",
77+
"Advent of Code, with default input file names."
78+
]
79+
)
80+
81+
optionsParser :: Parser Options
82+
optionsParser = Options <$> dayParser <*> verbosityParser
83+
where
84+
verbosityParser :: Parser Verbosity
85+
verbosityParser =
86+
C.option Quiet $
87+
( flag' Verbose $
88+
long "verbose" <> short 'v'
89+
<> help
90+
( unwords
91+
[ "Whether to print out extra info, such as the",
92+
"result of the input parser, and more detailed",
93+
"error messages.",
94+
"Also enables timing of solutions."
95+
]
96+
)
97+
)
98+
<|> ( flag' Timings $
99+
long "timings" <> short 't'
100+
<> help
101+
( unwords
102+
["Whether to enable timing of the solutions."]
103+
)
104+
)
105+
106+
days :: Map Int (Day, String)
107+
days =
108+
Map.fromList . zip [1 ..] $
109+
[ (Day01.runDay, "input/Day01.txt"),
110+
(Day02.runDay, "input/Day02.txt"),
111+
(Day03.runDay, "input/Day03.txt"),
112+
(Day04.runDay, "input/Day04.txt"),
113+
(Day05.runDay, "input/Day05.txt"),
114+
(Day06.runDay, "input/Day06.txt"),
115+
(Day07.runDay, "input/Day07.txt"),
116+
(Day08.runDay, "input/Day08.txt"),
117+
(Day09.runDay, "input/Day09.txt"),
118+
(Day10.runDay, "input/Day10.txt"),
119+
(Day11.runDay, "input/Day11.txt"),
120+
(Day12.runDay, "input/Day12.txt"),
121+
(Day13.runDay, "input/Day13.txt"),
122+
(Day14.runDay, "input/Day14.txt"),
123+
(Day15.runDay, "input/Day15.txt"),
124+
(Day16.runDay, "input/Day16.txt"),
125+
(Day17.runDay, "input/Day17.txt"),
126+
(Day18.runDay, "input/Day18.txt"),
127+
(Day19.runDay, "input/Day19.txt"),
128+
(Day20.runDay, "input/Day20.txt"),
129+
(Day21.runDay, "input/Day21.txt"),
130+
(Day22.runDay, "input/Day22.txt"),
131+
(Day23.runDay, "input/Day23.txt"),
132+
(Day24.runDay, "input/Day24.txt"),
133+
(Day25.runDay, "input/Day25.txt")
134+
]
135+
136+
performDay :: Options -> IO ()
137+
performDay (Options d v) = case d of
138+
AllDays -> do
139+
results <-
140+
let eachDay d (dayFunc, inputFile) = do
141+
withColor Magenta $ putStrLn $ printf "\n***Day %02d***" d
142+
dayFunc v inputFile
143+
in sequence $ Map.mapWithKey eachDay days
144+
145+
printSummary results
146+
OneDay {..} -> case days Map.!? day of
147+
Nothing -> putStrLn "Invalid day provided. There are 25 days in Advent."
148+
Just (dayFunc, inputFile) -> do
149+
let i' = fromMaybe inputFile input
150+
withColor Magenta $ putStrLn $ printf "\n***Day %02d***" day
151+
dayFunc v i'
152+
withColor Magenta $ putStrLn "************"
153+
154+
printSummary :: Map Int (Maybe Double, Maybe Double) -> IO ()
155+
printSummary results = do
156+
putStrLn "\n************\n Summary: "
157+
let partsA = Map.mapKeys ((++ " (a)") . printf "%02d") $ fmap fst results
158+
partsB = Map.mapKeys ((++ " (b)") . printf "%02d") $ fmap snd results
159+
parts = Map.toList $ partsA <> partsB
160+
161+
fails = [p | (p, Nothing) <- parts]
162+
fasts = [(p, t) | (p, Just t) <- parts, t < 1]
163+
slows = [(p, t) | (p, Just t) <- parts, t >= 1]
164+
165+
putStr $ printf "\n%d parts " $ length fasts
166+
withColor Green $ putStr "completed in under 1 second"
167+
putStrLn ".\nOf the remainder:"
168+
unless (null fails) $ do
169+
putStr $ printf " %d parts" $ length fails
170+
withColor Red $ putStr " failed"
171+
putStrLn $ ":\n " ++ intercalate ", " fails
172+
unless (null slows) $ do
173+
putStr $ printf " %d parts" $ length slows
174+
withColor Yellow $ putStr " took over 1 second to complete"
175+
putStrLn ":"
176+
forM_ slows $
177+
\(p, t) -> putStrLn $ printf " %s took %.2f seconds" p t
178+
179+
main :: IO ()
180+
main = performDay =<< execParser opts
181+
where
182+
opts =
183+
info
184+
(optionsParser <**> helper)
185+
(fullDesc <> progDesc "Prints out some Advent of Code solutions.")

Diff for: apply_changes.zsh

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/zsh
2+
3+
for x in "02" "03" "04" "05" "06" "07" "08" "09" "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "20" "21" "22" "23" "24" "25"; do cat src/Days/Day01.hs | sed "s/01/$x/" > src/Days/Day"$x".hs; done

Diff for: hie.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
cradle:
2+
stack:

Diff for: package.yaml

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: aoc-template
2+
version: 2022.0
3+
github: "samcoy3/advent-of-code-template"
4+
license: BSD3
5+
author: "Sam Coy"
6+
maintainer: "[email protected]"
7+
copyright: "2022 Sam Coy"
8+
9+
extra-source-files:
10+
- README.md
11+
12+
# Metadata used when publishing your package
13+
# synopsis: Short description of your package
14+
# category: Web
15+
16+
# To avoid duplicated efforts in documentation and dealing with the
17+
# complications of embedding Haddock markup inside cabal files, it is
18+
# common to point users to the README.md file.
19+
description: A template for Advent of Code projects in Haskell
20+
21+
dependencies:
22+
- base >= 4.7 && < 5
23+
- time
24+
- ansi-terminal
25+
- directory
26+
- optparse-applicative
27+
- parser-combinators
28+
- attoparsec
29+
- containers
30+
- text
31+
- mtl
32+
- vector
33+
34+
library:
35+
source-dirs: src
36+
default-extensions:
37+
- LambdaCase
38+
- MultiWayIf
39+
- RecordWildCards
40+
- OverloadedRecordDot
41+
- OverloadedStrings
42+
- GADTs
43+
- GHC2021
44+
45+
executables:
46+
aoc-template-exe:
47+
main: Main.hs
48+
source-dirs: app
49+
ghc-options:
50+
- -threaded
51+
- -rtsopts
52+
- -with-rtsopts=-N4
53+
- -with-rtsopts=-qa
54+
- -with-rtsopts=-C0
55+
- -funfolding-use-threshold=16
56+
- -fexcess-precision
57+
- -optc-O3
58+
- -optc-ffast-math
59+
- -O2
60+
default-extensions:
61+
- LambdaCase
62+
- MultiWayIf
63+
- RecordWildCards
64+
- OverloadedRecordDot
65+
- OverloadedStrings
66+
- GADTs
67+
- GHC2021
68+
dependencies:
69+
- aoc-template

0 commit comments

Comments
 (0)