refactored to use Data.Lens

master
Abhinav Sarkar 2011-10-05 19:35:14 +05:30
parent bfa77fc13d
commit 82361f606a
4 changed files with 88 additions and 67 deletions

View File

@ -1,9 +1,12 @@
module AI.Vacuum.TableLookupAgent where
import AI.Vacuum import AI.Vacuum
import Control.Monad.State import Control.Monad.State
import Data.Lens.Common
import Data.Maybe (isJust, isNothing, fromJust) import Data.Maybe (isJust, isNothing, fromJust)
-- Problem 2.6 -- Problem 2.6
possiblePerceptsHistories :: [PerceptsHistory] possiblePerceptsHistories :: [PerceptsHistory]
possiblePerceptsHistories = takeWhile ((<= 9) . length) $ possiblePerceptsHistories = takeWhile ((<= 9) . length) $
[[]] : [[PhotoSensor]] : concatMap (\s -> [[] : s, [PhotoSensor] : s]) possiblePerceptsHistories [[]] : [[PhotoSensor]] : concatMap (\s -> [[] : s, [PhotoSensor] : s]) possiblePerceptsHistories
@ -16,19 +19,19 @@ possiblePerceptsHistories = takeWhile ((<= 9) . length) $
-- X -> X -- X -> X
chooseAction :: PerceptsHistory -> Maybe Action chooseAction :: PerceptsHistory -> Maybe Action
chooseAction ph = chooseAction ph =
case ph of case ph of
[] -> Just GoForward [] -> Just GoForward
[[]] -> Just TurnRight [[]] -> Just TurnRight
[[PhotoSensor]] -> Just SuckDirt [[PhotoSensor]] -> Just SuckDirt
(p:ps) -> (p:ps) ->
case lookup ps perceptsHistoryToActionMap of case lookup ps perceptsHistoryToActionMap of
Just (Just prevAction) -> chooseAction' ph prevAction Just (Just prevAction) -> chooseAction' ph prevAction
_ -> Nothing _ -> Nothing
chooseAction' :: PerceptsHistory -> Action -> Maybe Action chooseAction' :: PerceptsHistory -> Action -> Maybe Action
chooseAction' ph prevAction chooseAction' ph prevAction
| prevAction == TurnRight || prevAction == TurnLeft = | prevAction == TurnRight || prevAction == TurnLeft =
case head ph of case head ph of
[] -> Just GoForward [] -> Just GoForward
[PhotoSensor] -> Nothing [PhotoSensor] -> Nothing
@ -41,58 +44,57 @@ chooseAction' ph prevAction
[] -> Just TurnRight [] -> Just TurnRight
[PhotoSensor] -> Just SuckDirt [PhotoSensor] -> Just SuckDirt
| prevAction == TurnOff = error "Cannot move after turnoff" | prevAction == TurnOff = error "Cannot move after turnoff"
perceptsHistoryToActionMap :: [(PerceptsHistory, Maybe Action)] perceptsHistoryToActionMap :: [(PerceptsHistory, Maybe Action)]
perceptsHistoryToActionMap = perceptsHistoryToActionMap =
map (\ph -> (ph, chooseAction ph)) possiblePerceptsHistories map (\ph -> (ph, chooseAction ph)) possiblePerceptsHistories
grid1 = gridFromCellList [ grid1 = gridFromCellList [
Cell (0, 0) Home, Cell (1, 0) Empty, Cell (0, 0) Home, Cell (1, 0) Empty,
Cell (0, 1) Empty, Cell (1, 1) Empty Cell (0, 1) Empty, Cell (1, 1) Empty
] ]
grid2 = gridFromCellList [ grid2 = gridFromCellList [
Cell (0, 0) Home, Cell (1, 0) Dirt, Cell (0, 0) Home, Cell (1, 0) Dirt,
Cell (0, 1) Empty, Cell (1, 1) Empty Cell (0, 1) Empty, Cell (1, 1) Empty
] ]
grid3 = gridFromCellList [ grid3 = gridFromCellList [
Cell (0, 0) Home, Cell (1, 0) Empty, Cell (0, 0) Home, Cell (1, 0) Empty,
Cell (0, 1) Dirt, Cell (1, 1) Empty Cell (0, 1) Dirt, Cell (1, 1) Empty
] ]
grid4 = gridFromCellList [ grid4 = gridFromCellList [
Cell (0, 0) Home, Cell (1, 0) Empty, Cell (0, 0) Home, Cell (1, 0) Empty,
Cell (0, 1) Empty, Cell (1, 1) Dirt Cell (0, 1) Empty, Cell (1, 1) Dirt
] ]
grid5 = gridFromCellList [ grid5 = gridFromCellList [
Cell (0, 0) Home, Cell (1, 0) Dirt, Cell (0, 0) Home, Cell (1, 0) Dirt,
Cell (0, 1) Dirt, Cell (1, 1) Empty Cell (0, 1) Dirt, Cell (1, 1) Empty
] ]
grid6 = gridFromCellList [ grid6 = gridFromCellList [
Cell (0, 0) Home, Cell (1, 0) Empty, Cell (0, 0) Home, Cell (1, 0) Empty,
Cell (0, 1) Dirt, Cell (1, 1) Dirt Cell (0, 1) Dirt, Cell (1, 1) Dirt
] ]
grid7 = gridFromCellList [ grid7 = gridFromCellList [
Cell (0, 0) Home, Cell (1, 0) Dirt, Cell (0, 0) Home, Cell (1, 0) Dirt,
Cell (0, 1) Empty, Cell (1, 1) Dirt Cell (0, 1) Empty, Cell (1, 1) Dirt
] ]
runCleaner :: Cleaner -> State Grid Cleaner runCleaner :: Cleaner -> State Grid Cleaner
runCleaner cleaner@(Cleaner _ _ _ ph _) = do runCleaner cleaner = do
case chooseAction ph of case chooseAction $ cleaner^.perceptsHist of
Just action -> do Just action -> do
cleaner <- doAction action cleaner cleaner' <- doAction action cleaner
let ph = clPrcptsHist cleaner if InfraredSensor `elem` (head $ cleaner'^.perceptsHist)
if InfraredSensor `elem` (head ph) then doAction TurnOff cleaner'
then doAction TurnOff cleaner else runCleaner cleaner'
else runCleaner cleaner
_ -> doAction TurnOff cleaner _ -> doAction TurnOff cleaner
simulateOnGrid :: Grid -> (Cleaner, Grid) simulateOnGrid :: Grid -> (Cleaner, Grid)
simulateOnGrid grid = simulateOnGrid grid =
runState (runCleaner cleaner) grid runState (runCleaner cleaner) grid
where cleaner = createCleaner (fromJust $ cell (0, 0) grid) East where cleaner = createCleaner (fromJust $ lookupCell (0, 0) grid) East

View File

@ -33,4 +33,3 @@ makeGrid minMaxWidth minMaxHeight dirtProb = do
(\m p -> makeCell p dirtProb >>= \c -> return $ M.insert p c m) (\m p -> makeCell p dirtProb >>= \c -> return $ M.insert p c m)
(M.singleton (0,0) (Cell (0,0) Home)) (M.singleton (0,0) (Cell (0,0) Home))
[(x,y) | x <- range (0, width - 1), y <- range (0, height -1), (x,y) /= (0,0)] [(x,y) | x <- range (0, width - 1), y <- range (0, height -1), (x,y) /= (0,0)]

View File

@ -1,5 +1,8 @@
module AI.Vacuum.ReflexAgent where
import AI.Vacuum import AI.Vacuum
import AI.Vacuum.RandomGrid import AI.Vacuum.RandomGrid
import Data.Lens.Common
import Control.Monad.State import Control.Monad.State
import Control.Monad.Identity import Control.Monad.Identity
import System.Random import System.Random
@ -24,12 +27,13 @@ chooseAction percepts
| otherwise = return GoForward | otherwise = return GoForward
runCleaner :: Int -> Cleaner -> StateT Grid RandomState Cleaner runCleaner :: Int -> Cleaner -> StateT Grid RandomState Cleaner
runCleaner turnsLeft cleaner@(Cleaner _ _ _ ph _) = runCleaner turnsLeft cleaner =
if turnsLeft == 1 if turnsLeft == 1
then do then do
cleaner' <- doAction TurnOff cleaner cleaner' <- doAction TurnOff cleaner
return cleaner' return cleaner'
else do else do
let ph = cleaner^.perceptsHist
cleaner'' <- case ph of cleaner'' <- case ph of
[] -> do [] -> do
cleaner' <- doAction GoForward cleaner cleaner' <- doAction GoForward cleaner
@ -39,11 +43,11 @@ runCleaner turnsLeft cleaner@(Cleaner _ _ _ ph _) =
cleaner' <- doAction action cleaner cleaner' <- doAction action cleaner
return cleaner' return cleaner'
case clState cleaner'' of case cleaner''^.state of
Off -> return cleaner'' Off -> return cleaner''
On -> runCleaner (turnsLeft - 1) cleaner'' On -> runCleaner (turnsLeft - 1) cleaner''
simulateOnGrid :: Int -> Grid -> StdGen -> (Cleaner, Grid) simulateOnGrid :: Int -> Grid -> StdGen -> (Cleaner, Grid)
simulateOnGrid maxTurns grid gen = simulateOnGrid maxTurns grid gen =
evalState (runStateT (runCleaner maxTurns cleaner) grid) gen evalState (runStateT (runCleaner maxTurns cleaner) grid) gen
where cleaner = createCleaner (fromJust $ cell (0,0) grid) East where cleaner = createCleaner (fromJust $ lookupCell (0,0) grid) East

View File

@ -1,10 +1,14 @@
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts, TemplateHaskell #-}
module AI.Vacuum where module AI.Vacuum where
import qualified Data.Map as M import qualified Data.Map as M
import Control.Monad.State import Control.Monad.State
import Prelude hiding (id, (.))
import Control.Category
import Data.Maybe (isJust, isNothing, fromJust) import Data.Maybe (isJust, isNothing, fromJust)
import Data.Lens.Common
import Data.Lens.Template
import System.IO.Unsafe (unsafePerformIO) import System.IO.Unsafe (unsafePerformIO)
import Debug.Trace (putTraceMsg) import Debug.Trace (putTraceMsg)
@ -21,20 +25,24 @@ data Action = GoForward | TurnRight | TurnLeft | SuckDirt | TurnOff deriving (Eq
data CellType = Empty | Furniture | Dirt | Home deriving (Eq, Show) data CellType = Empty | Furniture | Dirt | Home deriving (Eq, Show)
type Point = (Int, Int) type Point = (Int, Int)
data Cell = Cell Point CellType deriving (Eq, Show) data Cell = Cell { _point :: Point, _cellType :: CellType } deriving (Eq, Show)
type Grid = M.Map Point Cell type Grid = M.Map Point Cell
data CleanerState = On | Off deriving (Eq, Show) data CleanerState = On | Off deriving (Eq, Show)
type Score = Int type Score = Int
data Cleaner = data Cleaner =
Cleaner { Cleaner {
clState :: CleanerState, _state :: CleanerState,
clCell :: Cell, _cell :: Cell,
clDir :: Direction, _direction :: Direction,
clPrcptsHist :: PerceptsHistory, _path :: [Point],
clScore :: Score _perceptsHist :: PerceptsHistory,
_actionHist :: [Action],
_score :: Score
} deriving (Show) } deriving (Show)
makeLenses [''Cell, ''Cleaner]
class (Enum a, Eq a, Bounded a) => WrappedBoundedEnum a where class (Enum a, Eq a, Bounded a) => WrappedBoundedEnum a where
next :: a -> a next :: a -> a
prev :: a -> a prev :: a -> a
@ -68,60 +76,68 @@ leftPoint (x, y) East = (x, y - 1)
leftPoint (x, y) South = (x + 1, y) leftPoint (x, y) South = (x + 1, y)
leftPoint (x, y) West = (x, y + 1) leftPoint (x, y) West = (x, y + 1)
cell :: Point -> Grid -> Maybe Cell lookupCell :: Point -> Grid -> Maybe Cell
cell = M.lookup lookupCell = M.lookup
forwardCell :: Cell -> Direction -> Grid -> Maybe Cell forwardCell :: Cell -> Direction -> Grid -> Maybe Cell
forwardCell (Cell point _) dir grid = cell (forwardPoint point dir) grid forwardCell (Cell point _) = lookupCell . (forwardPoint point)
rightCell :: Cell -> Direction -> Grid -> Maybe Cell rightCell :: Cell -> Direction -> Grid -> Maybe Cell
rightCell (Cell point _) dir grid = cell (rightPoint point dir) grid rightCell (Cell point _) = lookupCell . (rightPoint point)
leftCell :: Cell -> Direction -> Grid -> Maybe Cell leftCell :: Cell -> Direction -> Grid -> Maybe Cell
leftCell (Cell point _) dir grid = cell (leftPoint point dir) grid leftCell (Cell point _) = lookupCell . (leftPoint point)
gridFromCellList :: [Cell] -> Grid gridFromCellList :: [Cell] -> Grid
gridFromCellList = foldl (\m cell@(Cell p _) -> M.insert p cell m) M.empty gridFromCellList = foldl (\m cell@(Cell p _) -> M.insert p cell m) M.empty
createCleaner :: Cell -> Direction -> Cleaner createCleaner :: Cell -> Direction -> Cleaner
createCleaner cell dir = Cleaner On cell dir [] 0 createCleaner cell dir = Cleaner On cell dir [cell^.point] [] [] 0
setPercepts percepts = perceptsHist ^%= (percepts :)
turnRight :: (MonadState Grid m) => Cleaner -> m Cleaner turnRight :: (MonadState Grid m) => Cleaner -> m Cleaner
turnRight (Cleaner state cell dir ph score) = turnRight = return . (direction ^%= right) . (setPercepts [])
return $ Cleaner state cell (right dir) ([] : ph) score
turnLeft :: (MonadState Grid m) => Cleaner -> m Cleaner turnLeft :: (MonadState Grid m) => Cleaner -> m Cleaner
turnLeft (Cleaner state cell dir ph score) = turnLeft = return . (direction ^%= left) . (setPercepts [])
return $ Cleaner state cell (left dir) ([] : ph) score
moveForward :: (MonadState Grid m) => Cleaner -> m Cleaner moveForward :: (MonadState Grid m) => Cleaner -> m Cleaner
moveForward cleaner@(Cleaner state cell@(Cell _ cellType) dir ph score) = do moveForward cleaner = do
grid <- get grid <- get
return $ return .
case forwardCell cell dir grid of case forwardCell (cleaner^.cell) (cleaner^.direction) grid of
Nothing -> Cleaner state cell dir ([TouchSensor] : ph) score Nothing -> setPercepts [TouchSensor]
Just nextCell@(Cell _ nextCellType) -> Just nextCell@(Cell nextPoint nextCellType) ->
let setNextCellPoint = (cell ^= nextCell) . (path ^%= (nextPoint :)) in
case nextCellType of case nextCellType of
Empty -> Cleaner state nextCell dir ([] : ph) score Empty -> setNextCellPoint . (setPercepts [])
Furniture -> Cleaner state cell dir ([TouchSensor] : ph) score Furniture -> setPercepts [TouchSensor]
Dirt -> Cleaner state nextCell dir ([PhotoSensor] : ph) score Dirt -> setNextCellPoint . (setPercepts [PhotoSensor])
Home -> Cleaner state nextCell dir ([InfraredSensor] : ph) score Home -> setNextCellPoint . (setPercepts [InfraredSensor])
$ cleaner
suckDirt :: (MonadState Grid m) => Cleaner -> m Cleaner
suckDirt cleaner = do
let point' = (cleaner^.cell)^.point
grid <- get
put $ M.insert point' (Cell point' Empty) grid
return cleaner
doAction :: (MonadState Grid m) => Action -> Cleaner -> m Cleaner doAction :: (MonadState Grid m) => Action -> Cleaner -> m Cleaner
doAction action cleaner@(Cleaner state cell@(Cell point cellType) dir ph score) = doAction action cleaner = do
case action of case action of
GoForward -> moveForward $ Cleaner state cell dir ph (score - 1) GoForward -> moveForward
TurnRight -> turnRight $ Cleaner state cell dir ph (score - 1) TurnRight -> turnRight
TurnLeft -> turnLeft $ Cleaner state cell dir ph (score - 1) TurnLeft -> turnLeft
SuckDirt -> SuckDirt ->
case cellType of (if cellType' == Dirt then suckDirt . (score ^%= (+ 100)) else return)
Dirt -> do . (setPercepts [])
grid <- get
put $ M.insert point (Cell point Empty) grid
return $ Cleaner state cell dir ([] : ph) (score + 99)
otherwise -> return $ Cleaner state cell dir ([] : ph) (score - 1)
TurnOff -> TurnOff ->
case cellType of return . (state ^= Off) . (setPercepts [])
Home -> return $ Cleaner Off cell dir ([] : ph) score . (if cellType' == Home then id else score ^%= (subtract 1000))
otherwise -> return $ Cleaner Off cell dir ([] : ph) (score - 1000) . (score ^%= subtract 1)
. (actionHist ^%= (action :))
$ cleaner
where
cellType' = (cleaner^.cell)^.cellType