{-|
Module      : Hangman Word Module
Description : Library to handle an __hangman solution word.__
Copyright   : >implying
License     : >implying
Maintainer  : Florian Hageneder
Stability   : none
Portability : what?
-}
module Word where

import Data.Char (toLower)
import Data.List

-- | Placeholder for unsolved chars in solution
placeholder = '_' :: Char

-- | Solution of a Hangman game. Unguessd chars are marked with a False.
type SolutionWord = [(Char, Bool)]

{- | Creates a SolutionWord from a given String

>>> createSolutionWord "abc"
[('a', False), ('b', False), ('b', False)]
-}
createSolutionWord ::
  String -- ^ String to play with
  -> SolutionWord -- ^ Solution to create a game with
createSolutionWord = solveChar '.' . solveChar ',' . solveChar '!' . solveChar '?' . solveChar ';' . solveChar '\'' . solveChar ' ' . map (\char -> (char, False))

{- | Checks if a given char is in the word.

>>> tryChar 'a' "Hangman"
True
-}
tryChar ::
  Char -- ^ The char the player played
  -> SolutionWord -- ^ The word that is being played3
  -> Bool -- ^ wether guessed char was right
tryChar try solution = not . null $ elemIndices try (map fst solution)

{- | Applys a guess on the solution.

>>> solveChar a [('a', false), ('b', true)]
[('a', True), ('b', True)]
-}
solveChar ::
  Char -- ^ Char that player tries to add
  -> SolutionWord -- ^ Current game state
  -> SolutionWord -- ^ New state of progress
solveChar try = map (\(char, state) -> (char, state || toLower char == toLower try))

{- | Checks of the given word matches the solution.

>>> solveWord "ab" [('a', False), ('b', False)]
(True, [('a', True), ('b', True)])
-}
solveWord ::
  String  -- ^ String to compare with the solution
  -> SolutionWord -- ^ Solution to check against
  -> (Bool, SolutionWord) -- ^ (if it was successfull, new State of the solution word)
solveWord try word =
  if map fst word == try
    then (True, map (\x -> (fst x, True)) word)
    else (False, word)

{- | Converts a solution into a String

>>> showSolution [('a', True), ('b', False)]
"a_"
-}
showSolution ::
  SolutionWord -- ^ SolutionWord to show
  -> String -- ^ Stringified version of the solution
showSolution = map (\sol -> if snd sol then fst sol else placeholder)

{- | Checks if there are unknown chars in a solutionWord left.

>>> isPlayable [('a', True)]
False
-}
isPlayable ::
  SolutionWord -- ^ Word to check
  -> Bool -- ^ Wether the word is still playable
isPlayable word = not . null $ filter (not . snd) word