【问题标题】:Grid with obstacles coverage algorithm带有障碍物覆盖算法的网格
【发布时间】:2015-04-05 15:39:18
【问题描述】:

我必须找到一个算法让机器人代理执行以下操作(对不起,我真的不知道怎么称呼它):

  • 机器人位于有障碍物的 10x10 网格上(每个方块都是障碍物或可穿越)

  • 机器人有一个碰撞传感器:当机器人碰到障碍物时它会激活。

  • 在网格上有不断增长的胡萝卜。有快速增长的方块和缓慢增长的方块。

  • 每走一步,机器人可以:前进或向右或向左转动 90° 或原地不动

  • 事先不知道胡萝卜和障碍物的位置

  • 机器人在移动时胡萝卜继续生长(即使在收获之后)

  • 胡萝卜生长在大多数不是障碍物的方格中

  • 机器人不知道方块是快还是慢

  • 在每个方格中可以有 0 到 20 个胡萝卜。在每个时间实例中,一个正方形的胡萝卜数量增加的概率为 p = 0.01(或快速增长的正方形为 p = 0.02)

  • 您可以测量收获的胡萝卜数量。

目标是在 2000 步内获得最大数量的胡萝卜。

会有一种懒惰/简单的方法吗?

到目前为止,我有点迷茫,因为这不是一个解决迷宫的问题。它会是一种洪水填充算法吗?有没有更简单的?

我不一定要寻找“解决”问题,而是尽可能简单地近似

【问题讨论】:

  • (1) 是否事先知道胡萝卜的位置? (2) 是否事先知道障碍? (3) 即使有这些假设,这个问题仍然很困难——因为它很容易从哈密顿路径/旅行推销员问题中减少。如果没有这些假设,将更难获得最佳解决方案。
  • (1) 不知道胡萝卜的位置 (2) 障碍物也不知道
  • 已经收获的胡萝卜斑点会怎样?它们会在一段时间后重新生长吗?
  • 是的,它们会随着时间的推移继续增长
  • 机器人必须前进还是也可以原地不动?

标签: algorithm artificial-intelligence grid-layout maze


【解决方案1】:

鉴于它不知道食物来源的位置和数量,找到具有完美策略的机器人实现确实是一项工作。

机器人的任何给定策略可能不会在每次运行中产生最大可能收获。所以问题是,在多次模拟运行中哪种策略最成功。

要为给定的正方形类型(P(fastFood)、P(slowFood)、P(obstacle))的统计分布找到合适的策略,可能会想出以下想法:

让 Bot(npatch) 成为一个寻找 npatch 食物点的机器人。策略是在搜索第二个食物块之前吃掉它在第一个食物块中找到的东西,依此类推。当它访问 npatch 食物来源(或没有找到更多食物补丁)时,它会返回找到的第一个食物来源并重新收获。

此类机器人 (Bot(npatch)) 现在可以在统计相关的模拟运行次数中相互竞争。最佳机器人是比赛的获胜者。

这种方法可以被认为是受到遗传算法的启发,但没有混合任何基因,而是简单地迭代所有基因(1..npatch)。也许有人知道如何将这个想法变成一个完全遗传的算法。这可能涉及转向 Bot(npatch,searchStrategy),然后使用多个基因来应用遗传算法。

每当模拟的参数发生变化时,比赛就必须重复,显然取决于世界上食物块的数量,如果一些食物块被替换,那么去寻找另一个食物块可能会也可能不会有回报已经知道了。

下面的代码是用 F# 编写的,是该问题的模拟器(如果我的所有要求都正确,那就是……)。编写一个新的机器人就像编写一个函数一样简单,然后将其传递给模拟器。

对于那些想尝试自己的机器人的人来说,可以考虑这是我的复活节彩蛋。

我写的 2 个机器人叫做“marvinRobot”,它会做 Marvin 会做的事情,而“lazyRobot”是一个机器人,它会在它找到的第一个食物来源上扎营。

type Square =
    | Empty
    | Obstacle
    | Food of float * (float -> float) // available * growth
    | Unknown

let rnd = new System.Random()
let grow p a =
    let r = rnd.NextDouble()
    if r < p then a + 1.0
    else a

let slowGrowth a = grow 0.01 a
let fastGrowth a = grow 0.02 a

let eatPerTick = 1.0 
let maxFoodPerSquare = 20.0

let randomPick values =
    let count = List.length values
    let r = rnd.Next(0,count-1)
    values.Item(r)

type World = Square[,]

let randomSquare pobstacle pfood =
    let r = rnd.NextDouble()
    match r with
    | x1 when x1 < pobstacle -> Obstacle
    | x2 when x2 < (pobstacle + pfood) && x2 >= pobstacle -> 
        Food(rnd.NextDouble() * maxFoodPerSquare, randomPick [slowGrowth; fastGrowth])
    | _ -> Empty

let createRandomWorld n pobstacle pfood = 
    Array2D.init n n (fun col row -> randomSquare pobstacle pfood)

let createUnknownWorld n =
    Array2D.create n n Unknown

type Position = { Column : int; Row : int }

type RoboState = { Memory : Square[,]; Pos : Position; Heading : Position }
type RoboAction = 
    | TurnRight
    | TurnLeft
    | MoveOne
    | Eat
    | Idle

type RoboActor = World -> RoboState -> RoboAction

let right heading : Position =
    match heading with
    | { Column = 0; Row = 1 } -> { Column = -1; Row = 0 }
    | { Column = -1; Row = 0 } -> { Column = 0; Row = -1 }
    | { Column = 0; Row = -1 } -> { Column = 1; Row = 0 }
    | { Column = 1; Row = 0 } -> { Column = 0; Row = 1 }
    | _ -> failwith "Invalid heading!"

let left heading : Position =
    match heading with
    | { Column = -1; Row = 0 } -> { Column = 0; Row = 1 }
    | { Column = 0; Row = -1 } -> { Column = -1; Row = 0 }
    | { Column = 1; Row = 0 } -> { Column = 0; Row = -1 }
    | { Column = 0; Row = 1 } -> { Column = 1; Row = 0 } 
    | _ -> failwith "Invalid heading!"

let checkAccess n position =
    let inRange v = v >= 0 && v < n
    (inRange position.Column) && (inRange position.Row)

let tickWorld world =
    world 
    |> Array2D.map 
        (fun sq -> 
            match sq with 
            | Empty -> Empty 
            | Obstacle -> Obstacle 
            | Food(a,r) -> Food(min (r a) maxFoodPerSquare, r)
            | Unknown -> Unknown
        )

let rec step robot world roboState i imax acc = 
    if i < imax then
        let action = robot world roboState
        match action with
        | TurnRight ->
            let rs1 = { roboState with Heading = right roboState.Heading }
            let wrld1 = tickWorld world
            step robot wrld1 rs1 (i+1) imax acc
        | TurnLeft ->
            let rs1 = { roboState with Heading = left roboState.Heading }
            let wrld1 = tickWorld world
            step robot wrld1 rs1 (i+1) imax acc
        | MoveOne ->
            let rs1 =
                let c = 
                    { Column = roboState.Pos.Column + roboState.Heading.Column 
                      Row = roboState.Pos.Row + roboState.Heading.Row
                    }
                if checkAccess (Array2D.length1 world) c 
                then 
                    match world.[c.Column,c.Row] with
                    | Obstacle -> 
                        roboState.Memory.[c.Column,c.Row] <- Obstacle
                        roboState
                    | _ -> { roboState with Pos = c }
                else
                    roboState
            let wrld1 = tickWorld world
            step robot wrld1 rs1 (i+1) imax acc
        | Eat -> 
            let eat,acc1 = 
                match world.[roboState.Pos.Column,roboState.Pos.Row] with
                | Empty -> Empty,acc
                | Obstacle -> Obstacle,acc
                | Food(a,r) -> 
                    let eaten = if a >= eatPerTick then eatPerTick else 0.0
                    printfn "eating %f carrots" eaten
                    Food(a - eaten, r),eaten + acc
                | Unknown -> Unknown,acc
            world.[roboState.Pos.Column,roboState.Pos.Row] <- eat
            let wrld1 = tickWorld world
            step robot wrld1 roboState (i+1) imax acc1
        | Idle ->
            step robot (tickWorld world) roboState (i+1) imax acc
    else
        acc

let initRoboState n = 
    {   Memory = createUnknownWorld n; 
        Pos = { Column = 0; Row = 0;}; 
        Heading = {Column = 1; Row = 0}
    }

let simulate n pobstacle pfood imax robot =
    let w0 = createRandomWorld n pobstacle pfood
    let r0 = initRoboState n
    printfn "World: %A" w0
    printfn "Initial Robo State: %A" r0
    let result = step robot w0 r0 0 imax 0.0
    printfn "Final Robo State: %A" r0
    result

// Not that Marvin would care, but the rule for this simulator is that the 
// bot may only inspect the square in the world at the current position.
// This means, IT CANNOT SEE the neighboring squares.
// This means, that if there is a obstacle next to current square, 
// it costs a simulation tick to find out, trying to bump against it.
// Any access to other squares in world is considered cheating!
// world is passed in spite of all said above to allow for alternate rules.
let marvinRobot world roboState =
    Idle

// Tries to find a square with food, then stays there, eating when there is something to eat.
let lazyRobot (world : World) (roboState : RoboState) =
    let search() =
        let status action : RoboAction =
            match action with
            | TurnLeft -> printfn "%A TurnLeft at %A (heading: %A)" world.[roboState.Pos.Column,roboState.Pos.Row] roboState.Pos roboState.Heading
            | TurnRight -> printfn "%ATurnRight at %A (heading: %A)" world.[roboState.Pos.Column,roboState.Pos.Row]  roboState.Pos roboState.Heading
            | MoveOne -> printfn "%A MoveOne at %A (heading: %A)" world.[roboState.Pos.Column,roboState.Pos.Row] roboState.Pos roboState.Heading
            | Idle -> printfn "%A Idle at %A (heading: %A)" world.[roboState.Pos.Column,roboState.Pos.Row] roboState.Pos roboState.Heading
            | Eat -> printfn "%A Eat at %A (heading: %A)" world.[roboState.Pos.Column,roboState.Pos.Row] roboState.Pos roboState.Heading
            action
        let neighbors = 
            [ roboState.Heading, MoveOne;
              (roboState.Heading |> right),TurnRight;
              (roboState.Heading |> left),TurnLeft;
              (roboState.Heading |> right |> right),TurnRight
            ]
            |> List.map (fun (p,a) -> (p.Column,p.Row),a)
            |> List.map (fun ((c,r),a) -> (roboState.Pos.Column + c,roboState.Pos.Row + r),a)
            |> List.filter (fun ((c,r),a) -> checkAccess (Array2D.length1 world){Position.Column = c; Row = r})
            |> List.sortBy (fun ((c,r),a) -> match roboState.Memory.[c,r] with | Food(_,_) -> 0 | Unknown -> 1 | Empty -> 2 | Obstacle -> 3)
            |> List.map (fun ((c,r),a) -> { Column = c; Row = r},a)
        if neighbors.IsEmpty then failwith "It's a trap!" // can happen if bot is surrounded by obstacles, e.g.  in a corner
        else
            let p,a = neighbors.Head
            status a
    roboState.Memory.[roboState.Pos.Column, roboState.Pos.Row] <- 
            world.[roboState.Pos.Column,roboState.Pos.Row]
    match world.[roboState.Pos.Column,roboState.Pos.Row] with
    | Food(a,_) -> 
        printfn "Found food at %A" roboState.Pos
        Eat
    | _ -> 
        search()

//simulate 10 0.1 0.05 2000 marvinRobot
simulate 10 0.1 0.1 2000 lazyRobot

最后一个提示:如果您使用 0.0 个食物块进行模拟,您的机器人应该已经访问了地图上的所有方格。如果它不能做到这一点,它肯定不是一个好的机器人;)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-24
    相关资源
    最近更新 更多