Documentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/cartpole/llms.txt
Use this file to discover all available pages before exploring further.
DQNAgent implements a Deep Q-Network in vanilla JavaScript without any machine-learning framework. It manages a three-layer fully-connected network, an epsilon-greedy exploration policy, and an experience-replay memory buffer. Weights are updated via a hand-rolled backpropagation pass after each batch of sampled transitions.
Constructor Hyperparameters
Every hyperparameter is set in the constructor and governs how the agent learns and explores.Discount factor for future rewards. Used in the Bellman target:
target = reward + gamma × max(Q(nextState)).Step size applied when updating network weights during
trainSample().Maximum number of transitions stored in the replay buffer. When the buffer is full, the oldest transition is evicted with
memory.shift().Number of transitions sampled from memory on each call to
experienceReplay().Initial epsilon value. At
1.0 the agent acts entirely at random, ensuring broad exploration at the start of training.Lower bound for epsilon.
explorationRate will never decay below this value.Multiplicative decay applied to
explorationRate at the end of each experienceReplay() call:
explorationRate = Math.max(explorationMin, explorationRate × explorationDecay).Network Architecture
The network is a three-layer dense (fully-connected) multilayer perceptron:matrix(). Biases are initialized to zero.
| Tensor | Shape | Initialization |
|---|---|---|
w1 | 4 × 24 | (Math.random() - 0.5) * 0.16 per element |
b1 | 24 | 0 |
w2 | 24 × 24 | (Math.random() - 0.5) * 0.16 per element |
b2 | 24 | 0 |
w3 | 24 × 2 | (Math.random() - 0.5) * 0.16 per element |
b3 | 2 | 0 |
0 (push left) and 1 (push right).
Methods
matrix(rows, cols)
Helper that allocates a 2-D array of shape rows × cols, with every element sampled from a small uniform distribution centered at zero.
0.16 keeps initial activations in a reasonable range given the state inputs are in roughly [-2.4, 2.4].
dense(input, weights, bias, relu)
Performs a forward pass through a single dense layer: computes the weighted sum plus bias for each output neuron, then optionally applies a ReLU activation.
Activation vector from the previous layer (or the raw state for layer 1).
2-D weight matrix of shape
[input.length][output.length]. Accessed as weights[i][j].Bias vector of length equal to the number of output neurons.
When
true, applies Math.max(0, sum) element-wise (ReLU). Pass false for a linear output layer.number[] — the output activation vector for this layer.
predict(state)
Runs a full forward pass through all three layers and returns the two Q-values.
4-element state vector
[x, xDot, theta, thetaDot].[Q(left), Q(right)] — a 2-element array of raw (linear) Q-value estimates.
act(state)
Selects an action using an epsilon-greedy policy.
4-element state vector
[x, xDot, theta, thetaDot].0 or 1.
| Path | Probability | Action chosen |
|---|---|---|
| Random exploration | explorationRate | 0 or 1 with equal probability |
| Greedy exploitation | 1 − explorationRate | argmax(predict(state)) — whichever Q-value is higher |
remember(state, action, reward, nextState, terminal)
Appends a single transition to the replay buffer and evicts the oldest entry if the buffer exceeds memorySize.
State before the action was taken. Stored as a shallow copy.
The action taken (
0 or 1).Reward received (
+1 or -1).State after the action. Stored as a shallow copy.
Whether this transition ended the episode.
trainSample(sample)
Trains the network on a single experience tuple. Computes the Bellman target, clips the TD error, updates the output layer weights, and propagates a scaled error signal to hidden layer 2.
Backpropagation is partial: only the output-layer weights (
w3, b3) and hidden-layer-2 weights (w2, b2) are updated. No gradient is propagated to hidden layer 1 (w1, b1). The additional scale factor of 0.2 on the hidden-2 updates dampens gradient magnitude deeper in the network.experienceReplay()
Samples a random mini-batch from memory and calls trainSample() on each transition, then decays the exploration rate.
batchSize (20) transitions, the method returns immediately without updating any weights.
Each of the batchSize samples is drawn with uniform replacement from the full memory buffer, so the same transition may be trained on more than once within a single call.