{-# OPTIONS_GHC -Wall #-}-- {-# LANGUAGE FlexibleContexts #-}-- The `(..)` syntax represents all of the constructors for the data type. [1]-- Without that export, we can pattern-match in BinaryDiagnostic.hs because we-- run into a "Not in scope: data constructor ‘BinaryDiagnostics’" error.---- [1]: https://stackoverflow.com/a/34548070/7812406moduleAoC2021InputParser(parseBinaryDiagnosticInput,parseBingoInput,parseHydrothermalVents,parseLanternfishInternalTimers,parseHorizontalCrabPositions,parseSevenSegmentsDisplay,parseHeightMap,)whereimportBinaryDiagnostic.BinaryDiagnostic(BinaryDiagnostics(..),diagNums,diagWidth)importControl.DeepSeq(($!!))importData.Char(digitToInt,ord)importData.Maybe(isJust,listToMaybe)importData.String(IsString(fromString))importqualifiedData.List.SplitasSplitimportqualifiedData.VectorasVimportGiantSquid(Board,DrawnNumbers,Tile)importqualifiedHydrothermalVenture.HydrothermalVentureasHV(LineSegment(..),Point(..))importPaths_advent_of_code_y2021(getDataFileName)importSystem.IO(IOMode(ReadMode),hGetContents,withFile)importText.Parsec(endOfLine)importText.ParserCombinators.ParsecimportText.Read(readMaybe)importqualifiedAoC2021.SevenSegmentSearchasSevenSegmentSearch(SevenSegmentDisplay(..))importqualifiedData.IntSetasIntSetimportqualifiedAoC2021.SmokeBasinasSmokeBasin(HeightMap)importqualifiedData.Massiv.ArrayasMassivArray(empty,fromLists')importqualifiedData.Massiv.CoreasMassivCore(Comp(Seq))
Day 03: Binary Diagnostic
-- The `Numeric` module has a `readBin` function [1], but for some reason, I get-- a "Variable not in scope: readBin" error. However, `readDec`, `readOct` and-- `readHex` work...---- [1]: https://hackage.haskell.org/package/base-4.16.0.0/docs/Numeric.html#v:readBinreadBin'::String->IntreadBin'binString=fst$foldrf(0,1)binStringwherefc(s,powerOf2)=(s+powerOf2*digitToIntc,powerOf2*2)-- The file is a list of binary digits of the same width, e.g.---- 00100-- 11110parseBinaryDiagnosticInput::FilePath->IOBinaryDiagnosticsparseBinaryDiagnosticInputfp=dovalidatedFP<-getDataFileNamefpwithFilevalidatedFPReadMode(\h->dos<-hGetContentshletls=lines(fromStrings)letwidth=maybe0length(listToMaybels)-- With lazy I/O, `h` gets closed as soon as we leave `withFile`. This-- introduces errors of the form "hGetContents: illegal operation-- (delayed read on closed handle)". [1]---- In the expression `f $!! x`, `x` is fully evaluated before `f` is-- applied to it. [1] [2]---- Alternatively, one can use `readFile` which holds the file open until-- it has finished reading the file [1]. However, [3] says that-- `readFile` reads the file lazily. Hmm...---- [1]: https://stackoverflow.com/a/26949379/7812406-- [2]: https://hackage.haskell.org/package/deepseq-1.4.6.1/docs/Control-DeepSeq.html#v:-36--33--33--- [3]: https://hackage.haskell.org/package/base-4.16.0.0/docs/html#v:readFilereturn$!!(BinaryDiagnostics{diagWidth=width,diagNums=mapreadBin'ls}))
Day 04: Giant Squid
-- `endBy` expects the very last item to be followed by the separator. It-- continues parsing until it can't parse any more content. [1]---- TODO: Is it possible to define `bingoFile` as `endBy bingoSection doubleEOL`?-- The last double EOL is partly consumed by `bingoElementSeparator` that also-- matches a new line.---- [1]: http://book.realworldhaskell.org/read/using-parsec.htmlbingoFile::Parser[[String]]bingoFile=endBybingoSectionendOfLine-- `sepBy` takes two functions as arguments. The first function parses some sort-- of content, while the second function parses a separator. `sepBy` starts by-- trying to parse content, then separators, and alternates back and forth until-- it can't parse a separator. It returns a list of all the content it was able-- to parse. [1]---- [1]: http://book.realworldhaskell.org/read/using-parsec.htmlbingoSection::Parser[String]bingoSection=sepBybingoElementbingoElementSeparatorbingoElement::ParserStringbingoElement=manydigitbingoElementSeparator::ParserCharbingoElementSeparator=try(char',')<|>try(char' ')<?>"separator for element"-- `try` applies a parser, and if it fails, then `try` behaves as if it hadn't-- consumed any input at all, and tries the option on the right of the `<|>`.-- Note that `try` only has an effect if its on the left side of a `<|>`. [1]---- `<?>` tries the parser on its left. In the event of a failure, it presents an-- error message instead of trying another parser. The error message should-- complete the sentence, "Expecting...". [1]---- [1]: http://book.realworldhaskell.org/read/using-parsec.html-- eol = try (string "\n\r\n\r")-- <|> try (string "\r\n\r\n")-- <|> try (string "\n\n")-- <|> string "\r\r"-- <?> "end of line followed by empty line"-- File format: the first line contains the numbers to draw. The rest is a new-- line followed by a 5x5 grid of numbers representing a board.---- 7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1---- 22 13 17 11 0-- 8 2 23 4 24-- 21 9 14 16 7-- 6 10 3 18 5-- 1 12 20 15 19---- 3 15 0 2 22-- 9 18 13 17 5-- 19 8 7 25 23-- 20 11 10 24 4-- 14 21 16 12 6parseBingoInput::FilePath->IO(DrawnNumbers,[Board])parseBingoInputfp=dodataFp<-getDataFileNamefp-- Objective: Try using `readFile` and see if `($!!)` is needed to fully-- evaluate the contents before exiting this function.fileContents<-readFiledataFpcaseparsebingoFile""fileContentsofLefte->doputStrLn"Error parsing input"printereturn([],[])Rightr->doletparseInt::String->IntparseInts=reads::IntdrawnNumbers=mapparseInt(headr)tile::String->Tiletilex=(parseIntx,False)-- TODO: Figure out how to parse multiple spaces as-- separators, and get rid of the `isValidNum` filter.isValidNum::String->BoolisValidNums=isJust(readMaybes::MaybeInt)parseBoards::[[String]]->[Board]parseBoards([_]:l1:l2:l3:l4:l5:ls)=letnums=V.filterisValidNum(V.fromList(l1++l2++l3++l4++l5))board=V.maptilenumsin(board,False):parseBoardslsparseBoards[l1,l2,l3,l4,l5]=letnums=V.filterisValidNum(V.fromList(l1++l2++l3++l4++l5))board=V.maptilenumsin[(board,False)]parseBoards_=[]return(drawnNumbers,parseBoards(tailr))
Day 05: Hydrothermal Venture
hydrothermalFile::Parser[HV.LineSegment]hydrothermalFile=endByhydrothermalLineendOfLine-- Parses "0,9 -> 5,9" into a `LineSegment`.hydrothermalLine::ParserHV.LineSegmenthydrothermalLine=dop1<-commaSeparatedCoordinates-- Underscores to avoid shadowing._<-string" -> "p2<-commaSeparatedCoordinatesreturnHV.LineSegment{HV.p1=p1,HV.p2=p2}-- Parses "0,9" into (0, 9)commaSeparatedCoordinates::ParserHV.PointcommaSeparatedCoordinates=dox<-many1digit_<-char','y<-many1digitreturnHV.Point{HV.x=readx::Int,HV.y=ready::Int}reportError::ParseError->IO()reportErrore=doputStrLn"Error parsing input"printeparseHydrothermalVents::FilePath->IO[HV.LineSegment]parseHydrothermalVentsfp=dodataFp<-getDataFileNamefpfileContents<-readFiledataFpcaseparsehydrothermalFile"Hydrothermal Parser"fileContentsofLefte->do{reportErrore;return[]}Rightr->returnrsingleLineCommaDelimitedFile::Parser[Int]singleLineCommaDelimitedFile=sepBy(dox<-many1digit;return(readx::Int))(char',')parseSingleLineCommaDelimitedFile::FilePath->IO[Int]parseSingleLineCommaDelimitedFilefp=dodataFp<-getDataFileNamefpfileContents<-readFiledataFpcaseparsesingleLineCommaDelimitedFile"Single Comma-Delimited Int Parser"fileContentsofLefte->do{reportErrore;return[]}Rightr->returnr
-- Sample line:-- fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbgsevenSegmentsDisplayLine::ParserSevenSegmentSearch.SevenSegmentDisplaysevenSegmentsDisplayLine=do-- TODO: Is there syntax for avoiding the intermediate _allSegments variable?_allSegments<-many1(noneOf"\r\n")letuniquePatternsAndOutputs=splitAt10$Split.split(Split.dropDelims.Split.dropInnerBlanks$Split.oneOf"| ")_allSegmentsletstringsToIntSets=map(IntSet.fromList.mapord)::[String]->[IntSet.IntSet]returnSevenSegmentSearch.SevenSegmentDisplay{SevenSegmentSearch.uniquePatterns=stringsToIntSets(fstuniquePatternsAndOutputs),SevenSegmentSearch.outputValues=stringsToIntSets(snduniquePatternsAndOutputs)}sevenSegmentsDisplayFile::Parser[SevenSegmentSearch.SevenSegmentDisplay]sevenSegmentsDisplayFile=endBysevenSegmentsDisplayLineendOfLineparseSevenSegmentsDisplay::FilePath->IO[SevenSegmentSearch.SevenSegmentDisplay]parseSevenSegmentsDisplayfp=dodataFp<-getDataFileNamefpfileContents<-readFiledataFpcaseparsesevenSegmentsDisplayFile"Seven Segment Display"fileContentsofLefte->do{reportErrore;return[]}Rightr->do{returnr}
I gave up on using parsec to parse a line of the form cg bdaec gdafb agbcfd gdcbef | cg cg fdcagb cbg because the | and space delimiter kept tripping
up my endBy sevenSegmentsDisplayLine endOfLine construction. Interested in how
others parsed this line.
Day 8 - Advent of Code 2021.
Eric Wastl.
adventofcode.com.
Accessed Mar 7, 2022.
Part I Description
You barely reach the safety of the cave when the whale smashes into the cave
mouth, collapsing it. Sensors indicate another exit to this cave at a much
greater depth, so you have no choice but to press on.
As your submarine slowly makes its way through the cave system, you notice that
the four-digit seven-segment displays in your submarine are malfunctioning;
they must have been damaged during the escape. You’ll be in a lot of trouble
without them, so you’d better figure out what’s wrong.