packages feed

advent-of-code-ocr-0.1.0.0: src/Advent/OCR/Internal.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections   #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- |
-- Module      : Advent.OCR.Internal
-- Copyright   : (c) Justin Le 2020
-- License     : BSD3
--
-- Maintainer  : justin@jle.im
-- Stability   : experimental
-- Portability : non-portable
--
-- Internal utility functions for "Advent.OCR".


module Advent.OCR.Internal (
  -- * Parse
    V2(..)
  , Point
  , parseLettersV2
  , parseLettersEitherV2
  , parseAsciiMapV2
  -- * Letter Map
  , LetterMap(..)
  , lookupLetterMap
  , defaultLetterMap
  , rawLetterforms1
  , rawLetterforms2
  , parseLetterMap
  -- * Utility
  , contiguousShapes
  , contiguousShapesBy
  ) where

import           Advent.OCR.LetterMap
import           Data.Default.Class
import           Data.Maybe
import           Data.Monoid
import           Data.Set                   (Set)
import           Language.Haskell.TH.Lift
import qualified Data.Set                   as S

-- | A version of 'Advent.OCR.parseLetters' taking 'Point'.  Used
-- internally.
parseLettersV2
    :: LetterMap
    -> Set Point
    -> Maybe String
parseLettersV2 lm = (fmap . map) (either (const '?') id)
                  . parseLettersEitherV2 lm

-- | A version of 'Advent.OCR.parseLettersEither' taking (and returning)
-- 'Point'. Used internally.
parseLettersEitherV2
    :: LetterMap
    -> Set Point
    -> Maybe [Either (Set Point) Char]
parseLettersEitherV2 lm letters = listToMaybe attempts
  where
    attempts =
      [ res
      | refl <- [id, reflX]
      , rots <- [id, perp, negate, negate . perp]
      , let ls = S.map (rots . refl) letters
            ((Sum n, Sum goodCount), res) = tryMe ls
            percGood = goodCount / n :: Double
      , n > 0 && percGood >= 0.5
      ]
    tryMe = traverse (\c -> maybe ((Sum 1, Sum 0), Left c) (((Sum 1, Sum 1),) . Right) . lookupLetterMap c $ lm)
          . contiguousShapesBy v2x
    perp (V2 x y) = V2 (negate y) x
    reflX (V2 x y) = V2 (negate x) y

-- | Default is compatible with all challenges in Advent of Code 2015 to
-- 2019.
instance Default LetterMap where
    def = defaultLetterMap

-- | The default lettermap compatible all challenges in Advent of Code 2015
-- - 2019.
defaultLetterMap :: LetterMap
defaultLetterMap = $( lift $ uncurry parseLetterMap rawLetterforms1
                          <> uncurry parseLetterMap rawLetterforms2
                    )