summaryrefslogtreecommitdiffstats
path: root/BrainFuck.hs
blob: aa724b89ebadd5790ef2f612034e2a7016aa41bf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
{-# LANGUAGE FlexibleInstances #-}
module BrainFuck (runBF, BF(..), strToBF) where

import qualified Data.Sequence as Sequence
import Data.Foldable (toList)
import qualified System.Random as Random
import qualified Evolve
import RandomState (splitG)
import qualified Control.Monad.State as State
import System.IO.Unsafe (unsafePerformIO)
import Control.Concurrent (yield)
import qualified EditDistance
import Data.List (intercalate)

data BF = Plus | Minus | ML | MR | Print | Loop [BF] deriving Eq

instance Show BF where
  show Plus = "+"
  show Minus = "-"
  show ML = "<"
  show MR = ">"
  show Print = "."
  show (Loop bf) = '[' : intercalate "" (map show bf) ++ "]"

mutateString :: Random.RandomGen g => String -> State.State g String
mutateString [] = do r <- fst . Random.random <$> splitG
                     if r < Evolve.mutateProbability
                       then (: []) . fst . Random.randomR ('+', ']') <$> splitG
                       else return []
mutateString (x : xs) = do i <- insert
                           d <- delete
                           r <- rest
                           return $ i ++ d ++ r
  where insert = do r <- fst . Random.random <$> splitG
                    if r < Evolve.mutateProbability
                      then (: []) . fst . Random.randomR ('+', ']') <$> splitG
                      else return []
        delete = do r <- fst . Random.random <$> splitG
                    if r < Evolve.mutateProbability
                      then return []
                      else return [x]
        rest = mutateString xs

instance Evolve.Evolvable [BF] where
  mutate bf = BrainFuck.strToBF <$> mutateString (intercalate "" (map show bf))
  score bf = 100 - fromIntegral (EditDistance.dist (BrainFuck.runBF bf) "Hello World!")

randomBF :: Random.RandomGen g => State.State g BF
randomBF = do r <- fst . Random.randomR (0, 5 :: Int) <$> splitG
              case r of
                0 -> return Plus
                1 -> return Minus
                2 -> return ML
                3 -> return MR
                4 -> return Print
                _ -> Loop <$> Evolve.mutate []

instance Random.Random BF where
  -- TODO: may as well have a correct implementation here
  randomR _ _ = undefined
  random g = State.runState randomBF g

extend :: a -> Int -> Sequence.Seq a -> Sequence.Seq a
extend def i s
  | i < length s = s
  | otherwise = s Sequence.>< Sequence.replicate (i - length s + 1) def

safeAdjust :: a -> (a -> a) -> Int -> Sequence.Seq a -> Sequence.Seq a
safeAdjust def f i s = Sequence.adjust f i (extend def i s)

iAdj :: Num a => (a -> a) -> Int -> Sequence.Seq a -> Sequence.Seq a
iAdj = safeAdjust 0

safeIndex :: a -> Sequence.Seq a -> Int -> a
safeIndex def s i = (extend def i s) `Sequence.index` i

iInd :: Num a => Sequence.Seq a -> Int -> a
iInd = safeIndex 0

safeEnumToChar :: Enum e => e -> Char
safeEnumToChar e = toEnum $ i `mod` (fromEnum '~' - spc + 1) + spc
  where i = fromEnum e
        spc = fromEnum ' '

runBF :: [BF] -> String
runBF bf = let (_, _, s) = foldl go (Sequence.singleton 0, 0, Sequence.empty) bf
           in toList s
  where go :: (Sequence.Seq Integer, Int, Sequence.Seq Char) -> BF
           -> (Sequence.Seq Integer, Int, Sequence.Seq Char)
        go (a, dp, s) Plus = (iAdj succ dp a, dp, s)
        go (a, dp, s) Minus = (iAdj pred dp a, dp, s)
        go (a, dp, s) ML = (a, max (pred dp) 0, s)
        go (a, dp, s) MR = (a, succ dp, s)
        go (a, dp, s) Print = (a, dp, s Sequence.|> safeEnumToChar (a `iInd` dp))
        go st@(a, dp, _) cmd@(Loop inner)
          | 0 <- a `iInd` dp = st
          | otherwise = unsafePerformIO yield `seq` go (foldl go st inner) cmd

strToBF' :: String -> ([BF], String)
strToBF' [] = ([], [])
strToBF' ('[':ss) =
 case strToBF' ss of
  (bf, ']':rest) -> let (bf', extra) = strToBF' rest
                    in (Loop bf : bf', extra)
  -- Failure case, but we tolerate it
  (bf, rest) -> ([Loop bf], rest)
strToBF' rest@(']':_) = ([], rest)
strToBF' (x:ss)
  | Just cmd <- conv x = let (bf, extra) = strToBF' ss
                         in (cmd : bf, extra)
  | otherwise = strToBF' ss

conv :: Char -> Maybe BF
conv '+' = Just Plus
conv '-' = Just Minus
conv '<' = Just ML
conv '>' = Just MR
conv '.' = Just Print
conv _ = Nothing

strToBF :: String -> [BF]
strToBF s = let (bf, _) = strToBF' s
            in bf