parent
5d4e23f5e5
commit
643a75deb0
@ -1,2 +1,3 @@ |
||||
build |
||||
ReflexAgent |
||||
StatefulReflexAgent |
||||
|
@ -0,0 +1,242 @@ |
||||
{-# LANGUAGE FlexibleContexts #-} |
||||
|
||||
module AI.Vacuum.StatefulReflexAgent where |
||||
|
||||
import AI.Vacuum.Grid |
||||
import AI.Vacuum.Cleaner hiding (doAction) |
||||
import qualified AI.Vacuum.Cleaner (doAction) |
||||
import AI.Vacuum.RandomGrid |
||||
import qualified Data.Map as M |
||||
import qualified Data.List as L |
||||
import Data.Maybe (fromMaybe, fromJust) |
||||
import Data.Lens.Common |
||||
import Data.Ix (range) |
||||
import Control.Monad.State |
||||
import System.Random |
||||
import System (getArgs) |
||||
|
||||
data PointState = Unreachable | Explored | Unexplored deriving (Eq, Ord, Show) |
||||
type GridState = M.Map Point PointState |
||||
|
||||
updateGridState :: Point -> PointState -> GridState -> GridState |
||||
updateGridState point pointState gridState = |
||||
let gridState' = M.insert point pointState gridState in |
||||
case point of |
||||
(0, 0) -> |
||||
foldl (\m p -> M.insert p Unreachable m) gridState' [(-1, 0), (0, -1), (-1, -1)] |
||||
(0, y) -> M.insert (-1, y) Unreachable gridState' |
||||
(x, 0) -> M.insert (x, -1) Unreachable gridState' |
||||
_ -> gridState' |
||||
|
||||
createGridState :: Cleaner -> GridState |
||||
createGridState cleaner = updateGridState ((cleaner^.cell)^.point) Explored M.empty |
||||
|
||||
getPointState point = fromMaybe Unexplored . M.lookup point |
||||
|
||||
getCellState :: (Point -> Direction -> Point) |
||||
-> Cell -> Direction -> GridState -> PointState |
||||
getCellState pointFn cell direction = getPointState (pointFn (cell^.point) direction) |
||||
|
||||
leftCellState :: Cell -> Direction -> GridState -> PointState |
||||
leftCellState = getCellState leftPoint |
||||
|
||||
rightCellState :: Cell -> Direction -> GridState -> PointState |
||||
rightCellState = getCellState rightPoint |
||||
|
||||
forwardCellState :: Cell -> Direction -> GridState -> PointState |
||||
forwardCellState = getCellState forwardPoint |
||||
|
||||
backwardCellState :: Cell -> Direction -> GridState -> PointState |
||||
backwardCellState = getCellState backwardPoint |
||||
|
||||
doAction :: (MonadState Grid m) => Action -> Cleaner -> GridState -> m (Cleaner, GridState) |
||||
doAction action cleaner gridState = do |
||||
cleaner' <- AI.Vacuum.Cleaner.doAction action cleaner |
||||
let gridState' = |
||||
if action == GoForward |
||||
then if TouchSensor `elem` (head (cleaner'^.perceptsHist)) |
||||
then updateGridState (nextPoint cleaner') Unreachable gridState |
||||
else updateGridState ((cleaner'^.cell)^.point) Explored gridState |
||||
else gridState |
||||
return (cleaner', gridState') |
||||
where |
||||
nextPoint cl = forwardPoint ((cl^.cell)^.point) (cl^.direction) |
||||
|
||||
possibleManhattanPaths :: [Path] -> GridState -> [Path] |
||||
possibleManhattanPaths paths gridState |
||||
| paths == [] = [] |
||||
| otherwise = |
||||
filter (L.all (== Explored) |
||||
. map (\p -> getPointState p gridState) |
||||
. filter (\p -> p `notElem` [p1, p2])) |
||||
paths |
||||
where |
||||
p1 = head . head $ paths |
||||
p2 = last . head $ paths |
||||
|
||||
nearestUnexploredPoint :: Point -> Int -> GridState -> Maybe (Point, Path) |
||||
nearestUnexploredPoint point maxDist gridState = do |
||||
np <- L.find (\(p,ps) -> ps == Unexplored && pathExists p) |
||||
$ map (\p -> (p, getPointState p gridState)) |
||||
$ concatMap (borderingPoints point) [1..maxDist] |
||||
return ((fst np), (head . paths . fst $ np)) |
||||
where |
||||
paths p = possibleManhattanPaths (manhattanPaths point p) gridState |
||||
pathExists p = (/= 0) . length . paths $ p |
||||
|
||||
actionsByRelDirection = [ |
||||
("f", [GoForward]), |
||||
("l", [TurnLeft, GoForward]), |
||||
("r", [TurnRight, GoForward]), |
||||
("b", [TurnLeft, TurnLeft, GoForward])] |
||||
|
||||
relDirectionToDirection relDir dir = |
||||
case relDir of |
||||
"f" -> dir |
||||
"l" -> left dir |
||||
"r" -> right dir |
||||
"b" -> right . right $ dir |
||||
|
||||
moveActions :: (Point, Point) -> Direction -> ([Action], Direction) |
||||
moveActions (p1, p2) dir = |
||||
(\rd -> |
||||
(fromJust . lookup rd $ actionsByRelDirection, |
||||
relDirectionToDirection rd dir)) |
||||
$ fst |
||||
$ fromJust |
||||
$ L.find (\(d, p) -> p == p2) |
||||
$ zip ["f", "l", "r", "b"] |
||||
$ map (\pfn -> pfn p1 dir) |
||||
$ [forwardPoint, leftPoint, rightPoint, backwardPoint] |
||||
|
||||
pathActions :: Direction -> Path -> [Action] |
||||
pathActions dir path = |
||||
fst |
||||
. foldl |
||||
(\(as, d) ps -> let (as', d') = moveActions ps d in (as ++ as', d')) |
||||
([], dir) |
||||
. pairs |
||||
$ path |
||||
|
||||
chooseActions :: Cleaner -> GridState -> RandomState [Action] |
||||
chooseActions cleaner gridState = |
||||
case cleaner^.perceptsHist of |
||||
[] -> return [GoForward] |
||||
(ps : _) | PhotoSensor `elem` ps -> return [SuckDirt] |
||||
(ps : _) | InfraredSensor `elem` ps -> return [TurnOff] |
||||
_ -> |
||||
if length unexplored == 0 |
||||
then -- trace ("surrounded at " ++ show ((cleaner^.cell)^.point)) $ |
||||
case nearestUnexploredPoint ((cleaner^.cell)^.point) 4 gridState of |
||||
Nothing -> do |
||||
r <- getRandomR ((0.0, 1.0) :: (Float, Float)) |
||||
-- trace ("choosing on random: " ++ show r) $ |
||||
case r of |
||||
r | r < 0.1 -> return [TurnRight] |
||||
r | r < 0.2 -> return [TurnLeft] |
||||
otherwise -> return [GoForward] |
||||
Just (nPoint, path) -> -- trace ("taking path: " ++ show path) $ |
||||
return . pathActions (cleaner^.direction) $ path |
||||
else |
||||
return . fromJust . lookup (fst . head $ unexplored) $ actionsByRelDirection |
||||
where |
||||
gridStates = |
||||
zip ["f", "l", "r", "b"] $ |
||||
map (\f -> f (cleaner^.cell) (cleaner^.direction) gridState) |
||||
[forwardCellState, leftCellState, rightCellState, backwardCellState] |
||||
unexplored = filter ((== Unexplored) . snd) gridStates |
||||
|
||||
runCleaner :: Int -> Cleaner -> GridState -> StateT Grid RandomState (Cleaner, GridState) |
||||
runCleaner turnsLeft cleaner gridState = |
||||
if turnsLeft == 1 |
||||
then do |
||||
(cleaner', gridState') <- doAction TurnOff cleaner gridState |
||||
return (cleaner', gridState') |
||||
else do |
||||
actions <- lift $ chooseActions cleaner gridState |
||||
(cleaner', gridState') <- |
||||
foldM |
||||
(\(cl, gs) a -> do |
||||
(cl', gs') <- doAction a cl gs |
||||
return (cl', gs')) |
||||
(cleaner, gridState) |
||||
actions |
||||
|
||||
case cleaner'^.state of |
||||
Off -> return (cleaner', gridState') |
||||
On -> runCleaner (turnsLeft - (length actions)) cleaner' gridState' |
||||
|
||||
simulateOnGrid :: Int -> Grid -> StdGen -> (Cleaner, GridState, Grid) |
||||
simulateOnGrid maxTurns grid gen = |
||||
let ((cleaner', gridState'), grid') = |
||||
evalState (runStateT (runCleaner maxTurns cleaner gridState) grid) gen in |
||||
(cleaner', gridState', grid') |
||||
where |
||||
cleaner = createCleaner (fromJust $ lookupCell (0,0) grid) East |
||||
gridState = createGridState cleaner |
||||
|
||||
printGridState :: GridState -> Grid -> IO () |
||||
printGridState gridState grid = do |
||||
let width = gridWidth grid |
||||
let height = gridHeight grid |
||||
|
||||
forM_ (range (-1, height)) $ \y -> do |
||||
forM_ (range (-1, width)) $ \x -> do |
||||
case lookupCell (x,y) $ grid of |
||||
Nothing -> |
||||
case M.lookup (x,y) gridState of |
||||
Nothing -> putStr "! " |
||||
Just Unreachable -> putStr "/ " |
||||
Just cell -> |
||||
case M.lookup (cell^.point) gridState of |
||||
Nothing -> putStr "! " |
||||
Just pointState -> |
||||
case pointState of |
||||
Explored -> putStr "- " |
||||
Unexplored -> putStr "! " |
||||
Unreachable -> putStr "/ " |
||||
putStrLn "" |
||||
|
||||
printSimulation :: Int -> Int -> Int -> Float -> Float -> Bool -> IO () |
||||
printSimulation |
||||
minSize maxSize maxTurns dirtProb furnitureProb toPrintGrid = do |
||||
gen <- newStdGen |
||||
let grid = evalState |
||||
(makeRandomGrid (minSize,maxSize) (minSize,maxSize) dirtProb furnitureProb) |
||||
gen |
||||
|
||||
when toPrintGrid $ do |
||||
putStrLn "Grid before traversal" |
||||
printGrid grid |
||||
putStrLn "" |
||||
|
||||
let (cleaner, gridState', grid') = simulateOnGrid maxTurns grid gen |
||||
|
||||
when toPrintGrid $ do |
||||
putStrLn "Grid after traversal" |
||||
printPath cleaner grid' |
||||
putStrLn "" |
||||
|
||||
when toPrintGrid $ do |
||||
putStrLn "Grid state" |
||||
printGridState gridState' grid |
||||
putStrLn "" |
||||
|
||||
printRunStats cleaner grid |
||||
putStrLn ("Grid Exploration stats = " |
||||
++ (show . freqMap $ |
||||
[fromMaybe Unexplored . M.lookup (x, y) $ gridState' |
||||
| x <- range (0, gridWidth grid - 1), |
||||
y <- range(0, gridHeight grid - 1)])) |
||||
|
||||
main :: IO () |
||||
main = do |
||||
args <- getArgs |
||||
let minSize = (read $ args !! 0) :: Int |
||||
let maxSize = (read $ args !! 1) :: Int |
||||
let dirtProb = (read $ args !! 2) :: Float |
||||
let furnitureProb = (read $ args !! 3) :: Float |
||||
let maxTurns = (read $ args !! 4) :: Int |
||||
let toPrintGrid = (read $ args !! 5) :: Bool |
||||
|
||||
printSimulation minSize maxSize maxTurns dirtProb furnitureProb toPrintGrid |
@ -0,0 +1 @@ |
||||
ghc -O2 -o StatefulReflexAgent --make -hidir build -odir build -main-is AI.Vacuum.StatefulReflexAgent AI/Vacuum/StatefulReflexAgent.hs AI/Vacuum/RandomGrid.hs AI/Vacuum/Cleaner.hs AI/Vacuum/Grid.hs |
Loading…
Reference in new issue