Alphametics - haskell track - cli submission failure

Hello, with

$stack test

I have

alphametics> Test suite test passed

for my code. However, for

$ exercism submit src/Alphametics.hs package.yaml

Your solution has been submitted successfully.

I get:

An error occurred

An error occurred while running your tests. This might mean that there was an issue in our infrastructure, or it might mean that you have something in your code that’s causing our systems to break.

Please check your code, and if nothing seems to be wrong, try running the tests again.

The instructions state that it can be solved with a brute force algorithm so I suspect the fact that my code involves generating permutations should not be the cause of the failure. Can you please help me understand why I get the failure on submission via the CLI.? I tried several time via the CLI. No joy. Then I submitted via the editor and it passed first time.

What’s your code for both the src/Alphametics.hs and package.yaml files?

My code for both src/Alphametics.hs and package.yaml follows:

module Alphametics (solve) where

import Data.Char (isLetter)
import qualified Data.List as List
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import qualified Data.Set as Set

data Expression
  = WordExpr String
  | AddExpr Expression Expression
  | MultiplyExpr Expression Expression
  | PowerExpr Expression Expression
  | EqualsExpr Expression Expression
  deriving Show

data Token
  = Word String
  | Add
  | Multiply
  | Power
  | Equals
  deriving (Eq, Ord, Show)

data OperatorPrecedence = Add' | Multiply' | Power'
  deriving (Eq, Ord, Show)

digitValue :: Map.Map Char Int -> (Char, Int) -> Int
digitValue dict (c, x) = (fromMaybe 0 (Map.lookup c dict)) * 10^x

equationIsTrue :: Map.Map Char Int -> Expression -> Bool
equationIsTrue dict (EqualsExpr left right) = eval dict left == eval dict right

eval :: Map.Map Char Int -> Expression -> Int
eval dict (WordExpr s) = foldl (\acc x -> acc + digitValue dict x) 0 $ zip s [n - 1, n - 2 .. 0]
  where
    n = length s
eval dict (AddExpr left right) = eval dict left + eval dict right
eval dict (MultiplyExpr left right) = eval dict left * eval dict right
eval dict (PowerExpr left right) = eval dict left ^ eval dict right

tokensToExpr :: [Token] -> Expression
tokensToExpr (Word a : []) = WordExpr a
tokensToExpr xs 
  | Equals `elem` xs = 
      let (left, right) = tokensToEquation xs
          leftExpr = tokensToExpr left
          rightExpr = tokensToExpr right
      in makeExpression Equals leftExpr rightExpr
  | otherwise =
      let (left, op, right) = tokensToOperandsAndOp xs
          leftExpr = tokensToExpr left
          rightExpr = tokensToExpr right
      in makeExpression op leftExpr rightExpr

tokensToEquation :: [Token] -> ([Token], [Token])
tokensToEquation xs = (left, rs)
  where
    (left, (Equals:rs))   = break (== Equals) xs

makeExpression :: Token -> Expression -> Expression -> Expression
makeExpression Add left right = AddExpr left right
makeExpression Multiply left right = MultiplyExpr left right
makeExpression Power left right = PowerExpr left right
makeExpression Equals left right = EqualsExpr left right

tokensToOperandsAndOp :: [Token] -> ([Token], Token, [Token])
tokensToOperandsAndOp xs =
  let splitToken = findLowestPrecedence xs
      (left, (t:ts)) = span (/= splitToken) xs
  in (left, t, ts)

findLowestPrecedence :: [Token] -> Token
findLowestPrecedence xs = fromPrecedence $ minimum $ map toPrecedence xs'
  where
    xs' = filter (\x -> x `elem` [Add, Multiply, Power]) xs

toPrecedence :: Token -> OperatorPrecedence
toPrecedence Add = Add'
toPrecedence Multiply = Multiply'
toPrecedence Power = Power'

fromPrecedence :: OperatorPrecedence -> Token
fromPrecedence Add' = Add
fromPrecedence Multiply' = Multiply
fromPrecedence Power' = Power

tokenize :: String -> Token
tokenize str
  | all isLetter str = Word str
  | str == "+"       = Add
  | str == "*"       = Multiply
  | str == "^"       = Power
  | str == "=="      = Equals

parse :: String -> [Token]
parse [] = []
parse xs = 
  let _words = words xs
  in map (\w -> tokenize w) _words

subsets ::Int -> [a] -> [[a]]
subsets 0 _      = [[]]
subsets _ []     = []
subsets n (x:xs) = map (x:) (subsets (n-1) xs) ++ subsets n xs

rPermutations :: Int -> [a] -> [[a]]
rPermutations r = concatMap List.permutations . subsets r

makeTrans :: Ord k => [k] -> [a] -> Map.Map k a
makeTrans chars permutation = Map.fromList $ zip chars permutation

solve :: String -> Maybe [(Char, Int)]
solve puzzle = 
  let parts = words puzzle
      _words = filter (\s -> all isLetter s) parts
      chars = Set.fromList(concat _words)
      chars' = Set.toList chars
      r = Set.size chars
      firsts = Set.fromList [head w | w <- _words]
      perms = filter validPerm (rPermutations r [0 .. 9])
        where
          validPerm p = all (\(c,d) -> c `Set.notMember` firsts || d /= 0)
                            (zip chars' p)
      dicts = map (\p -> makeTrans chars' p) perms
      expr = tokensToExpr (parse puzzle)
      result = List.find (\d -> equationIsTrue d expr) dicts 
  in fmap Map.toList result






name: alphametics
version: 1.3.0.6

dependencies:
  - base

library:
  exposed-modules: Alphametics
  source-dirs: src
  ghc-options: -Wall
  dependencies:
    - containers       # List here the packages you
  # - bar       # want to use in your solution.

tests:
  test:
    main: Tests.hs
    source-dirs: test
    dependencies:
      - alphametics
      - hspec

Is this dependency listed both in your local package.yaml and the online editor when your solution passes in the editor?

The test runner doesn’t have internet access at runtime so if you specify a package that it doesn’t already have pre-installed, I could see where that’d cause an error. However, that only makes sense if the passing solution’s package.yaml is different than your local one.

Yes the - containers dependency is listed both in my local package.yaml and in the package.yaml in the online editor.

This ran fine for me on the website. It might’ve been a temporary backend issue.

1 Like

OK that is fine - like I posted - I also found, after several failed CLI submissions, it passed first time on the website via the online editor. I will consider my question answered. :smiley: