<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daksh Jain</title>
    <description>The latest articles on DEV Community by Daksh Jain (@dash10107).</description>
    <link>https://dev.to/dash10107</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3989571%2F196ca0b3-07b4-47f2-879b-4ce4168dcda8.png</url>
      <title>DEV Community: Daksh Jain</title>
      <link>https://dev.to/dash10107</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dash10107"/>
    <language>en</language>
    <item>
      <title>Precision Engineering: Landing a Rocket with Soft Actor-Critic (SAC)</title>
      <dc:creator>Daksh Jain</dc:creator>
      <pubDate>Thu, 25 Jun 2026 04:00:00 +0000</pubDate>
      <link>https://dev.to/dash10107/precision-engineering-landing-a-rocket-with-soft-actor-critic-sac-30ho</link>
      <guid>https://dev.to/dash10107/precision-engineering-landing-a-rocket-with-soft-actor-critic-sac-30ho</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fycbceir3v6ya5s1tfo6u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fycbceir3v6ya5s1tfo6u.png" alt="Rocket SAC Cover" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the previous projects in this portfolio, our AI agents interacted with the world using a "D-pad." The warehouse robots could move &lt;em&gt;Up, Down, Left, or Right&lt;/em&gt;. The smart grid battery could &lt;em&gt;Charge&lt;/em&gt; or &lt;em&gt;Discharge&lt;/em&gt;. These are called &lt;strong&gt;Discrete Actions&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;But the physical world rarely works like a D-pad. It works like a steering wheel.&lt;/p&gt;

&lt;p&gt;If you are trying to land a multi-million dollar rocket back onto a landing pad, you cannot just tell the main engine to "Turn On" or "Turn Off." You have to output an exact, continuous value. You might need exactly &lt;code&gt;42.7%&lt;/code&gt; thrust on the main engine, while simultaneously firing the left lateral thruster at &lt;code&gt;14.2%&lt;/code&gt;. If you output too much thrust, the rocket shoots back into the atmosphere. If you output too little, it violently crashes into the concrete.&lt;/p&gt;

&lt;p&gt;This is the frontier of &lt;strong&gt;Continuous Control&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To solve this, I built the &lt;strong&gt;&lt;a href="https://huggingface.co/spaces/Dash10107/rocket-lander-sac" rel="noopener noreferrer"&gt;Rocket Lander Simulator&lt;/a&gt;&lt;/strong&gt;. In this project, we transition away from the algorithms we've used so far and introduce one of the most powerful continuous control algorithms in modern robotics: &lt;strong&gt;Soft Actor-Critic (SAC)&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Curse of the Continuous
&lt;/h2&gt;

&lt;p&gt;Why can't we just use the Deep Q-Networks (DQN) we used to solve our Smart Grid and Logistics projects? &lt;/p&gt;

&lt;p&gt;DQN is a discrete algorithm. It calculates the expected future reward for a fixed list of actions, and then picks the highest one. If you wanted to use DQN to land a rocket, you would have to chop the engine throttle into discrete buckets: 10%, 20%, 30%, etc. &lt;/p&gt;

&lt;p&gt;But what if the perfect landing requires exactly &lt;code&gt;15.5%&lt;/code&gt; thrust? If you create a bucket for every possible decimal percentage across multiple engines, the number of possible combinations explodes into the millions. The neural network would freeze, unable to compute the math in real time. &lt;/p&gt;

&lt;p&gt;We need an algorithm that doesn't choose from a list, but rather &lt;em&gt;generates&lt;/em&gt; a highly precise continuous number.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Physics of the Action Space
&lt;/h3&gt;

&lt;p&gt;To achieve this, the neural network in our simulation outputs an array of two continuous numbers, both bounded between &lt;code&gt;-1.0&lt;/code&gt; and &lt;code&gt;1.0&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Main Engine:&lt;/strong&gt; Negative values mean the engine is off. Positive values map smoothly to thrust intensity (e.g., &lt;code&gt;0.5&lt;/code&gt; = 50% thrust).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lateral Thrusters:&lt;/strong&gt; Negative values fire the left thruster (pushing the rocket right). Positive values fire the right thruster.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because these outputs are mathematically continuous, the AI can make micro-adjustments smaller than a fraction of a percent to perfectly balance the rocket.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Generating Precision: The Gaussian Actor
&lt;/h2&gt;

&lt;p&gt;In the Warehouse project, we introduced the &lt;strong&gt;Actor-Critic&lt;/strong&gt; architecture. SAC uses this same dual-brain setup, but the Actor behaves entirely differently.&lt;/p&gt;

&lt;p&gt;Instead of outputting probabilities for discrete buttons, the SAC Actor outputs the mathematical parameters of a &lt;strong&gt;Gaussian Distribution&lt;/strong&gt; (a Bell Curve). Specifically, for every engine, it outputs two variables:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Mean (μ):&lt;/strong&gt; What the AI thinks the exact perfect throttle percentage is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Standard Deviation (σ):&lt;/strong&gt; How confident the AI is in that guess (the spread of the curve).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is exactly what that looks like in PyTorch. The neural network outputs the mean and standard deviation, and we sample an action from that curve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;torch.distributions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Normal&lt;/span&gt;

&lt;span class="c1"&gt;# 1. The Neural Network outputs the parameters of the Bell Curve
&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;actor_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;log_std&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actor_log_std&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log_std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# 2. We build the mathematical Bell Curve
&lt;/span&gt;&lt;span class="n"&gt;distribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. We sample a random throttle percentage from the curve
&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;distribution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rsample&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 

&lt;span class="c1"&gt;# 4. Squeeze it between -1.0 and 1.0 for the physics engine
&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tanh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When the AI is completely untrained, its Standard Deviation is massive. The code above will wildly fire the engines at random percentages. As the Critic network slowly coaches the Actor, the Standard Deviation mathematically shrinks. The Bell Curve becomes a sharp, narrow spike, and the AI outputs highly precise, deterministic throttle commands.&lt;/p&gt;

&lt;p&gt;But landing a rocket is dangerous. We have to prevent the AI from becoming recklessly overconfident.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. Curing Neural Optimism (The Twin Critics)
&lt;/h2&gt;

&lt;p&gt;Neural Networks suffer from a well-documented psychological flaw: &lt;strong&gt;Optimism Bias&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;If an untrained AI accidentally fires its engines at 100% and miraculously avoids crashing due to a lucky gust of wind, the Critic network might immediately assume that 100% thrust is a genius move. It vastly overestimates the value of that action. In a physical simulation, optimism leads to catastrophic, vehicle-destroying crashes.&lt;/p&gt;

&lt;p&gt;To cure this, SAC introduced a brilliant engineering trick: &lt;strong&gt;Twin Critics&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of having one Critic coach the Actor, SAC uses &lt;em&gt;two&lt;/em&gt; completely independent Critic networks (&lt;code&gt;Q1&lt;/code&gt; and &lt;code&gt;Q2&lt;/code&gt;). When the Actor asks, "How many points will I get if I fire the engine at 42%?", both Critics calculate an answer. &lt;/p&gt;

&lt;p&gt;The algorithm mathematically forces the Actor to take the &lt;strong&gt;minimum&lt;/strong&gt; of the two predictions:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Critic 1 predicts the future reward
&lt;/span&gt;&lt;span class="n"&gt;q1_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;critic_1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Critic 2 independently predicts the future reward
&lt;/span&gt;&lt;span class="n"&gt;q2_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;critic_2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# The AI assumes the absolute worst-case scenario
&lt;/span&gt;&lt;span class="n"&gt;target_q_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q1_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q2_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By forcing the AI to assume the absolute worst-case scenario, SAC completely eliminates optimism bias. The AI only attempts a dangerous maneuver if &lt;em&gt;both&lt;/em&gt; pessimistic coaches agree that it is completely safe.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Maximum Entropy RL: Rewarding Chaos
&lt;/h2&gt;

&lt;p&gt;The most defining feature of Soft Actor-Critic is the word "Soft". This refers to &lt;strong&gt;Maximum Entropy Reinforcement Learning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In standard RL, the goal is simple: Maximize the Reward. &lt;br&gt;
But SAC changes the fundamental equation of AI by adding a new term to the objective function:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Objective = Maximize (Reward + α * Entropy)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Entropy is the mathematical measurement of chaos and randomness. Why on earth would we actively reward a rocket for flying chaotically? &lt;/p&gt;

&lt;p&gt;Imagine an AI that finds exactly one perfect, elegant path to the landing pad. It memorizes that path perfectly. But what happens during a real flight if a massive gust of wind blows the rocket 10 feet to the left? Because the AI only memorized one path, it has no idea what to do, panics, and crashes.&lt;/p&gt;

&lt;p&gt;By mathematically rewarding Entropy (scaled by a temperature parameter &lt;code&gt;α&lt;/code&gt; or &lt;code&gt;alpha&lt;/code&gt;), we actively force the AI to &lt;em&gt;not&lt;/em&gt; memorize a single path.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Calculate how 'predictable' the AI's action was
&lt;/span&gt;&lt;span class="n"&gt;log_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;distribution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# If the action was highly unpredictable (high entropy),
# the log_prob is very low/negative. We subtract it to give a bonus!
&lt;/span&gt;&lt;span class="n"&gt;actor_loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;log_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;min_q_value&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We force it to discover 1,000 different, slightly messy ways to land the rocket. By forcing the AI to explore the chaos, it builds an incredibly robust, generalized intuition. When that massive gust of wind hits it in the real world, the AI doesn't panic—it has already explored that exact chaotic state during its training and knows exactly how to dynamically recover.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. Liquid Memory and Sample Efficiency
&lt;/h2&gt;

&lt;p&gt;If you've ever trained a neural network, you know they are incredibly data-hungry. To solve this, SAC is designed to be an &lt;strong&gt;Off-Policy&lt;/strong&gt; algorithm with a massive &lt;strong&gt;Replay Buffer&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Instead of throwing away data after every flight, SAC records every single millisecond of telemetry (State, Action, Reward, Next State) into a memory bank that holds 1,000,000 steps. While the rocket is flying, the AI is constantly "daydreaming" about old flights, randomly sampling batches of past mistakes to squeeze every possible drop of mathematical insight out of them. This makes SAC incredibly &lt;strong&gt;Sample Efficient&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Polyak Averaging (The Smooth Update)
&lt;/h3&gt;

&lt;p&gt;Because the AI is learning so aggressively from its past, we run into a stability problem. If the Target Critic network updates its weights too abruptly, the AI loses its mind and unlearns how to fly. &lt;/p&gt;

&lt;p&gt;To fix this, SAC uses a trick called &lt;strong&gt;Polyak Averaging&lt;/strong&gt;. Instead of replacing the Target network's brain entirely, it updates using an exponential moving average (&lt;code&gt;tau = 0.005&lt;/code&gt;). &lt;br&gt;
Every step, the new brain is composed of &lt;code&gt;99.5%&lt;/code&gt; of the old weights, and &lt;code&gt;0.5%&lt;/code&gt; of the newly learned weights. This creates an ultra-smooth, liquid learning curve that prevents the math from oscillating wildly.&lt;/p&gt;


&lt;h2&gt;
  
  
  🧪 Try It Yourself
&lt;/h2&gt;

&lt;p&gt;To truly appreciate the power of SAC, you have to watch its continuous precision and test its limits. Open the &lt;strong&gt;&lt;a href="https://huggingface.co/spaces/Dash10107/rocket-lander-sac" rel="noopener noreferrer"&gt;Rocket Lander Simulator&lt;/a&gt;&lt;/strong&gt; and run these engineering tests:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the Telemetry:&lt;/strong&gt; Go to the Mission Control tab and run a baseline flight. Look at the &lt;code&gt;Engine Throttle&lt;/code&gt; chart at the bottom. You won't see blocky, ON/OFF steps. You will see incredibly smooth, continuous curves as the AI perfectly modulates the main engine to counter gravity, followed by lightning-fast micro-bursts from the lateral thrusters to correct its angle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Wind Turbulence Test:&lt;/strong&gt; This is where the Entropy training shines. Turn the &lt;code&gt;Wind Power&lt;/code&gt; up to 15, and the &lt;code&gt;Turbulence&lt;/code&gt; up to 1.5. Run the mission again. Watch the replay GIF. You will see the rocket get violently pushed off-course by invisible forces, but because of its Maximum Entropy training, it dynamically corrects its continuous thrusters in real-time to fight the wind and hit the pad.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Sim-to-Real Gap (Distribution Shift):&lt;/strong&gt; Change the gravity from &lt;code&gt;-10.0&lt;/code&gt; (Earth) to &lt;code&gt;-5.0&lt;/code&gt; (The Moon). If the pre-trained AI crashes, you are witnessing &lt;strong&gt;Distribution Shift&lt;/strong&gt;. Because the AI was only trained on Earth, its Bell Curves are perfectly tuned for Earth physics. When the physical rules change, the AI fails. This is exactly why deploying robotics in the real world is so hard—simulators never perfectly match reality. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-Tune the Brain:&lt;/strong&gt; If the AI failed the Moon test, go to the Training Lab. Run 10,000 timesteps of fine-tuning, and then run the Moon mission again. Watch how quickly the Replay Buffer adapts the weights to the new gravity.&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;Moving from discrete grids to continuous physical simulations is the holy grail of robotics. It requires us to abandon simple lookup tables and embrace algorithms like Soft Actor-Critic—managing infinite action spaces with Gaussian distributions, curing optimism with Twin Critics, building physical robustness by actively rewarding chaos, and stabilizing learning with liquid memory updates.&lt;/p&gt;

&lt;p&gt;This is the sixth of 12 interactive RL projects I am building to bridge the gap between academic math and real-world intuition. If this deep dive into continuous control helped clarify how real robots think, I would be incredibly grateful if you checked out the source code and dropped a star on the full repository:&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Reinforcement Learning Portfolio on GitHub&lt;/strong&gt;&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Dash10107" rel="noopener noreferrer"&gt;
        Dash10107
      &lt;/a&gt; / &lt;a href="https://github.com/Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;
        rl-portfolio
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      End-to-end reinforcement learning projects — Q-Learning, DQN, PPO, SAC, A2C, IPPO, MBRL, HMM, RLHF, and Multi-Armed Bandits — each deployed as an interactive Gradio app on Hugging Face Spaces.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Dash10107/rl-portfolio/assets/banner.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FDash10107%2Frl-portfolio%2FHEAD%2Fassets%2Fbanner.png" alt="Reinforcement Learning Portfolio Banner" width="900"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/actions/workflows/lint.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Dash10107/rl-portfolio/actions/workflows/lint.yml/badge.svg" alt="Lint Status"&gt;&lt;/a&gt;
  &lt;a href="https://huggingface.co/spaces/Dash10107" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/872f3617f8520e4eec3aa40401095b0ebe81b0e56aa6608df1c8961c013cfb30/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f48756767696e67253230466163652d5370616365732d79656c6c6f773f7374796c653d666c6174266c6f676f3d68756767696e6766616365" alt="HuggingFace Spaces"&gt;&lt;/a&gt;
  &lt;a href="https://colab.research.google.com/github/Dash10107/rl-portfolio/blob/main/open_in_colab.ipynb" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ff68bd4526bf49af34888a32dc6cdaaf15de08b2e87958ca1d75193c07fde47a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f6c61622d4f70656e2d6f72616e67653f7374796c653d666c6174266c6f676f3d676f6f676c65636f6c6162266c6f676f436f6c6f723d7768697465" alt="Open in Colab"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/codespaces/new?hide_repo_select=true&amp;amp;ref=main&amp;amp;repo=Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ee58e60d66f1cfc9ac447becf2fa8330807686c21fbaf15aedf168d9b02cc1a7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f64657370616365732d4f70656e2d626c75653f7374796c653d666c6174266c6f676f3d676974687562266c6f676f436f6c6f723d7768697465" alt="Open in Codespaces"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/blob/main/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b0347597560d4e01a4ea5c0d1afaecd0a7b68752516e32700fab4f6964606491/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75653f7374796c653d666c6174" alt="License"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/stargazers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f2552088777d97a2d8c6487ca73bb2083a86bfa7c18352bf6a23f9bcae1cebe0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f73746172732d25453225393825383525323077656c636f6d652d627269676874677265656e3f7374796c653d666c6174" alt="GitHub stars"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/issues" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/51bcf793698eb578579df6f367164058b73aad4e4411b1f592a646ec9505867a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6973737565732d77656c636f6d652d627269676874677265656e3f7374796c653d666c6174" alt="GitHub issues"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Reinforcement Learning Portfolio&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A collection of 12 end-to-end reinforcement learning projects, each deployed as an interactive web application on Hugging Face Spaces. The projects span the full range of modern RL — from the simplest tabular methods that fit on a single page, to multi-agent coordination, model-based planning, and learning from human feedback.&lt;/p&gt;

&lt;p&gt;Every project is built to be understood by someone who is new to RL. Each has its own README explaining the algorithm, the environment, and what you are looking at when you run it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New to reinforcement learning?&lt;/strong&gt; Start with these two documents before anything else:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Dash10107/rl-portfolio/./CONCEPTS.md" rel="noopener noreferrer"&gt;CONCEPTS.md&lt;/a&gt; — what RL is, the core vocabulary, and how all 12 algorithms relate to each other&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Dash10107/rl-portfolio/./GETTING_STARTED.md" rel="noopener noreferrer"&gt;GETTING_STARTED.md&lt;/a&gt; — step-by-step guide to running your first project and your first experiment&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Key Highlights&lt;/h2&gt;
&lt;/div&gt;


&lt;ul&gt;

&lt;li&gt;⚡ &lt;strong&gt;Zero-Install Interactive Demos&lt;/strong&gt;: Every project is deployed live on Hugging Face Spaces for instant testing.&lt;/li&gt;

&lt;li&gt;🎓 &lt;strong&gt;Curriculum-Based&lt;/strong&gt;…&lt;/li&gt;

&lt;/ul&gt;&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Let me know in the comments: &lt;em&gt;If you had to write the reward function for a self-driving car, what penalty would you assign to a bumpy brake versus a slow arrival?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>reinforcementlearning</category>
      <category>ai</category>
      <category>robotics</category>
    </item>
    <item>
      <title>The Invisible Hand: Teaching Warehouse Robots Teamwork with Multi-Agent PPO</title>
      <dc:creator>Daksh Jain</dc:creator>
      <pubDate>Wed, 24 Jun 2026 03:31:00 +0000</pubDate>
      <link>https://dev.to/dash10107/the-invisible-hand-teaching-warehouse-robots-teamwork-with-multi-agent-ppo-1chp</link>
      <guid>https://dev.to/dash10107/the-invisible-hand-teaching-warehouse-robots-teamwork-with-multi-agent-ppo-1chp</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqkmfnrdeycypqnsdfhwq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqkmfnrdeycypqnsdfhwq.png" alt="MARL Warehouse Cover" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you look inside a massive Amazon fulfillment center, you will see thousands of small orange robots scurrying across the floor, carrying shelves of inventory. They weave past each other in narrow corridors, rarely colliding, and somehow coordinate to process thousands of orders per hour.&lt;/p&gt;

&lt;p&gt;How is this coordination actually programmed?&lt;/p&gt;

&lt;p&gt;You might assume there is a massive "Central Brain" computer that calculates the path for every single robot simultaneously. But in computer science, we know that is mathematically impossible. Because of the &lt;strong&gt;Curse of Dimensionality&lt;/strong&gt;, the number of possible state combinations for 1,000 robots is larger than the number of atoms in the universe. A central brain would instantly freeze trying to calculate the math.&lt;/p&gt;

&lt;p&gt;Instead, the robots must be decentralized. They must think for themselves. But if every robot is acting selfishly to finish its own task, how do you prevent them from causing massive traffic jams?&lt;/p&gt;

&lt;p&gt;To answer this, I built the &lt;strong&gt;&lt;a href="https://huggingface.co/spaces/Dash10107/marl-warehouse-sim" rel="noopener noreferrer"&gt;MARL Warehouse Coordinator&lt;/a&gt;&lt;/strong&gt;. In this simulation, we drop a fleet of AI agents into a grid and force them to learn teamwork using &lt;strong&gt;Multi-Agent Reinforcement Learning (MARL)&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Nightmare of Non-Stationarity
&lt;/h2&gt;

&lt;p&gt;In the previous projects in this portfolio, we trained a single agent interacting with an environment that had fixed rules.&lt;/p&gt;

&lt;p&gt;Multi-Agent RL is fundamentally different, and infinitely harder. &lt;/p&gt;

&lt;p&gt;Imagine trying to learn how to play chess, but every time you make a move, your opponent also learns and changes their strategy. What was a "good move" yesterday is suddenly a "terrible move" today. In RL, this is known as the &lt;strong&gt;Non-Stationarity Problem&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;When multiple robots are training in the same warehouse, the "environment" isn't just the walls and packages. The environment &lt;em&gt;is the other robots&lt;/em&gt;. Standard Reinforcement Learning algorithms completely break down in non-stationary environments because the mathematical targets are constantly shifting. &lt;/p&gt;

&lt;p&gt;To solve this, we rely on one of the most robust algorithms in modern AI: &lt;strong&gt;Proximal Policy Optimization (PPO)&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Independent PPO (IPPO) and the "Proximal" Trick
&lt;/h2&gt;

&lt;p&gt;The simplest way to build a Multi-Agent system is to treat it like a single-agent system. We give every single robot its own independent brain (Neural Network), and let them all train at the same time. This is called &lt;strong&gt;Independent Learning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But as we discussed, independent learning usually causes the math to explode due to non-stationarity. To keep the training stable, we use PPO. &lt;/p&gt;

&lt;p&gt;PPO is the exact same underlying algorithm that OpenAI used to fine-tune ChatGPT. It is famous for a mathematical trick called the &lt;strong&gt;Clipped Surrogate Objective&lt;/strong&gt; (the "Proximal" part of PPO).&lt;/p&gt;

&lt;p&gt;In Machine Learning, when a neural network discovers a good action, it adjusts its weights to take that action more often. But sometimes, the math calculates a gradient step that is so massive it completely destroys the network's previously learned knowledge (a phenomenon called &lt;em&gt;Policy Collapse&lt;/em&gt;). &lt;/p&gt;

&lt;p&gt;PPO prevents this by strictly clipping the update ratio. Here is exactly what the "Proximal" math looks like in PyTorch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Calculate how much the robot's brain has changed since the last update
&lt;/span&gt;&lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_log_prob&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;old_log_prob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Calculate the raw, unbounded gradient
&lt;/span&gt;&lt;span class="n"&gt;surrogate_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;advantage&lt;/span&gt;

&lt;span class="c1"&gt;# 3. Clip the ratio to enforce a strict speed limit (e.g., epsilon = 0.2 means +/- 20%)
&lt;/span&gt;&lt;span class="n"&gt;surrogate_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;epsilon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;epsilon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;advantage&lt;/span&gt;

&lt;span class="c1"&gt;# 4. The math physically prevents massive, destructive updates
&lt;/span&gt;&lt;span class="n"&gt;actor_loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;surrogate_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;surrogate_2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By enforcing this strict "speed limit" on learning (&lt;code&gt;torch.clamp&lt;/code&gt;), PPO ensures that the robot's policy only changes in small, stable increments. This incredible stability is exactly what allows multiple independent robots to train in the same warehouse without their math exploding into chaos.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. The Dual Brain: Actor-Critic Architecture
&lt;/h2&gt;

&lt;p&gt;When you train a robot with PPO, you aren't actually training one Neural Network. You are training two. PPO uses an &lt;strong&gt;Actor-Critic Architecture&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Actor:&lt;/strong&gt; This is the network that actually drives the robot. It looks at the grid and outputs probabilities: &lt;em&gt;"I am 80% sure I should move Left, and 20% sure I should move Up."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Critic:&lt;/strong&gt; This network acts as the coach. It doesn't make decisions. Instead, it looks at the grid and outputs a single number predicting how good the current situation is: &lt;em&gt;"I estimate being in this corridor is worth 5 points."&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How do they work together? Through a concept called &lt;strong&gt;Advantage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's say the Critic predicts the current state is worth 5 points. The Actor decides to move Left, picks up a package, and the robot actually earns 8 points. &lt;/p&gt;

&lt;p&gt;The math calculates the Advantage: &lt;code&gt;8 actual points - 5 expected points = +3 Advantage&lt;/code&gt;. &lt;br&gt;
The Critic tells the Actor: &lt;em&gt;"Wow! Moving Left was 3 points better than I expected! Update your weights to do that more often."&lt;/em&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Generalized Advantage Estimation (GAE)
&lt;/h3&gt;

&lt;p&gt;If a robot wanders aimlessly for 10 moves, accidentally nudges a package, and earns 8 points, how does the Critic know which of the 10 moves was the "good" one? &lt;/p&gt;

&lt;p&gt;In our code, we solve this temporal credit assignment problem using a highly advanced technique called &lt;strong&gt;GAE (Generalized Advantage Estimation)&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;GAE calculates a mathematical "Error" (Delta) for every single step:&lt;br&gt;
&lt;strong&gt;Delta = Immediate Reward + (Gamma * Next Expected Value) - Current Expected Value&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It then uses a smoothing parameter (Lambda, or &lt;code&gt;λ&lt;/code&gt;) to exponentially decay that error backwards through time. Here is the exact logic we use to calculate the Advantage for any given step &lt;code&gt;t&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The immediate error of the current move
&lt;/span&gt;&lt;span class="n"&gt;delta_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reward_t&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gamma&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;value_next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;value_current&lt;/span&gt;

&lt;span class="c1"&gt;# Blend it with the exponentially decayed errors of all future moves
&lt;/span&gt;&lt;span class="n"&gt;advantage_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;delta_t&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gamma&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;advantage_next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By perfectly blending short-term immediate rewards with long-term future predictions, GAE allows the Critic to coach the Actor with incredible precision.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Engineering the State: Partial Observability
&lt;/h2&gt;

&lt;p&gt;If a central brain can't compute the whole warehouse, how do the independent robots do it? &lt;/p&gt;

&lt;p&gt;We use a trick called &lt;strong&gt;Partial Observability&lt;/strong&gt; (or Local Sensing). Instead of feeding the robot a massive 2D image of the entire 100x100 warehouse, we only give the robot a tiny, 13-dimensional array of numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Its own coordinates.&lt;/li&gt;
&lt;li&gt;  The coordinates of its current package.&lt;/li&gt;
&lt;li&gt;  The relative coordinates of its 4 nearest neighbors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By blinding the robot to the rest of the warehouse, the Neural Network only needs 128 hidden neurons. It trains in seconds instead of days. The robot learns to navigate locally, trusting that its localized decisions will result in global efficiency.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. Entropy &amp;amp; The Invisible Hand of Shared Rewards
&lt;/h2&gt;

&lt;p&gt;If every robot has an independent, localized brain, how do they learn teamwork? How do they know to yield to each other in narrow corridors instead of fighting for space?&lt;/p&gt;

&lt;p&gt;They learn through the "Invisible Hand" of economics—specifically, &lt;strong&gt;Shared Rewards&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;+2.0 points&lt;/strong&gt; when &lt;em&gt;anyone&lt;/em&gt; delivers a package.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;-0.3 points&lt;/strong&gt; if you collide with another robot.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;-0.01 points&lt;/strong&gt; for every step taken.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To ensure the robots actually discover this teamwork, the overall PPO algorithm balances three massive mathematical forces in a single equation:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The Final PPO Objective Function
&lt;/span&gt;&lt;span class="n"&gt;total_loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actor_loss&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;critic_loss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;entropy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Notice that we actively &lt;em&gt;subtract&lt;/em&gt; the Entropy term from the loss, mathematically rewarding the AI for being chaotic. If we didn't do this, a robot might find one mediocre path and stubbornly stick to it forever. By rewarding chaos (&lt;code&gt;0.01 * entropy&lt;/code&gt;) early on, the robots explore the entire warehouse until they discover the optimal choreography.&lt;/p&gt;

&lt;p&gt;They learn to claim different pickup zones, and they instinctively wait at intersections to let other robots pass, because avoiding the &lt;code&gt;-0.3&lt;/code&gt; collision penalty results in a higher net score for their independent brains.&lt;/p&gt;


&lt;h2&gt;
  
  
  🧪 Try It Yourself
&lt;/h2&gt;

&lt;p&gt;To see this multi-agent choreography in action, open the &lt;strong&gt;&lt;a href="https://huggingface.co/spaces/Dash10107/marl-warehouse-sim" rel="noopener noreferrer"&gt;MARL Warehouse Simulator&lt;/a&gt;&lt;/strong&gt; and run these visual experiments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Traffic Jam (Greedy Baseline):&lt;/strong&gt; Go to the Simulation tab. Select the &lt;code&gt;Greedy&lt;/code&gt; strategy. Greedy robots are programmed with a simple heuristic: "Always walk directly toward the package." Click Run Simulation. You will watch them instantly cluster together, blocking each other in narrow corridors and creating massive traffic jams because they have no awareness of their teammates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Choreography (IPPO Agent):&lt;/strong&gt; Switch the strategy to &lt;code&gt;IPPO (trained)&lt;/code&gt;. Run the simulation again. Watch how the robots smoothly weave around each other. Notice how they naturally spread out to different sectors of the warehouse to avoid getting in each other's way. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Train the Swarm:&lt;/strong&gt; Go to the Training tab. Set the number of robots to 4, and start the training. Watch the live "Collisions per Episode" chart trend downwards as the Critic networks slowly teach the Actor networks how to navigate the non-stationary chaos of their peers.&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;Multi-Agent Reinforcement Learning (MARL) is the frontier of modern AI. Moving from a single agent in a static world to multiple agents in a dynamic world requires robust algorithms like PPO and clever reward shaping. By letting independent brains learn through shared economics, we can create complex, decentralized teamwork that would be impossible to hard-code.&lt;/p&gt;

&lt;p&gt;This is the fifth of 12 interactive RL projects I am building to bridge the gap between academic math and real-world intuition. If this breakdown of PPO and Actor-Critic architecture helped things click for you, I would be incredibly grateful if you checked out the source code and dropped a star on the full repository:&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Reinforcement Learning Portfolio on GitHub&lt;/strong&gt;&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Dash10107" rel="noopener noreferrer"&gt;
        Dash10107
      &lt;/a&gt; / &lt;a href="https://github.com/Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;
        rl-portfolio
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      End-to-end reinforcement learning projects — Q-Learning, DQN, PPO, SAC, A2C, IPPO, MBRL, HMM, RLHF, and Multi-Armed Bandits — each deployed as an interactive Gradio app on Hugging Face Spaces.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Dash10107/rl-portfolio/assets/banner.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FDash10107%2Frl-portfolio%2FHEAD%2Fassets%2Fbanner.png" alt="Reinforcement Learning Portfolio Banner" width="900"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/actions/workflows/lint.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Dash10107/rl-portfolio/actions/workflows/lint.yml/badge.svg" alt="Lint Status"&gt;&lt;/a&gt;
  &lt;a href="https://huggingface.co/spaces/Dash10107" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/872f3617f8520e4eec3aa40401095b0ebe81b0e56aa6608df1c8961c013cfb30/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f48756767696e67253230466163652d5370616365732d79656c6c6f773f7374796c653d666c6174266c6f676f3d68756767696e6766616365" alt="HuggingFace Spaces"&gt;&lt;/a&gt;
  &lt;a href="https://colab.research.google.com/github/Dash10107/rl-portfolio/blob/main/open_in_colab.ipynb" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ff68bd4526bf49af34888a32dc6cdaaf15de08b2e87958ca1d75193c07fde47a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f6c61622d4f70656e2d6f72616e67653f7374796c653d666c6174266c6f676f3d676f6f676c65636f6c6162266c6f676f436f6c6f723d7768697465" alt="Open in Colab"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/codespaces/new?hide_repo_select=true&amp;amp;ref=main&amp;amp;repo=Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ee58e60d66f1cfc9ac447becf2fa8330807686c21fbaf15aedf168d9b02cc1a7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f64657370616365732d4f70656e2d626c75653f7374796c653d666c6174266c6f676f3d676974687562266c6f676f436f6c6f723d7768697465" alt="Open in Codespaces"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/blob/main/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b0347597560d4e01a4ea5c0d1afaecd0a7b68752516e32700fab4f6964606491/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75653f7374796c653d666c6174" alt="License"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/stargazers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f2552088777d97a2d8c6487ca73bb2083a86bfa7c18352bf6a23f9bcae1cebe0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f73746172732d25453225393825383525323077656c636f6d652d627269676874677265656e3f7374796c653d666c6174" alt="GitHub stars"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/issues" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/51bcf793698eb578579df6f367164058b73aad4e4411b1f592a646ec9505867a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6973737565732d77656c636f6d652d627269676874677265656e3f7374796c653d666c6174" alt="GitHub issues"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Reinforcement Learning Portfolio&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A collection of 12 end-to-end reinforcement learning projects, each deployed as an interactive web application on Hugging Face Spaces. The projects span the full range of modern RL — from the simplest tabular methods that fit on a single page, to multi-agent coordination, model-based planning, and learning from human feedback.&lt;/p&gt;

&lt;p&gt;Every project is built to be understood by someone who is new to RL. Each has its own README explaining the algorithm, the environment, and what you are looking at when you run it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New to reinforcement learning?&lt;/strong&gt; Start with these two documents before anything else:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Dash10107/rl-portfolio/./CONCEPTS.md" rel="noopener noreferrer"&gt;CONCEPTS.md&lt;/a&gt; — what RL is, the core vocabulary, and how all 12 algorithms relate to each other&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Dash10107/rl-portfolio/./GETTING_STARTED.md" rel="noopener noreferrer"&gt;GETTING_STARTED.md&lt;/a&gt; — step-by-step guide to running your first project and your first experiment&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Key Highlights&lt;/h2&gt;
&lt;/div&gt;


&lt;ul&gt;

&lt;li&gt;⚡ &lt;strong&gt;Zero-Install Interactive Demos&lt;/strong&gt;: Every project is deployed live on Hugging Face Spaces for instant testing.&lt;/li&gt;

&lt;li&gt;🎓 &lt;strong&gt;Curriculum-Based&lt;/strong&gt;…&lt;/li&gt;

&lt;/ul&gt;&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Let me know in the comments: &lt;em&gt;What other real-world systems (like traffic lights or stock trading) do you think could be optimized using independent multi-agent AI?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>reinforcementlearning</category>
      <category>ai</category>
      <category>robotics</category>
    </item>
    <item>
      <title>Energy Arbitrage: How AI Trades Electricity Using Reinforcement Learning</title>
      <dc:creator>Daksh Jain</dc:creator>
      <pubDate>Tue, 23 Jun 2026 03:31:00 +0000</pubDate>
      <link>https://dev.to/dash10107/energy-arbitrage-how-ai-trades-electricity-using-reinforcement-learning-9a8</link>
      <guid>https://dev.to/dash10107/energy-arbitrage-how-ai-trades-electricity-using-reinforcement-learning-9a8</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu0fnbdiwcfkgnjdlse3q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu0fnbdiwcfkgnjdlse3q.png" alt="Smart Grid Cover" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the most fascinating challenges in the modern energy sector is that the price of electricity changes every single hour.&lt;/p&gt;

&lt;p&gt;At 3:00 AM, when wind turbines are spinning but everyone is asleep, power is incredibly cheap—sometimes dropping to 6 cents per kilowatt-hour. But at 6:00 PM, when the sun goes down and everyone turns on their ovens and air conditioners, the grid undergoes immense stress. Prices can violently spike to 60 cents or even $2.50 per kilowatt-hour during a crisis.&lt;/p&gt;

&lt;p&gt;If you own a massive industrial battery system, this volatility presents a massive opportunity known as &lt;strong&gt;Energy Arbitrage&lt;/strong&gt;. You buy electricity when it is cheap, store it, and sell it back to the grid (or use it to power your building) when prices spike. &lt;/p&gt;

&lt;p&gt;But there is a catch: &lt;em&gt;You do not know the future.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To explore how Artificial Intelligence solves this, I built the &lt;strong&gt;&lt;a href="https://huggingface.co/spaces/Dash10107/smart-grid-energy-optimizer" rel="noopener noreferrer"&gt;Smart Grid Energy Optimizer&lt;/a&gt;&lt;/strong&gt;. In this interactive simulation, we pit a Deep Reinforcement Learning agent against simple human heuristics and a mathematically perfect algorithm to see how well AI can trade energy under uncertainty.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Heuristic Fallacy
&lt;/h2&gt;

&lt;p&gt;If you asked a software engineer to write a script to manage this battery, they would likely write a simple Rule-Based Heuristic. In code, it looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;heuristic_trader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;daily_average_price&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;daily_average_price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CHARGE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;current_price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;daily_average_price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.25&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DISCHARGE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;IDLE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This works reasonably well, but it is deeply flawed because it lacks nuance. A static rule doesn't know if a small price spike at 2:00 PM is the peak of the day, or if an absolutely massive spike is coming at 6:00 PM. Furthermore, if you add solar panels to the roof of your building, the logic becomes overwhelmingly complex. If the sun is shining, should you use that free solar energy to charge the battery, or should you push it directly into the building to offset demand? &lt;/p&gt;

&lt;p&gt;Hard-coding rules for every possible combination of price, time, solar generation, and building load is a nightmare.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. The Theoretical Ceiling: Dynamic Programming
&lt;/h2&gt;

&lt;p&gt;Before we train an AI to solve this, we need a baseline. How do we know if our AI is actually doing a good job? We need to calculate the &lt;strong&gt;mathematical upper bound&lt;/strong&gt; of the problem.&lt;/p&gt;

&lt;p&gt;To do this, we use an elegant computer science technique called &lt;strong&gt;Dynamic Programming (DP)&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;If we assume that we have &lt;em&gt;perfect foresight&lt;/em&gt;—meaning we magically know the exact electricity price, solar output, and building load for every hour of the upcoming day—we can use DP to calculate the absolute optimal charging schedule.&lt;/p&gt;

&lt;p&gt;The trick to Dynamic Programming is &lt;strong&gt;Backward Induction&lt;/strong&gt; via the Bellman Equation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;V(s) = max_a [ Reward(s, a) + V(next_s) ]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We don't start at 12:00 AM. We start at the very end of the day (Hour 24) and work backwards, calculating the exact value of every possible state:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;solve_dp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_soc_levels&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Create a grid of all possible battery charge levels
&lt;/span&gt;    &lt;span class="n"&gt;soc_grid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;linspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_soc_levels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;V&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zeros&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_soc_levels&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# Work backwards from the end of the day to the beginning
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;si&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;soc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;soc_grid&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;best_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1e9&lt;/span&gt;

            &lt;span class="c1"&gt;# Test every possible action (-3kW to +3kW)
&lt;/span&gt;            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;possible_actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;reward&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_soc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;simulate_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;soc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="c1"&gt;# The total value is the immediate reward + the known future value
&lt;/span&gt;                &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;get_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next_soc&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;best_val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;best_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;

            &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;si&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;best_val&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt; &lt;span class="c1"&gt;# Returns the absolute maximum possible profit
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Because DP explores every possible state starting from the end, it guarantees the perfect schedule. &lt;/p&gt;
&lt;h3&gt;
  
  
  The Curse of Dimensionality
&lt;/h3&gt;

&lt;p&gt;If DP is mathematically perfect, why don't we use it for everything? &lt;/p&gt;

&lt;p&gt;The answer is &lt;strong&gt;The Curse of Dimensionality&lt;/strong&gt;. In our simulation, we only have 50 battery charge levels and 24 hours. The DP solver checks $50 \times 24 \times 7 \text{ actions} = 8,400$ combinations. That takes milliseconds. But imagine managing a factory with 10 independent batteries, 100 machines, and stochastic weather predictions. The number of state combinations explodes into the trillions. DP completely breaks down because the math takes years to compute. &lt;/p&gt;

&lt;p&gt;Furthermore, DP requires perfect clairvoyance. In the real world, you cannot predict the exact solar output 12 hours in advance. DP is physically impossible to run in real-time, but it gives us a beautiful "Theoretical Ceiling" to grade our AI against.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. Acting Under Uncertainty: Deep Q-Networks
&lt;/h2&gt;

&lt;p&gt;In the real world, we must make decisions right now based only on what we currently know. This is where &lt;strong&gt;Deep Q-Networks (DQN)&lt;/strong&gt; shine. &lt;/p&gt;

&lt;p&gt;Unlike the DP solver, the DQN agent does not get to see the future. Instead, at every hour, it receives an 8-dimensional observation snapshot of the grid. It passes this snapshot through a neural network, which outputs the "Q-Value" (expected future profit) for 7 discrete actions: &lt;strong&gt;[-3kW, -2kW, -1kW, 0kW, +1kW, +2kW, +3kW]&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;To learn, the DQN essentially tries to approximate the exact same Bellman Equation that the DP solver uses, but without knowing the future. Here is what the core learning loop looks like in PyTorch:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch.nn.functional&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Ask the neural network what the current state is worth
&lt;/span&gt;&lt;span class="n"&gt;current_q_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;q_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;q_value_of_action_taken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_q_values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Ask the Target Network to predict the value of the NEXT state
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;no_grad&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;next_q_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;target_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;max_next_q_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_q_values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# 3. Calculate the Bellman Target (Immediate Reward + Future Value)
&lt;/span&gt;&lt;span class="n"&gt;expected_q_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gamma&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;max_next_q_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 4. Update the neural network to minimize the mathematical error
&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mse_loss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q_value_of_action_taken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_q_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Feature Engineering: The Cyclic Time Trick
&lt;/h3&gt;

&lt;p&gt;One of the most fascinating engineering challenges in RL is how you represent the "State" to the neural network. &lt;/p&gt;

&lt;p&gt;For example, how do you tell the AI what time it is? If you feed the network the raw integer &lt;code&gt;hour = 23&lt;/code&gt; (11 PM), the next step will be &lt;code&gt;hour = 0&lt;/code&gt; (Midnight). To a neural network, jumping from 23 to 0 looks like a massive, disruptive mathematical anomaly. &lt;/p&gt;

&lt;p&gt;To fix this, we use &lt;strong&gt;Cyclic Encoding&lt;/strong&gt;. We map the 24-hour clock onto a circle using sine and cosine functions:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sin_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cos_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, Hour 23 and Hour 0 are mathematically right next to each other on the circle. The network smoothly understands the passage of time without any jarring jumps.&lt;/p&gt;
&lt;h3&gt;
  
  
  Thermodynamics: Learning the "Spread"
&lt;/h3&gt;

&lt;p&gt;The environment enforces a physical reality: battery systems have a 92% round-trip efficiency. If you put 1kW into the battery, you lose 8% to heat, and only get 0.92kW out.&lt;/p&gt;

&lt;p&gt;Because of this efficiency loss, buying at 10 cents and selling at 10.5 cents actually &lt;em&gt;loses&lt;/em&gt; money. You have to write zero code telling the AI about thermodynamics. The DQN naturally figures out that it must only execute trades when the price "spread" is wide enough to cover the 8% efficiency tax.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Preventing Overfitting via Noise Injection
&lt;/h2&gt;

&lt;p&gt;If you train a neural network on the exact same 24-hour price curve for a million episodes, it doesn't actually become intelligent. It just becomes a clock. It memorizes &lt;em&gt;"charge at step 4, discharge at step 16"&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;To prevent this, the environment uses &lt;strong&gt;Stochastic Training&lt;/strong&gt;. &lt;br&gt;
During training, every single price is perturbed with 12% Gaussian noise, and the solar generation is multiplied by random "cloud factors". &lt;/p&gt;

&lt;p&gt;Because the prices are never the same twice, the AI cannot rely on the clock. It is forced to learn the &lt;em&gt;causal relationship&lt;/em&gt; between its inputs (Price Trend, Solar Output, Time to Peak) and the reward. This forces the DQN to learn a robust, generalized trading strategy that can survive the chaos of real-world markets.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. Measuring the "Intelligence Gap"
&lt;/h2&gt;

&lt;p&gt;In the interactive dashboard, we can run a &lt;strong&gt;Benchmark&lt;/strong&gt; that races three strategies on the exact same day:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Rule-Based Heuristic&lt;/strong&gt; (The human attempt)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The DQN Agent&lt;/strong&gt; (The AI acting under uncertainty)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The DP Solver&lt;/strong&gt; (The theoretical maximum with perfect foresight)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you look at the final cumulative revenue chart, the DP Solver is always at the top. The Rule-Based system is usually at the bottom. The DQN agent sits in the middle. &lt;/p&gt;

&lt;p&gt;The financial gap between the DQN and the DP Solver is literally &lt;strong&gt;the mathematical measurement of how much the AI has left to learn&lt;/strong&gt;, combined with the unavoidable cost of not knowing the future. It is a stunning visual representation of AI performance.&lt;/p&gt;


&lt;h2&gt;
  
  
  🧪 Try It Yourself
&lt;/h2&gt;

&lt;p&gt;To truly understand how this works, open up the &lt;strong&gt;&lt;a href="https://huggingface.co/spaces/Dash10107/smart-grid-energy-optimizer" rel="noopener noreferrer"&gt;Smart Grid Simulator&lt;/a&gt;&lt;/strong&gt; and run these experiments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Benchmark Race:&lt;/strong&gt; Go to the Benchmark tab. Select the &lt;code&gt;Summer Peak&lt;/code&gt; scenario. Click Run Benchmark. Look at the bar chart comparing the total revenue. How much money did the DQN leave on the table compared to the DP optimal?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Solar Impact:&lt;/strong&gt; Go to the Dispatch tab. Run the DQN on the &lt;code&gt;Summer Peak&lt;/code&gt; scenario (which has a 5kW solar array). Watch how it behaves at 1:00 PM. Then switch the scenario to &lt;code&gt;No Solar&lt;/code&gt; and run it again. You will see the agent completely change its strategy, forced to buy expensive grid power earlier in the day because it no longer has free solar power to rely on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Train the Brain:&lt;/strong&gt; Go to the Training Lab. Set the steps to &lt;code&gt;20,000&lt;/code&gt; and watch the live reward curve climb as the agent learns to ignore the immediate cost of charging the battery in favor of the massive delayed gratification of the 6:00 PM discharge.&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;Energy arbitrage perfectly encapsulates the beauty of Reinforcement Learning. It forces an AI to balance short-term costs against long-term gains in a highly volatile, unpredictable environment. By comparing the AI against the mathematical perfection of Dynamic Programming, we stop guessing if our algorithm is "good" and instead mathematically measure its intuition.&lt;/p&gt;

&lt;p&gt;This is the fourth of 12 interactive RL projects I am building to bridge the gap between academic math and real-world intuition. If this deep dive helped you understand how AI manages uncertainty, I would be incredibly grateful if you checked out the source code and dropped a star on the full repository:&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Reinforcement Learning Portfolio on GitHub&lt;/strong&gt;&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Dash10107" rel="noopener noreferrer"&gt;
        Dash10107
      &lt;/a&gt; / &lt;a href="https://github.com/Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;
        rl-portfolio
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      End-to-end reinforcement learning projects — Q-Learning, DQN, PPO, SAC, A2C, IPPO, MBRL, HMM, RLHF, and Multi-Armed Bandits — each deployed as an interactive Gradio app on Hugging Face Spaces.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Dash10107/rl-portfolio/assets/banner.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FDash10107%2Frl-portfolio%2FHEAD%2Fassets%2Fbanner.png" alt="Reinforcement Learning Portfolio Banner" width="900"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/actions/workflows/lint.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Dash10107/rl-portfolio/actions/workflows/lint.yml/badge.svg" alt="Lint Status"&gt;&lt;/a&gt;
  &lt;a href="https://huggingface.co/spaces/Dash10107" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/872f3617f8520e4eec3aa40401095b0ebe81b0e56aa6608df1c8961c013cfb30/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f48756767696e67253230466163652d5370616365732d79656c6c6f773f7374796c653d666c6174266c6f676f3d68756767696e6766616365" alt="HuggingFace Spaces"&gt;&lt;/a&gt;
  &lt;a href="https://colab.research.google.com/github/Dash10107/rl-portfolio/blob/main/open_in_colab.ipynb" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ff68bd4526bf49af34888a32dc6cdaaf15de08b2e87958ca1d75193c07fde47a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f6c61622d4f70656e2d6f72616e67653f7374796c653d666c6174266c6f676f3d676f6f676c65636f6c6162266c6f676f436f6c6f723d7768697465" alt="Open in Colab"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/codespaces/new?hide_repo_select=true&amp;amp;ref=main&amp;amp;repo=Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ee58e60d66f1cfc9ac447becf2fa8330807686c21fbaf15aedf168d9b02cc1a7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f64657370616365732d4f70656e2d626c75653f7374796c653d666c6174266c6f676f3d676974687562266c6f676f436f6c6f723d7768697465" alt="Open in Codespaces"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/blob/main/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b0347597560d4e01a4ea5c0d1afaecd0a7b68752516e32700fab4f6964606491/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75653f7374796c653d666c6174" alt="License"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/stargazers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f2552088777d97a2d8c6487ca73bb2083a86bfa7c18352bf6a23f9bcae1cebe0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f73746172732d25453225393825383525323077656c636f6d652d627269676874677265656e3f7374796c653d666c6174" alt="GitHub stars"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/issues" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/51bcf793698eb578579df6f367164058b73aad4e4411b1f592a646ec9505867a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6973737565732d77656c636f6d652d627269676874677265656e3f7374796c653d666c6174" alt="GitHub issues"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Reinforcement Learning Portfolio&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A collection of 12 end-to-end reinforcement learning projects, each deployed as an interactive web application on Hugging Face Spaces. The projects span the full range of modern RL — from the simplest tabular methods that fit on a single page, to multi-agent coordination, model-based planning, and learning from human feedback.&lt;/p&gt;

&lt;p&gt;Every project is built to be understood by someone who is new to RL. Each has its own README explaining the algorithm, the environment, and what you are looking at when you run it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New to reinforcement learning?&lt;/strong&gt; Start with these two documents before anything else:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Dash10107/rl-portfolio/./CONCEPTS.md" rel="noopener noreferrer"&gt;CONCEPTS.md&lt;/a&gt; — what RL is, the core vocabulary, and how all 12 algorithms relate to each other&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Dash10107/rl-portfolio/./GETTING_STARTED.md" rel="noopener noreferrer"&gt;GETTING_STARTED.md&lt;/a&gt; — step-by-step guide to running your first project and your first experiment&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Key Highlights&lt;/h2&gt;
&lt;/div&gt;


&lt;ul&gt;

&lt;li&gt;⚡ &lt;strong&gt;Zero-Install Interactive Demos&lt;/strong&gt;: Every project is deployed live on Hugging Face Spaces for instant testing.&lt;/li&gt;

&lt;li&gt;🎓 &lt;strong&gt;Curriculum-Based&lt;/strong&gt;…&lt;/li&gt;

&lt;/ul&gt;&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Let me know in the comments: &lt;em&gt;If you had a giant battery in your garage, would you trust an AI to trade electricity for you while you slept?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>reinforcementlearning</category>
      <category>ai</category>
      <category>energy</category>
    </item>
    <item>
      <title>Planning vs Reacting: Why A* Search Fails in Traffic, and How Deep Q-Networks Fix It</title>
      <dc:creator>Daksh Jain</dc:creator>
      <pubDate>Mon, 22 Jun 2026 14:30:00 +0000</pubDate>
      <link>https://dev.to/dash10107/planning-vs-reacting-why-a-search-fails-in-traffic-and-how-deep-q-networks-fix-it-3njk</link>
      <guid>https://dev.to/dash10107/planning-vs-reacting-why-a-search-fails-in-traffic-and-how-deep-q-networks-fix-it-3njk</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1exuq7ov17df4afgwtvu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1exuq7ov17df4afgwtvu.png" alt="Green Logistics Cover" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine you are a delivery driver navigating a busy city. Your goal is to get from Point A to Point B. If you open a standard navigation app, it will likely use a variation of a classical search algorithm—like Dijkstra's or A* (A-Star)—to draw a line showing the absolute shortest path.&lt;/p&gt;

&lt;p&gt;But any experienced driver knows a fundamental truth about city driving: &lt;strong&gt;the shortest path is rarely the cheapest path.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In modern logistics, companies aren't just optimizing for distance; they are optimizing for fuel consumption and carbon emissions. Heavy traffic zones multiply a vehicle's carbon output (and fuel cost) by a factor of four. An experienced human driver intuitively learns to take a slightly longer, winding route to completely bypass a congested downtown core. &lt;/p&gt;

&lt;p&gt;To teach an AI to develop this exact same intuition, I built a &lt;strong&gt;&lt;a href="https://huggingface.co/spaces/Dash10107/green-logistics-optimizer" rel="noopener noreferrer"&gt;Green Logistics Optimizer&lt;/a&gt;&lt;/strong&gt;. In this interactive simulation, we pit a classical mathematical planner (A*) against a Deep Reinforcement Learning agent (DQN). &lt;/p&gt;

&lt;p&gt;In this article, we're going to dive into the underlying theory of why classical planning algorithms eventually hit a wall in the real world, and how Neural Networks solve the problem by learning to &lt;em&gt;react&lt;/em&gt; rather than &lt;em&gt;plan&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Flaw of the Perfect Planner (A*)
&lt;/h2&gt;

&lt;p&gt;Let's start with the baseline. The A* algorithm is a masterpiece of computer science. It guarantees finding the optimal path between two points by using a "heuristic"—an educated guess of the remaining distance.&lt;/p&gt;

&lt;p&gt;In our Python implementation, A* evaluates the grid by adding the distance traveled so far (&lt;code&gt;g&lt;/code&gt;) to the estimated distance to the goal (&lt;code&gt;h&lt;/code&gt;). It uses a Priority Queue (a heap) to always explore the most promising path first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_astar_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;GreenCityEnv&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# A* minimises step count (distance), completely ignoring carbon.
&lt;/span&gt;    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent_pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;goal&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="c1"&gt;# The Heuristic: Manhattan distance to the goal
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;open_heap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;])]&lt;/span&gt;
    &lt;span class="n"&gt;visited&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;open_heap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heapq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;heappop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;open_heap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
        &lt;span class="c1"&gt;# ... explore neighbors, add to heap, repeat ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you look at this code, you'll notice it perfectly calculates the shortest physical distance. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So, what's the problem?&lt;/strong&gt;&lt;br&gt;
The problem is that A* is a &lt;em&gt;planner&lt;/em&gt;. It requires you to know the entire map, and the exact cost of every street, in advance. If you try to modify A* to include "traffic costs" instead of just distance, it works fine—&lt;em&gt;until the traffic changes&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;If a traffic light turns red, or an accident occurs, the weights of your map change. A* has to throw away its entire planned route and recalculate the mathematical tree from scratch. In a massive city grid with millions of nodes and dynamically shifting traffic, constantly recalculating A* becomes computationally paralyzing.&lt;/p&gt;

&lt;p&gt;We don't want an AI that mathematically calculates a billion possibilities every time a car brakes. We want an AI that looks at the traffic and just &lt;em&gt;knows&lt;/em&gt; what to do.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. The Shift: From Planning to Reacting
&lt;/h2&gt;

&lt;p&gt;This is where Reinforcement Learning enters the picture. Instead of writing an algorithm that searches a map, we drop an agent into the city and let it drive around millions of times. &lt;/p&gt;

&lt;p&gt;We use a &lt;strong&gt;Deep Q-Network (DQN)&lt;/strong&gt;. &lt;br&gt;
Unlike A*, a DQN doesn't plan a route from start to finish. Instead, it looks at a snapshot of the current state (its location and the traffic around it) and outputs a "Q-Value" for all possible immediate actions (Up, Down, Left, Right). &lt;/p&gt;

&lt;p&gt;The Q-Value represents the &lt;em&gt;expected future reward&lt;/em&gt; of taking that action, mathematically defined by the &lt;strong&gt;Bellman Optimality Equation&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q(s, a) = Reward(s, a) + γ * max Q(s_next, a_all)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because the environment is too massive for a simple lookup table, we use a Neural Network as a mathematical function approximator to estimate these Q-Values. In PyTorch, the "Brain" of the vehicle looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch.nn&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DQN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state_dim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action_dim&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Sequential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_dim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ReLU&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ReLU&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action_dim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Outputs 4 Q-Values (Up, Down, Left, Right)
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The agent simply passes its current state into this network, gets the four numbers, and picks the highest one. It's a purely reactive system.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_dqn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;GreenCityEnv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;RouteResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Load the trained Neural Network
&lt;/span&gt;    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DQN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;green_dqn_model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_dqn_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Look at the current state, instantly predict the best move
&lt;/span&gt;        &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deterministic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_rollout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_dqn_action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Notice there is no &lt;code&gt;while&lt;/code&gt; loop searching through a map. There is just a single &lt;code&gt;model.predict()&lt;/code&gt; call. Because the neural network has already "compiled" the knowledge of the city into its weights during training, querying it takes milliseconds.&lt;/p&gt;

&lt;p&gt;But training a neural network to do this requires overcoming two fascinating theoretical hurdles.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. Overcoming Catastrophic Forgetting (Experience Replay)
&lt;/h2&gt;

&lt;p&gt;Neural Networks have a fatal flaw when used in Reinforcement Learning: &lt;strong&gt;Catastrophic Forgetting&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagine our delivery driver gets stuck driving around a heavy congestion zone for 500 consecutive steps. The neural network is constantly updating its weights based on this high-traffic data. Because neural networks generalize, the math that adjusts the weights for the traffic zone will aggressively overwrite the weights that control how the car drives on an open, empty highway. By the time the agent escapes the traffic, it has literally "forgotten" how to drive in an empty street!&lt;/p&gt;
&lt;h3&gt;
  
  
  The Solution: Experience Replay
&lt;/h3&gt;

&lt;p&gt;To solve this, DQN introduced a brilliant concept called &lt;strong&gt;Experience Replay&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Instead of training the neural network on the exact sequence of events as they happen, the agent takes every single step it makes and throws it into a massive database called a Replay Buffer. &lt;/p&gt;

&lt;p&gt;When it's time to train, the network doesn't look at what just happened. Instead, it reaches into the Replay Buffer and pulls out a completely randomized mini-batch of past experiences:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReplayBuffer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;store_memory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Pull a randomized batch of 64 memories to break correlation
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It might pull one memory from a traffic jam, one memory from an empty highway, and one memory of reaching the goal. By shuffling its memories (&lt;code&gt;random.sample&lt;/code&gt;), the neural network mathematically breaks the correlation between consecutive steps. It is the artificial equivalent of human REM sleep—dreaming and shuffling past experiences to consolidate generalized, permanent knowledge.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Chasing a Moving Target (Target Networks)
&lt;/h2&gt;

&lt;p&gt;If you've ever trained a standard image classifier, you know the data has fixed labels. An image of a cat is always a cat. The network predicts "Dog", calculates the error against the fixed label "Cat", and updates.&lt;/p&gt;

&lt;p&gt;But in Deep Q-Networks, the network is trying to predict a Q-Value, and the "correct label" (the Target) is generated by the Bellman Equation we discussed earlier.&lt;/p&gt;

&lt;p&gt;Do you see the paradox? If we use a single neural network, the Target is being generated by the &lt;em&gt;exact same neural network&lt;/em&gt; that we are currently updating! If the network adjusts its weights to increase the value of moving "Up", it accidentally changes the calculated target for the next step too. It is like a dog violently chasing its own tail. The network weights will oscillate wildly and mathematically explode.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Solution: Dual Networks
&lt;/h3&gt;

&lt;p&gt;To stabilize the math, we maintain two identical Neural Networks in PyTorch:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Policy Network:&lt;/strong&gt; Actively driving the car and updating its weights every step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Target Network:&lt;/strong&gt; A completely frozen clone of the Policy Network.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When calculating the "correct label", we ask the frozen Target Network:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 1. Ask the FROZEN Target Network for the next state's value
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;no_grad&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;target_next_q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;target_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next_state&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Calculate the perfectly stable target label
&lt;/span&gt;&lt;span class="n"&gt;target_label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gamma&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;target_next_q&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# 3. Train the ACTIVE Policy Network against this frozen label
&lt;/span&gt;&lt;span class="n"&gt;current_q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;policy_network&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mse_loss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Because the Target Network is frozen (&lt;code&gt;torch.no_grad()&lt;/code&gt;), the mathematical anchor stays perfectly still, allowing the Policy Network to steadily learn. Every few hundred steps, we take the weights from the Policy Network and explicitly copy them over to the Target Network (&lt;code&gt;target_network.load_state_dict(policy_network.state_dict())&lt;/code&gt;). This simple trick is the theoretical bedrock that makes Deep RL stable.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. The Power of the Reward Function
&lt;/h2&gt;

&lt;p&gt;When you put all of this together, something magical happens. The agent's behavior is dictated entirely by a tiny reward function. In our environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Diesel Truck:&lt;/strong&gt; &lt;code&gt;Base Cost = 1.0&lt;/code&gt;. Congestion Multiplier = &lt;code&gt;4.0&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Electric Vehicle (EV):&lt;/strong&gt; &lt;code&gt;Base Cost = 0.2&lt;/code&gt;. Congestion Multiplier = &lt;code&gt;4.0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A diesel truck entering congestion suffers a massive &lt;code&gt;-4.0&lt;/code&gt; reward penalty per step. An EV suffers a &lt;code&gt;-0.8&lt;/code&gt; penalty.&lt;/p&gt;

&lt;p&gt;When you train the DQN, you don't have to write any complex &lt;code&gt;if/else&lt;/code&gt; statements telling the Diesel truck to avoid traffic. The math does it automatically. The Diesel agent will learn to take a massive detour around the city to avoid traffic, while the EV agent might decide it's mathematically cheaper to just cut straight through the congestion because its base emissions are so low.&lt;/p&gt;


&lt;h2&gt;
  
  
  🧪 Try It Yourself
&lt;/h2&gt;

&lt;p&gt;To truly understand the difference between planning and reacting, you have to see the visual traces. Open up the &lt;strong&gt;&lt;a href="https://huggingface.co/spaces/Dash10107/green-logistics-optimizer" rel="noopener noreferrer"&gt;Green Logistics Simulator&lt;/a&gt;&lt;/strong&gt; and run these experiments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Distance vs Carbon Gap:&lt;/strong&gt; Pick the &lt;code&gt;Downtown Rush (7x7)&lt;/code&gt; scenario. Select both A* and DQN. Click Deploy Fleet. You will see the A* path cut straight through the red congestion zone (because it is the shortest path). But look at the DQN path—it curves widely around the red zone to save carbon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the Analytics:&lt;/strong&gt; Go to the Analytics tab. Look at the Carbon Trace. A* reaches the goal in fewer steps, but its cumulative carbon spikes violently. DQN takes more steps, but its carbon line stays flat and low.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Train Your Own Brain:&lt;/strong&gt; Go to the Training Lab. Set the slider to &lt;code&gt;10,000&lt;/code&gt; steps and click Train. You can watch the live reward curve rise as the network populates its Replay Buffer and slowly stabilizes its Target Network.&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;Classical algorithms like A* are beautiful, but they require perfect, static knowledge of the world to plan ahead. By utilizing Deep Q-Networks, Experience Replay, and Target Networks, we can teach AI to simply look at a chaotic, shifting environment and intuitively &lt;em&gt;react&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is the third of 12 interactive RL projects I am building to bridge the gap between academic math and real-world intuition. If this deep dive helped clarify the theory inside Neural Networks, I would be incredibly grateful if you checked out the source code and dropped a star on the full repository:&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Reinforcement Learning Portfolio on GitHub&lt;/strong&gt;&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Dash10107" rel="noopener noreferrer"&gt;
        Dash10107
      &lt;/a&gt; / &lt;a href="https://github.com/Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;
        rl-portfolio
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      End-to-end reinforcement learning projects — Q-Learning, DQN, PPO, SAC, A2C, IPPO, MBRL, HMM, RLHF, and Multi-Armed Bandits — each deployed as an interactive Gradio app on Hugging Face Spaces.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Dash10107/rl-portfolio/assets/banner.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FDash10107%2Frl-portfolio%2FHEAD%2Fassets%2Fbanner.png" alt="Reinforcement Learning Portfolio Banner" width="900"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/actions/workflows/lint.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Dash10107/rl-portfolio/actions/workflows/lint.yml/badge.svg" alt="Lint Status"&gt;&lt;/a&gt;
  &lt;a href="https://huggingface.co/spaces/Dash10107" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/872f3617f8520e4eec3aa40401095b0ebe81b0e56aa6608df1c8961c013cfb30/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f48756767696e67253230466163652d5370616365732d79656c6c6f773f7374796c653d666c6174266c6f676f3d68756767696e6766616365" alt="HuggingFace Spaces"&gt;&lt;/a&gt;
  &lt;a href="https://colab.research.google.com/github/Dash10107/rl-portfolio/blob/main/open_in_colab.ipynb" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ff68bd4526bf49af34888a32dc6cdaaf15de08b2e87958ca1d75193c07fde47a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f6c61622d4f70656e2d6f72616e67653f7374796c653d666c6174266c6f676f3d676f6f676c65636f6c6162266c6f676f436f6c6f723d7768697465" alt="Open in Colab"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/codespaces/new?hide_repo_select=true&amp;amp;ref=main&amp;amp;repo=Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ee58e60d66f1cfc9ac447becf2fa8330807686c21fbaf15aedf168d9b02cc1a7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f64657370616365732d4f70656e2d626c75653f7374796c653d666c6174266c6f676f3d676974687562266c6f676f436f6c6f723d7768697465" alt="Open in Codespaces"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/blob/main/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b0347597560d4e01a4ea5c0d1afaecd0a7b68752516e32700fab4f6964606491/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75653f7374796c653d666c6174" alt="License"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/stargazers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f2552088777d97a2d8c6487ca73bb2083a86bfa7c18352bf6a23f9bcae1cebe0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f73746172732d25453225393825383525323077656c636f6d652d627269676874677265656e3f7374796c653d666c6174" alt="GitHub stars"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/issues" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/51bcf793698eb578579df6f367164058b73aad4e4411b1f592a646ec9505867a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6973737565732d77656c636f6d652d627269676874677265656e3f7374796c653d666c6174" alt="GitHub issues"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Reinforcement Learning Portfolio&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A collection of 12 end-to-end reinforcement learning projects, each deployed as an interactive web application on Hugging Face Spaces. The projects span the full range of modern RL — from the simplest tabular methods that fit on a single page, to multi-agent coordination, model-based planning, and learning from human feedback.&lt;/p&gt;

&lt;p&gt;Every project is built to be understood by someone who is new to RL. Each has its own README explaining the algorithm, the environment, and what you are looking at when you run it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New to reinforcement learning?&lt;/strong&gt; Start with these two documents before anything else:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Dash10107/rl-portfolio/./CONCEPTS.md" rel="noopener noreferrer"&gt;CONCEPTS.md&lt;/a&gt; — what RL is, the core vocabulary, and how all 12 algorithms relate to each other&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Dash10107/rl-portfolio/./GETTING_STARTED.md" rel="noopener noreferrer"&gt;GETTING_STARTED.md&lt;/a&gt; — step-by-step guide to running your first project and your first experiment&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Key Highlights&lt;/h2&gt;
&lt;/div&gt;


&lt;ul&gt;

&lt;li&gt;⚡ &lt;strong&gt;Zero-Install Interactive Demos&lt;/strong&gt;: Every project is deployed live on Hugging Face Spaces for instant testing.&lt;/li&gt;

&lt;li&gt;🎓 &lt;strong&gt;Curriculum-Based&lt;/strong&gt;…&lt;/li&gt;

&lt;/ul&gt;&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Let me know in the comments: &lt;em&gt;Which feels more "intelligent" to you—a mathematical algorithm that plans perfectly, or a neural network that guesses intuitively?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>reinforcementlearning</category>
      <category>ai</category>
      <category>logistics</category>
    </item>
    <item>
      <title>Building an AI that Solves Mazes from Scratch (Q-Learning, SARSA &amp; Monte Carlo)</title>
      <dc:creator>Daksh Jain</dc:creator>
      <pubDate>Sun, 21 Jun 2026 14:30:00 +0000</pubDate>
      <link>https://dev.to/dash10107/building-an-ai-that-solves-mazes-from-scratch-q-learning-sarsa-monte-carlo-224g</link>
      <guid>https://dev.to/dash10107/building-an-ai-that-solves-mazes-from-scratch-q-learning-sarsa-monte-carlo-224g</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7p95ma4ppiwmtg4k7i2v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7p95ma4ppiwmtg4k7i2v.png" alt="Maze AI Cover" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I first started studying Reinforcement Learning (RL), I spent weeks drowning in dense academic papers. I was tired of staring at Greek letters ($\gamma$, $\theta$, $\tau$) and abstract equations without actually &lt;em&gt;seeing&lt;/em&gt; what they meant. I didn't just want to memorize the Bellman equation; I wanted to touch the knobs, tweak the parameters, and watch the math come to life.&lt;/p&gt;

&lt;p&gt;So, I decided to build something highly visual: &lt;strong&gt;a maze solver.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The problem is classic but surprisingly profound. If you drop an AI agent into a completely dark maze, it has no map. It doesn't know where the walls are, and it certainly doesn't know where the exit is. It only knows its current location. &lt;/p&gt;

&lt;p&gt;To escape, it has to explore, make mistakes, remember what it learned, and gradually build a strategy. In this article, we'll walk through exactly how to build this from scratch using three fundamental algorithms: &lt;strong&gt;Q-Learning, SARSA, and Monte Carlo&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;If you want to play with the final result while you read, I've hosted the interactive playground here:&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://huggingface.co/spaces/Dash10107/rl_maze_solver" rel="noopener noreferrer"&gt;Live Interactive Demo: RL Maze Solver&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Setting the Rules of the World
&lt;/h2&gt;

&lt;p&gt;Before we write any algorithms, we have to define the environment in terms the AI can understand. In RL, this boils down to three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;State:&lt;/strong&gt; The agent's current location. We flatten the grid, so if the agent is in cell (row 2, col 3) on a 5x5 grid, its state is just an integer index.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Action:&lt;/strong&gt; The agent can move &lt;code&gt;Up&lt;/code&gt;, &lt;code&gt;Down&lt;/code&gt;, &lt;code&gt;Left&lt;/code&gt;, or &lt;code&gt;Right&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Reward:&lt;/strong&gt; This is how we communicate our goal to the AI.

&lt;ul&gt;
&lt;li&gt;  Step on open floor: &lt;code&gt;-1&lt;/code&gt; (We want it to find the exit &lt;em&gt;fast&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;  Bump into a wall: &lt;code&gt;-5&lt;/code&gt; (Painful. Don't do this).&lt;/li&gt;
&lt;li&gt;  Reach the goal: &lt;code&gt;+100&lt;/code&gt; (Success!).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The heavy &lt;code&gt;-5&lt;/code&gt; wall penalty is the main signal. It forces the algorithms to actually plan clean routes rather than randomly bouncing off the walls to reach the end.&lt;/p&gt;


&lt;h3&gt;
  
  
  The Dilemma: Exploration vs. Exploitation (The Restaurant Analogy)
&lt;/h3&gt;

&lt;p&gt;Before the AI can learn, it faces the oldest dilemma in Reinforcement Learning: &lt;strong&gt;Exploration vs. Exploitation&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Imagine moving to a new city. On your first night, you find a decent pizza place. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  If you &lt;strong&gt;Exploit&lt;/strong&gt; your knowledge, you will eat decent pizza every single night. You will never starve, but you will also never experience anything better.&lt;/li&gt;
&lt;li&gt;  If you &lt;strong&gt;Explore&lt;/strong&gt;, you might suffer through a terrible salad, but you might also discover a 5-star steakhouse. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To solve this, our AI uses an &lt;strong&gt;Epsilon-Greedy&lt;/strong&gt; strategy. It is the mathematical engine of curiosity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;choose_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Q_table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;epsilon&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# EXPLORE: Roll a loaded die
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;epsilon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Pick a completely random direction
&lt;/span&gt;
    &lt;span class="c1"&gt;# EXPLOIT: Use the cheat sheet
&lt;/span&gt;    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Q_table&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;# Pick the mathematically best direction
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When training starts, &lt;code&gt;epsilon&lt;/code&gt; is &lt;code&gt;1.0&lt;/code&gt; (100% exploration). The AI stumbles blindly into walls. As it learns, &lt;code&gt;epsilon&lt;/code&gt; decays to &lt;code&gt;0.01&lt;/code&gt;. It becomes a master of the maze, only exploring 1% of the time just in case there is a slightly faster shortcut it missed.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Q-Learning: The Optimistic Explorer
&lt;/h2&gt;

&lt;p&gt;Q-Learning is arguably the most famous tabular RL algorithm. The "tabular" part means the agent literally maintains a massive cheat sheet—a table with one row for every cell in the maze and one column for every possible action. &lt;/p&gt;

&lt;p&gt;Each entry in this table is a &lt;strong&gt;Q-value&lt;/strong&gt;: a numerical score of how good the agent currently believes it is to take a specific action from a specific cell.&lt;/p&gt;

&lt;p&gt;The magic of Q-Learning lies in its update rule. After every single step, the agent updates its cheat sheet using the &lt;strong&gt;Bellman Equation&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q(s, a) ← Q(s, a) + α * [ R + γ * max Q(s', a') - Q(s, a) ]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In plain English, the math says: &lt;em&gt;The value of my current move is the immediate reward I just got, plus the discounted value of the absolute best possible move from wherever I ended up next.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is the entire training loop in Python. Notice how short the core logic is:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;train_qlearning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;episodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decay&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TabularAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_states&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action_space&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;episodes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# Walk through the maze until we find the exit or time out
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_states&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choose_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Epsilon-greedy
&lt;/span&gt;            &lt;span class="n"&gt;next_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# The core Q-Learning update rule
&lt;/span&gt;            &lt;span class="c1"&gt;# Notice the np.max() - it bootstraps from the *best* possible next move
&lt;/span&gt;            &lt;span class="n"&gt;td_target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_state&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;td_target&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

            &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_state&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;

        &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decay_epsilon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Because Q-Learning uses &lt;code&gt;np.max(agent.Q[next_state])&lt;/code&gt;, it is highly optimistic. It updates its knowledge assuming it will always take the perfect path in the future. This is known as being &lt;strong&gt;off-policy&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. SARSA: The Cautious Learner
&lt;/h2&gt;

&lt;p&gt;But what if our agent's current strategy involves a lot of random exploration (which it does, thanks to our epsilon-greedy logic)? &lt;/p&gt;

&lt;p&gt;If Q-Learning is exploring randomly, it might stumble into a wall and take a &lt;code&gt;-5&lt;/code&gt; penalty. But when it updates its Q-table, it shrugs it off, pretending it would have taken the optimal path instead. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SARSA (State-Action-Reward-State-Action)&lt;/strong&gt; takes a different approach. Instead of updating toward the &lt;em&gt;maximum&lt;/em&gt; possible future value, it updates toward the value of the action it &lt;em&gt;actually&lt;/em&gt; chose next.&lt;/p&gt;

&lt;p&gt;Here is the mathematical update rule for SARSA:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q(s, a) ← Q(s, a) + α * [ R + γ * Q(s', a') - Q(s, a) ]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Notice the subtle difference from Q-Learning? There is no &lt;code&gt;max&lt;/code&gt; operator. The code difference is literally one line, but the behavioral difference is massive:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# SARSA's on-policy TD update
# We don't assume the best possible next move. 
# We evaluate the action we are ACTUALLY going to take next (a').
&lt;/span&gt;&lt;span class="n"&gt;td_target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_action&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Because SARSA learns the value of the policy it is currently running (random mistakes included), it is much more cautious. If an agent randomly stumbles into walls while navigating a tight corridor, SARSA will learn to lower the value of that corridor and might prefer a wider, safer path. Q-learning will confidently march right next to the wall, assuming it won't make a mistake.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. First-Visit Monte Carlo: Hindsight is 20/20
&lt;/h2&gt;

&lt;p&gt;Both Q-Learning and SARSA update their cheat sheets after every single step. &lt;strong&gt;Monte Carlo&lt;/strong&gt; methods take a completely different philosophical approach: &lt;em&gt;Wait until the run is over.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Instead of bootstrapping values step-by-step, a Monte Carlo agent plays out a full episode to the very end, storing every &lt;code&gt;(state, action, reward)&lt;/code&gt; in memory. Then, it works backward, calculating the actual cumulative return &lt;code&gt;G&lt;/code&gt; and updating the values based on real outcomes:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;G&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="c1"&gt;# Work backwards from the end of the episode to the start
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;episode_memory&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;G&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gamma&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Update the Q-Table toward the actual experienced return (G)
&lt;/span&gt;    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;The Pro:&lt;/strong&gt; It is completely unbiased. It uses real experienced returns rather than mathematical estimates.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Con:&lt;/strong&gt; It has incredibly high variance. A single unlucky episode where the agent gets lost can temporarily ruin the values of perfectly good states. Furthermore, because it cannot update until the episode ends, it struggles massively in large mazes where initial episodes might take thousands of steps.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  5. Visualizing the "Brain"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9i3sfq3ayypyd9ffg3gv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9i3sfq3ayypyd9ffg3gv.png" alt="Q-Learning Heatmap Progress" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When building this project, the most satisfying part wasn't writing the code; it was rendering the &lt;strong&gt;State-Value Heatmaps&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you look at the Q-table directly, it's just a giant matrix of floating-point numbers. But if you take the maximum Q-value for each cell and map it to a color gradient, you can literally watch the AI's brain light up. &lt;/p&gt;

&lt;p&gt;During training, the cells right next to the goal turn bright yellow first. Then, slowly, episode by episode, that bright path creeps backward through the maze corridors toward the start line. You are watching the reward signal propagate through the environment. It's a beautiful transition from pure random fumbling to precise mathematical pathfinding.&lt;/p&gt;


&lt;h2&gt;
  
  
  🧪 Try It Yourself
&lt;/h2&gt;

&lt;p&gt;The best way to understand these trade-offs is to see them in action. I built an interactive dashboard where you can race these algorithms against each other. &lt;/p&gt;

&lt;p&gt;Open up the &lt;strong&gt;&lt;a href="https://huggingface.co/spaces/Dash10107/rl_maze_solver" rel="noopener noreferrer"&gt;Live Playground&lt;/a&gt;&lt;/strong&gt; and try these three experiments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Cautious Agent Test:&lt;/strong&gt; Run both SARSA and Q-Learning on &lt;code&gt;Medium (9x9)&lt;/code&gt; difficulty for 400 episodes. Look at the heatmaps for the cells directly adjacent to the walls. You will see Q-Learning assigns them much higher values than SARSA does!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Size Limit:&lt;/strong&gt; Run Q-Learning for 500 episodes on &lt;code&gt;Tiny (5x5)&lt;/code&gt; and then on &lt;code&gt;Large (13x13)&lt;/code&gt;. You'll quickly see why exploration decay parameters are so critical as the state space grows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Algorithm Race:&lt;/strong&gt; Go to the "Algorithm Race" tab, select a &lt;code&gt;Large (13x13)&lt;/code&gt; DFS maze, and run all three algorithms simultaneously. Watch how Monte Carlo's convergence curve lags behind the TD methods because it has to wait for those massive initial episodes to finish.&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;The maze solver is a perfect microcosm of Reinforcement Learning. It's small enough to solve on your laptop in seconds, but complex enough to visually demonstrate the core tension in RL: exploration vs. exploitation, and on-policy vs. off-policy learning.&lt;/p&gt;

&lt;p&gt;This is just the first of 12 interactive RL projects I'm building to demystify artificial intelligence. If you found this breakdown helpful, the highest compliment you can give is checking out the source code and dropping a star on the GitHub repository:&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Reinforcement Learning Portfolio on GitHub&lt;/strong&gt;&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Dash10107" rel="noopener noreferrer"&gt;
        Dash10107
      &lt;/a&gt; / &lt;a href="https://github.com/Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;
        rl-portfolio
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      End-to-end reinforcement learning projects — Q-Learning, DQN, PPO, SAC, A2C, IPPO, MBRL, HMM, RLHF, and Multi-Armed Bandits — each deployed as an interactive Gradio app on Hugging Face Spaces.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Dash10107/rl-portfolio/assets/banner.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FDash10107%2Frl-portfolio%2FHEAD%2Fassets%2Fbanner.png" alt="Reinforcement Learning Portfolio Banner" width="900"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/actions/workflows/lint.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Dash10107/rl-portfolio/actions/workflows/lint.yml/badge.svg" alt="Lint Status"&gt;&lt;/a&gt;
  &lt;a href="https://huggingface.co/spaces/Dash10107" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/872f3617f8520e4eec3aa40401095b0ebe81b0e56aa6608df1c8961c013cfb30/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f48756767696e67253230466163652d5370616365732d79656c6c6f773f7374796c653d666c6174266c6f676f3d68756767696e6766616365" alt="HuggingFace Spaces"&gt;&lt;/a&gt;
  &lt;a href="https://colab.research.google.com/github/Dash10107/rl-portfolio/blob/main/open_in_colab.ipynb" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ff68bd4526bf49af34888a32dc6cdaaf15de08b2e87958ca1d75193c07fde47a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f6c61622d4f70656e2d6f72616e67653f7374796c653d666c6174266c6f676f3d676f6f676c65636f6c6162266c6f676f436f6c6f723d7768697465" alt="Open in Colab"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/codespaces/new?hide_repo_select=true&amp;amp;ref=main&amp;amp;repo=Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ee58e60d66f1cfc9ac447becf2fa8330807686c21fbaf15aedf168d9b02cc1a7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f64657370616365732d4f70656e2d626c75653f7374796c653d666c6174266c6f676f3d676974687562266c6f676f436f6c6f723d7768697465" alt="Open in Codespaces"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/blob/main/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b0347597560d4e01a4ea5c0d1afaecd0a7b68752516e32700fab4f6964606491/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75653f7374796c653d666c6174" alt="License"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/stargazers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f2552088777d97a2d8c6487ca73bb2083a86bfa7c18352bf6a23f9bcae1cebe0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f73746172732d25453225393825383525323077656c636f6d652d627269676874677265656e3f7374796c653d666c6174" alt="GitHub stars"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/issues" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/51bcf793698eb578579df6f367164058b73aad4e4411b1f592a646ec9505867a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6973737565732d77656c636f6d652d627269676874677265656e3f7374796c653d666c6174" alt="GitHub issues"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Reinforcement Learning Portfolio&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A collection of 12 end-to-end reinforcement learning projects, each deployed as an interactive web application on Hugging Face Spaces. The projects span the full range of modern RL — from the simplest tabular methods that fit on a single page, to multi-agent coordination, model-based planning, and learning from human feedback.&lt;/p&gt;

&lt;p&gt;Every project is built to be understood by someone who is new to RL. Each has its own README explaining the algorithm, the environment, and what you are looking at when you run it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New to reinforcement learning?&lt;/strong&gt; Start with these two documents before anything else:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Dash10107/rl-portfolio/./CONCEPTS.md" rel="noopener noreferrer"&gt;CONCEPTS.md&lt;/a&gt; — what RL is, the core vocabulary, and how all 12 algorithms relate to each other&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Dash10107/rl-portfolio/./GETTING_STARTED.md" rel="noopener noreferrer"&gt;GETTING_STARTED.md&lt;/a&gt; — step-by-step guide to running your first project and your first experiment&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Key Highlights&lt;/h2&gt;
&lt;/div&gt;


&lt;ul&gt;

&lt;li&gt;⚡ &lt;strong&gt;Zero-Install Interactive Demos&lt;/strong&gt;: Every project is deployed live on Hugging Face Spaces for instant testing.&lt;/li&gt;

&lt;li&gt;🎓 &lt;strong&gt;Curriculum-Based&lt;/strong&gt;…&lt;/li&gt;

&lt;/ul&gt;&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Let me know in the comments: &lt;em&gt;Have you ever tried to implement Q-learning? What was the hardest part to wrap your head around?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>reinforcementlearning</category>
      <category>ai</category>
    </item>
    <item>
      <title>Beyond A/B Testing: How AI Handles Ad Fatigue and Revenue Optimization</title>
      <dc:creator>Daksh Jain</dc:creator>
      <pubDate>Sun, 21 Jun 2026 03:48:23 +0000</pubDate>
      <link>https://dev.to/dash10107/beyond-ab-testing-how-ai-handles-ad-fatigue-and-revenue-optimization-1pe</link>
      <guid>https://dev.to/dash10107/beyond-ab-testing-how-ai-handles-ad-fatigue-and-revenue-optimization-1pe</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feesjgbvsuujob5ypzz62.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feesjgbvsuujob5ypzz62.png" alt="Bandit Optimizer Cover" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you read any standard tutorial on Multi-Armed Bandits, you will hear the exact same story: &lt;em&gt;A/B testing is inefficient because it wastes 50% of your traffic on a losing variation. Instead, use a Bandit algorithm to dynamically shift traffic to the winner.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;They usually introduce three algorithms: Epsilon-Greedy, UCB1, and Thompson Sampling. &lt;/p&gt;

&lt;p&gt;But almost all of these tutorials make two fatal, mathematically dangerous assumptions that will completely break your algorithms in the real world:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;They assume a click is just a click.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;They assume the world never changes.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I built a custom &lt;a href="https://huggingface.co/spaces/Dash10107/mab-banner-optimizer" rel="noopener noreferrer"&gt;Interactive Bandit Simulator&lt;/a&gt; to visualize exactly why these assumptions fail, and how advanced Reinforcement Learning actually handles the chaos of the real world.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Casino Analogy (Where the name comes from)
&lt;/h3&gt;

&lt;p&gt;Imagine walking into a casino and facing a row of slot machines (known as "One-Armed Bandits"). You know that some machines have a higher payout rate than others, but you don't know which ones. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Do you pull the lever on the machine that paid out $10 on your first try (Exploitation)? &lt;/li&gt;
&lt;li&gt;  Or do you put coins into the unknown machines just in case one of them is the secret jackpot machine (Exploration)? &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the &lt;strong&gt;Multi-Armed Bandit&lt;/strong&gt; problem. In digital marketing, the slot machines are your ad banners, and the pulls are your website visitors.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Revenue Trap (CTR vs EV)
&lt;/h2&gt;

&lt;p&gt;Most standard bandit implementations optimize purely for Click-Through Rate (CTR). A conversion equals &lt;code&gt;1&lt;/code&gt;, a failure equals &lt;code&gt;0&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Imagine you are running a SaaS pricing page with three different "Call to Action" (CTA) buttons.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;"Start Free Trial"&lt;/strong&gt;: Gets a massive 15% CTR. (Expected lifetime value: $120)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;"Request Demo"&lt;/strong&gt;: Gets a 7% CTR. (Expected value: $180)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;"Buy Now"&lt;/strong&gt;: Gets a tiny 3% CTR. (Expected value: $300)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your algorithm only looks at clicks, it will confidently route 100% of your traffic to the "Free Trial" button. It thinks it's winning, but you are actively bleeding potential revenue.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix: Expected Value (EV)
&lt;/h3&gt;

&lt;p&gt;To fix this, our environment must multiply the conversion by the actual revenue. In our simulator's environment code, the reward function looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arm_idx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;arm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;arm_idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Did they click based on the hidden True CTR?
&lt;/span&gt;    &lt;span class="n"&gt;converted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;arm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;true_ctr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Multiply by the actual monetary value of that conversion!
&lt;/span&gt;    &lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;revenue&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;converted&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;converted&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When you run &lt;strong&gt;Thompson Sampling&lt;/strong&gt; on the "SaaS Pricing Page" scenario in the live dashboard, you will watch something incredible happen. Initially, the algorithm gets flooded with clicks for the "Free Trial" banner. But over time, the rare—but massive—$300 payouts from the "Buy Now" button cause its revenue-weighted probability distribution to shift all the way to the right. The AI learns to ignore the high click rate and chases the money.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Standard Algorithms and Their Deep Flaws
&lt;/h2&gt;

&lt;p&gt;Let's look at how standard algorithms attempt to solve this exploration-exploitation trade-off, and where they break.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Naive Explorer: Epsilon-Greedy
&lt;/h3&gt;

&lt;p&gt;It rolls a loaded die. 90% of the time, it exploits the best banner. 10% of the time ($\epsilon$), it picks at random. &lt;br&gt;
&lt;strong&gt;The Flaw&lt;/strong&gt;: It never stops exploring. Even after 100,000 impressions when it is absolutely certain which banner is best, it still wastes 10% of its traffic on losers. &lt;/p&gt;

&lt;p&gt;We can fix this mathematically with &lt;strong&gt;Decaying Epsilon-Greedy&lt;/strong&gt;. Instead of a fixed 10%, we calculate epsilon dynamically:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ε = decay / √(t)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;choose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Epsilon shrinks as the square root of time!
&lt;/span&gt;    &lt;span class="n"&gt;eps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decay&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;eps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_arms&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This forces heavy exploration early, but gracefully decays exploration to zero as time approaches infinity.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Genius of the Logarithm: UCB1
&lt;/h3&gt;

&lt;p&gt;Upper Confidence Bound (UCB1) doesn't use randomness. It mathematically calculates the maximum potential value of a banner using this formula:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UCB = Expected_Reward + c * √( ln(t) / pulls )&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;choose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="c1"&gt;# The UCB bonus formula
&lt;/span&gt;    &lt;span class="n"&gt;bonus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bonus&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Why is &lt;code&gt;np.log(t)&lt;/code&gt; brilliant? As time (&lt;code&gt;t&lt;/code&gt;) moves forward, the numerator grows. But a logarithm grows &lt;em&gt;incredibly slowly&lt;/em&gt;. This guarantees that if a banner hasn't been pulled in a long time (the denominator &lt;code&gt;self.counts&lt;/code&gt; stays small), its bonus will eventually creep high enough to force the algorithm to test it again. No banner is ever permanently starved of attention.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. The Static World Fallacy (Ad Fatigue)
&lt;/h2&gt;

&lt;p&gt;Here is the second, much larger trap. UCB1 and Epsilon-Greedy assume that if a banner has a 10% CTR on Day 1, it will have a 10% CTR on Day 100. &lt;/p&gt;

&lt;p&gt;In digital marketing, this is completely false. Users get "Ad Fatigue". A brilliant new banner design will get high clicks for a week, and then slowly decay as users go blind to it. We call this a &lt;strong&gt;Non-Stationary Environment&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;If you run standard UCB1 in a non-stationary environment, it fails spectacularly. Why? Because UCB1 remembers &lt;em&gt;everything&lt;/em&gt;. If Banner A was amazing for the first 10,000 impressions, UCB1 builds an incredibly strong mathematical conviction that Banner A is the best. If Banner A suddenly goes blind and its CTR drops to zero, UCB1 is so weighed down by its historical data that it might take another 10,000 failed impressions before it finally changes its mind.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Advanced Solutions: Bayes and Gradients
&lt;/h2&gt;

&lt;p&gt;How do modern AI systems handle shifting trends?&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution A: The Bayesian Master (Thompson Sampling)
&lt;/h3&gt;

&lt;p&gt;Instead of tracking a single "average", Thompson Sampling tracks a &lt;strong&gt;Beta Distribution&lt;/strong&gt; of belief. It calculates the exact probability of a banner's true success rate using Bayes' Theorem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P(True_CTR | Data) = Beta(α + clicks, β + ignores)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;choose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Sample from the Beta distribution of each arm
&lt;/span&gt;    &lt;span class="n"&gt;samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;alpha&lt;/code&gt; is the number of successes.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;beta&lt;/code&gt; is the number of failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you open the &lt;strong&gt;Learner Mode&lt;/strong&gt; in the dashboard, you can watch these Beta Distribution curves physically morph. When a banner is new, the curve is flat and wide (high uncertainty). When it gets clicks, it shifts right and becomes a tight spike. &lt;/p&gt;

&lt;p&gt;If the environment drifts (Ad Fatigue), a once-great banner starts accumulating failures. Its &lt;code&gt;beta&lt;/code&gt; parameter grows, the curve widens and shifts left, and the AI naturally begins exploring other banners again. It gracefully rides the changing waves.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution B: Gradient Bandits (The Deep RL Bridge)
&lt;/h3&gt;

&lt;p&gt;Instead of trying to estimate CTR or Revenue at all, what if the agent just learns a &lt;em&gt;relative preference&lt;/em&gt;? &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gradient Bandits&lt;/strong&gt; maintain a preference score &lt;code&gt;H&lt;/code&gt; for each banner. It passes these scores through a &lt;code&gt;Softmax&lt;/code&gt; function to convert them into probabilities (just like a neural network). The update rule calculates the gradient of the reward against a rolling baseline:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;H_chosen = H_chosen + α * (Reward - Baseline) * (1 - Prob_chosen)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reward&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;probs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_softmax&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Stochastic gradient ascent on expected reward
&lt;/span&gt;    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alpha_lr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;baseline&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;probs&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;arm&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alpha_lr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;baseline&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Update running baseline
&lt;/span&gt;    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;baseline&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reward&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;baseline&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If a banner performs better than the historical baseline, its preference gets a boost. If it performs worse, it gets penalized. This relative updating makes it incredibly robust to non-stationary environments. &lt;em&gt;Fun fact: This exact math is the foundational stepping stone to Policy Gradient algorithms like PPO, which are used to train Large Language Models!&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🧪 Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Don't just read about it. Open up the &lt;strong&gt;&lt;a href="https://huggingface.co/spaces/Dash10107/mab-banner-optimizer" rel="noopener noreferrer"&gt;Live Dashboard&lt;/a&gt;&lt;/strong&gt; and run these exact experiments to see the AI break and recover:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Revenue Trap:&lt;/strong&gt; Go to the Face-Off tab. Select the &lt;code&gt;SaaS Pricing Page&lt;/code&gt; scenario. Race Epsilon-Greedy against Thompson Sampling. Watch how Thompson Sampling figures out that the lowest-clicked banner is actually the most profitable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Ad Fatigue Test:&lt;/strong&gt; Go to Advanced Settings and set the &lt;code&gt;CTR Drift&lt;/code&gt; to &lt;code&gt;0.008&lt;/code&gt;. Run UCB1 against Gradient Bandits. Watch how UCB1 stubbornly clings to early winners long after they have decayed, while Gradient Bandits smoothly adapt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step-by-Step Bayesian Learning:&lt;/strong&gt; Go to the Learner Mode, pick the &lt;code&gt;E-Commerce Sale&lt;/code&gt; scenario, and click "Next Impression" manually. Watch the mathematical confidence curves physically narrow in real time.&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;Bandit algorithms are the secret engine behind almost every digital platform you use today—from Netflix thumbnails to Amazon recommendations. But building them for the real world requires moving beyond simple coin-flips and addressing revenue weighting and non-stationarity.&lt;/p&gt;

&lt;p&gt;This is the second of 12 interactive RL projects I am building to bridge the gap between academic math and real-world intuition. If this helped things click for you, I would be incredibly grateful if you checked out the source code and dropped a star on the full repository:&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Reinforcement Learning Portfolio on GitHub&lt;/strong&gt;&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Dash10107" rel="noopener noreferrer"&gt;
        Dash10107
      &lt;/a&gt; / &lt;a href="https://github.com/Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;
        rl-portfolio
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      End-to-end reinforcement learning projects — Q-Learning, DQN, PPO, SAC, A2C, IPPO, MBRL, HMM, RLHF, and Multi-Armed Bandits — each deployed as an interactive Gradio app on Hugging Face Spaces.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Dash10107/rl-portfolio/assets/banner.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FDash10107%2Frl-portfolio%2FHEAD%2Fassets%2Fbanner.png" alt="Reinforcement Learning Portfolio Banner" width="900"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/actions/workflows/lint.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Dash10107/rl-portfolio/actions/workflows/lint.yml/badge.svg" alt="Lint Status"&gt;&lt;/a&gt;
  &lt;a href="https://huggingface.co/spaces/Dash10107" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/872f3617f8520e4eec3aa40401095b0ebe81b0e56aa6608df1c8961c013cfb30/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f48756767696e67253230466163652d5370616365732d79656c6c6f773f7374796c653d666c6174266c6f676f3d68756767696e6766616365" alt="HuggingFace Spaces"&gt;&lt;/a&gt;
  &lt;a href="https://colab.research.google.com/github/Dash10107/rl-portfolio/blob/main/open_in_colab.ipynb" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ff68bd4526bf49af34888a32dc6cdaaf15de08b2e87958ca1d75193c07fde47a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f6c61622d4f70656e2d6f72616e67653f7374796c653d666c6174266c6f676f3d676f6f676c65636f6c6162266c6f676f436f6c6f723d7768697465" alt="Open in Colab"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/codespaces/new?hide_repo_select=true&amp;amp;ref=main&amp;amp;repo=Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ee58e60d66f1cfc9ac447becf2fa8330807686c21fbaf15aedf168d9b02cc1a7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f64657370616365732d4f70656e2d626c75653f7374796c653d666c6174266c6f676f3d676974687562266c6f676f436f6c6f723d7768697465" alt="Open in Codespaces"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/blob/main/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b0347597560d4e01a4ea5c0d1afaecd0a7b68752516e32700fab4f6964606491/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75653f7374796c653d666c6174" alt="License"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/stargazers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f2552088777d97a2d8c6487ca73bb2083a86bfa7c18352bf6a23f9bcae1cebe0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f73746172732d25453225393825383525323077656c636f6d652d627269676874677265656e3f7374796c653d666c6174" alt="GitHub stars"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Dash10107/rl-portfolio/issues" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/51bcf793698eb578579df6f367164058b73aad4e4411b1f592a646ec9505867a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6973737565732d77656c636f6d652d627269676874677265656e3f7374796c653d666c6174" alt="GitHub issues"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Reinforcement Learning Portfolio&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A collection of 12 end-to-end reinforcement learning projects, each deployed as an interactive web application on Hugging Face Spaces. The projects span the full range of modern RL — from the simplest tabular methods that fit on a single page, to multi-agent coordination, model-based planning, and learning from human feedback.&lt;/p&gt;

&lt;p&gt;Every project is built to be understood by someone who is new to RL. Each has its own README explaining the algorithm, the environment, and what you are looking at when you run it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New to reinforcement learning?&lt;/strong&gt; Start with these two documents before anything else:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Dash10107/rl-portfolio/./CONCEPTS.md" rel="noopener noreferrer"&gt;CONCEPTS.md&lt;/a&gt; — what RL is, the core vocabulary, and how all 12 algorithms relate to each other&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Dash10107/rl-portfolio/./GETTING_STARTED.md" rel="noopener noreferrer"&gt;GETTING_STARTED.md&lt;/a&gt; — step-by-step guide to running your first project and your first experiment&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Key Highlights&lt;/h2&gt;
&lt;/div&gt;


&lt;ul&gt;

&lt;li&gt;⚡ &lt;strong&gt;Zero-Install Interactive Demos&lt;/strong&gt;: Every project is deployed live on Hugging Face Spaces for instant testing.&lt;/li&gt;

&lt;li&gt;🎓 &lt;strong&gt;Curriculum-Based&lt;/strong&gt;…&lt;/li&gt;

&lt;/ul&gt;&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Dash10107/rl-portfolio" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Let me know in the comments: &lt;em&gt;What's the weirdest A/B test result you've ever seen where the data totally contradicted your intuition?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>reinforcementlearning</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
