mirror of
https://github.com/wassname/metacar.git
synced 2026-09-11 12:20:12 +08:00
137 lines
4.2 KiB
JavaScript
137 lines
4.2 KiB
JavaScript
|
|
class QTableAgent {
|
|
/*
|
|
Q learning agent
|
|
*/
|
|
|
|
constructor(env, pts) {
|
|
this.stateList = [];
|
|
this.pts = pts;
|
|
this.env = env;
|
|
this.Q = {};
|
|
this.m = 0;
|
|
}
|
|
|
|
save(){
|
|
let save_content = JSON.stringify(this.Q);
|
|
this.env.save(save_content, "qtable.json");
|
|
}
|
|
|
|
stringStateToState(state){
|
|
state = state.split(",");
|
|
var nState = [];
|
|
var lineCt = 0;
|
|
var line = [];
|
|
for (let l = 0; l < state.length; l++){
|
|
line.push(parseInt(state[l]));
|
|
if ((l+1) % this.pts == 0){
|
|
nState.push(line);
|
|
line = [];
|
|
}
|
|
}
|
|
return nState;
|
|
}
|
|
|
|
restore(content){
|
|
this.Q = {};
|
|
this.stateList = [];
|
|
for (const key in content){
|
|
var nStateToPush = this.stringStateToState(key);
|
|
var st = key.toString();
|
|
if (nStateToPush.length == this.pts)
|
|
this.stateList.push(nStateToPush);
|
|
this.Q[st] = [];
|
|
for (var i = 0; i < content[st].length; i++) {
|
|
if (content[st][i] != null){
|
|
this.Q[st].push(content[st][i]);
|
|
}
|
|
else{
|
|
this.Q[st].push(-Infinity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
play(){
|
|
// Get the current state
|
|
let state = this.env.getState().lidar;
|
|
state = state.toString();
|
|
// In this state in not in the Q(s, a) function
|
|
if (!(state in this.Q)){
|
|
let action_space = this.env.actionSpace();
|
|
action_space.range = [0, 1, 2]; // Simplification of the numbers of actions
|
|
this.Q[state] = Array.apply(null, Array(action_space.range.length)).map(Number.prototype.valueOf, 0.0);
|
|
}
|
|
// Select the max action a in Q
|
|
let action = argMax(this.Q[state]);
|
|
|
|
// Take this action and get the associated reward
|
|
let reward = this.env.step(action);
|
|
}
|
|
|
|
createStateIfNotExist(st){
|
|
if (!(st in this.Q)){
|
|
let action_space = this.env.actionSpace();
|
|
action_space.range = [0, 1, 3];
|
|
this.stateList.push(st);
|
|
this.Q[st] = Array.apply(null, Array(action_space.range.length)).map(Number.prototype.valueOf, 0);
|
|
}
|
|
}
|
|
|
|
getStateValues(state){
|
|
state = state.toString();
|
|
this.createStateIfNotExist(state);
|
|
return this.Q[state];
|
|
}
|
|
|
|
pickAction(st, eps){
|
|
this.createStateIfNotExist(st);
|
|
let act;
|
|
if (Math.random() < eps){ // Pick a random action
|
|
act = Math.floor(Math.random()*this.Q[st].length);
|
|
}
|
|
else{
|
|
act = argMax(this.Q[st]);
|
|
}
|
|
return act;
|
|
}
|
|
|
|
train(){
|
|
let episode = 2000;
|
|
let eps = 1.0;
|
|
let eps_decrease = 0.99;
|
|
|
|
let mean_reward = [];
|
|
for (let ep = 0; ep < episode; ep++) {
|
|
if (ep % 10 == 0){
|
|
eps = Math.max(0.05, eps*eps_decrease);
|
|
console.log("episode=", ep, "eps=", eps, "mean_reward", mean(mean_reward));
|
|
}
|
|
mean_reward = [];
|
|
let st = this.env.getState().lidar.toString();
|
|
let act;
|
|
let gamma = 0.99;
|
|
let st2;
|
|
let act2;
|
|
for (var t = 0; t < 800; t++) {
|
|
act = this.pickAction(st, eps);
|
|
let reward = this.env.step(act);
|
|
mean_reward.push(reward);
|
|
st2 = this.env.getState().lidar.toString();
|
|
// Pick greedy action (eps = 0)
|
|
act2 = this.pickAction(st2, 0.);
|
|
this.createStateIfNotExist(st2);
|
|
this.createStateIfNotExist(st);
|
|
this.Q[st][act] = this.Q[st][act] + 0.01*(reward + (gamma*this.Q[st2][act2]) - this.Q[st][act]);
|
|
st = st2;
|
|
}
|
|
this.env.shuffle({cars: false});
|
|
}
|
|
this.env.render(true);
|
|
for (let s=0; s < this.stateList.length; s++){
|
|
this.stateList[s] = this.stringStateToState(this.stateList[s]);
|
|
}
|
|
displayQTable("q_table", this.stateList, this, ["Top", "Left", "Right"]);
|
|
}
|
|
}
|