icyrock.com

HomeOld blogOld blog 2

PureScript solution to Project Euler problem 59

2022-09-26 17:37

Problem details at Project Euler problem 59 page.

Test

module Euler059Test (euler59suite) where

import Prelude

import Effect.Class (liftEffect)
import Effect.Aff (Milliseconds(..), delay)
import Euler059 (euler59)
import Test.Unit (TestSuite, suite, test)
import Test.Unit.Assert as Assert

euler59suite :: TestSuite
euler59suite =
  suite "Euler 59" do
    test "Real" do
      delay (Milliseconds 0.0)
      Assert.equal 129448 =<< liftEffect euler59

Solution

module Euler059 where

import Prelude

import Data.Array (concat, drop, filter, head, length, range, snoc, take, zipWith)
import Data.Char (toCharCode)
import Data.Foldable (sum)
import Data.Int (fromString)
import Data.Int.Bits (xor)
import Data.Maybe (fromJust)
import Data.String (Pattern(..), split)
import Data.String.CodeUnits (toCharArray)
import Effect (Effect)
import Node.Encoding (Encoding(..))
import Node.FS.Sync (readTextFile)
import Partial.Unsafe (unsafePartial)

toCodes :: String -> Array Int
toCodes txt = 
  let as = split (Pattern ",") txt
  in map (unsafePartial fromJust <<< fromString) as

passwords :: Int -> Array (Array Int)
passwords l =
  let go :: Int -> Array (Array Int) -> Array (Array Int)
      go 0 as = as
      go n as =
        go (n - 1) do
          bs <- as
          cs <- range 97 122
          pure $ bs `snoc` cs
  in go l [[]]

doXor :: Array Int -> Array Int -> Array Int
doXor cs ps =
  let r = (length cs `div` length ps) + 1
      rep 0 _  = []
      rep n as = concat [as, rep (n - 1) as]
  in zipWith xor cs (rep r ps)

xors :: String -> Array (Array Int)
xors txt = 
  let cs = toCodes txt
      ps = passwords 3
  in map (doXor cs) ps

isEnglish :: Array Int -> Boolean
isEnglish as = 
  let wc = map toCharCode <<< toCharArray
      find _ [] = false
      find ns bs
        | take (length ns) bs == ns = true
        | otherwise                 = find ns (drop 1 bs)
  in find (wc "have") as && find (wc "the") as

findEnglish :: String -> Array Int
findEnglish = unsafePartial fromJust <<< head <<< filter isEnglish <<< xors

solve :: String -> Int
solve = sum <<< findEnglish

euler59 :: Effect Int
euler59 = do
  txt <- readTextFile UTF8 "etc/059-cipher.txt"
  pure $ solve txt