robot
To the book page
代码解释由AI生成。
总代码
数据结构和图的构建
首先定义了村庄中各个地点之间的道路:
1 2 3 4 5 6 7 8 9
| const roads = [ "Alice's House-Bob's House", "Alice's House-Cabin", "Alice's House-Post Office", "Bob's House-Town Hall", "Daria's House-Ernie's House", "Daria's House-Town Hall", "Ernie's House-Grete's House", "Grete's House-Farm", "Grete's House-Shop", "Marketplace-Farm", "Marketplace-Post Office", "Marketplace-Shop", "Marketplace-Town Hall", "Shop-Town Hall" ];
|
接着,定义 buildGraph
函数来构建图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function buildGraph(edges) { let graph = Object.create(null); function addEdge(from, to) { if (from in graph) { graph[from].push(to); } else { graph[from] = [to]; } } for (let [from, to] of edges.map(r => r.split("-"))) { addEdge(from, to); addEdge(to, from); } return graph; } const roadGraph = buildGraph(roads);
|
村庄状态类
VillageState
类表示村庄的状态,包括机器人的位置和包裹的位置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class VillageState { constructor(place, parcels) { this.place = place; this.parcels = parcels; } move(destination) { if (!roadGraph[this.place].includes(destination)) { return this; } else { let parcels = this.parcels.map(p => { if (p.place != this.place) return p; return {place: destination, address: p.address}; }).filter(p => p.place != p.address); return new VillageState(destination, parcels); } } }
|
运行机器人
runRobot
函数模拟机器人运行:
1 2 3 4 5 6 7 8 9 10 11
| function runRobot(state, robot, memory) { for (var turn = 0;; turn++) { if (state.parcels.length == 0) { break; } let action = robot(state, memory); state = state.move(action.direction); memory = action.memory; } return turn; }
|
随机机器人
randomRobot
随机选择一个方向移动:
1 2 3 4 5 6 7 8
| function randomPick(array) { let choice = Math.floor(Math.random() * array.length); return array[choice]; } function randomRobot(state) { return {direction: randomPick(roadGraph[state.place])}; }
|
预定义路径机器人
routeRobot
按照预定义的路径移动:
1 2 3 4 5 6 7 8 9 10 11 12 13
| const mailRoute = [ "Alice's House", "Cabin", "Alice's House", "Bob's House", "Town Hall", "Daria's House", "Ernie's House", "Grete's House", "Shop", "Grete's House", "Farm", "Marketplace", "Post Office" ];
function routeRobot(state, memory) { if (memory.length == 0) { memory = mailRoute; } return {direction: memory[0], memory: memory.slice(1)}; }
|
目标导向机器人
goalOrientedRobot
根据包裹的位置和目标地址找到最佳路径:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function findRoute(graph, from, to) { let work = [{at: from, route: []}]; for (let i = 0; i < work.length; i++) { let {at, route} = work[i]; for (let place of graph[at]) { if (place == to) return route.concat(place); if (!work.some(w => w.at == place)) { work.push({at: place, route: route.concat(place)}); } } } }
function goalOrientedRobot({place, parcels}, route) { if (route.length == 0) { let parcel = parcels[0]; if (parcel.place != place) { route = findRoute(roadGraph, place, parcel.place); } else { route = findRoute(roadGraph, place, parcel.address); } } return {direction: route[0], memory: route.slice(1)}; }
|
效率机器人
efficientRobot
根据所有包裹找到最短路径,以提高效率:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function efficientRobot({place, parcels}, route) { if (route.length == 0) { let routes = []; for (let parcel of parcels) { if (parcel.place != place) { routes.push(findRoute(roadGraph, place, parcel.place)); } else { routes.push(findRoute(roadGraph, place, parcel.address)); } } route = routes.reduce((shortest, current) => shortest.length < current.length ? shortest : current ); } return {direction: route[0], memory: route.slice(1)}; }
|
机器人比较
compareRobots
用于比较两个机器人的性能:
1 2 3 4 5 6 7 8 9 10 11
| function compareRobots(robot1, memory1, robot2, memory2) { let totalTurns1 = 0; let totalTurns2 = 0; for (let i = 0; i < 100; i++) { let state = VillageState.random(); totalTurns1 += runRobot(state, robot1, memory1); totalTurns2 += runRobot(state, robot2, memory2); } console.log(`Robot 1 average turns: ${totalTurns1 / 100}`); console.log(`Robot 2 average turns: ${totalTurns2 / 100}`); }
|
最后,比较目标导向机器人和效率机器人的表现:
1
| compareRobots(goalOrientedRobot, [], efficientRobot, []);
|
这段代码会运行两个机器人各100次,比较它们完成任务所需的平均回合数。
Exercises
Measuring a robot
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
| function runRobotCount(state, robot, memory) { for (let turn = 0;; turn++) { if (state.parcels.length == 0) { return turn; } let action = robot(state, memory); state = state.move(action.direction); memory = action.memory; } }
function compareRobots(robot1, memory1, robot2, memory2) { let totalTurns1 = 0, totalTurns2 = 0; let tasks = 100;
for (let i = 0; i < tasks; i++) { let state = VillageState.random(); totalTurns1 += runRobotCount(state, robot1, memory1); totalTurns2 += runRobotCount(state, robot2, memory2); }
console.log(`Robot 1 averaged ${totalTurns1 / tasks} turns per task`); console.log(`Robot 2 averaged ${totalTurns2 / tasks} turns per task`); }
compareRobots(routeRobot, [], goalOrientedRobot, []);
|
Robot efficiency
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
| function findShortestRoute(graph, from, goals) { let routes = goals.map(goal => findRoute(graph, from, goal)); return routes.reduce((shortest, current) => shortest.length < current.length ? shortest : current ); }
function efficientRobot({place, parcels}, route) { if (route.length == 0) { let routes = []; for (let parcel of parcels) { if (parcel.place != place) { routes.push(findRoute(roadGraph, place, parcel.place)); } else { routes.push(findRoute(roadGraph, place, parcel.address)); } } route = routes.reduce((shortest, current) => shortest.length < current.length ? shortest : current ); } return {direction: route[0], memory: route.slice(1)}; }
runRobotAnimation(VillageState.random(), efficientRobot, []);
|
Persistent group
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
| class PGroup { constructor(members) { this.members = members; }
add(value) { if (this.has(value)) return this; return new PGroup(this.members.concat(value)); }
delete(value) { if (!this.has(value)) return this; return new PGroup(this.members.filter(m => m !== value)); }
has(value) { return this.members.includes(value); }
static empty = new PGroup([]); }
let a = PGroup.empty.add("a"); let ab = a.add("b"); let b = ab.delete("a");
console.log(b.has("b")); console.log(a.has("b")); console.log(b.has("a"));
|