Step 1: Choose an action
Choose an action
Reinforcement learning has no labelled answers. The agent learns by acting, seeing the reward, and adjusting. It faces a constant trade-off: try something new (explore) or take the best action it knows (exploit). Epsilon-greedy is the simplest way to balance the two.
gridworld.py gives you the environment: GridWorld() with reset(), functional step(s, a) returning
(next_state, reward, done), states() and manhattan(s). Actions are 0=up, 1=down, 2=left, 3=right. You
write the agent in agent.py, where new_Q() gives a table mapping each state to four action-values.
Write epsilon_greedy(Q, s, eps, rng): with probability eps return a uniformly random action using
rng; otherwise return the highest-valued action for s, breaking ties toward the lower index.
agent.py, the file you edit61 lines
"""Tabular Q-learning. You choose actions, apply the temporal-difference update, train over episodes,
roll out the greedy policy, and finally shape the reward so learning speeds up."""
import random
from collections import defaultdict
import gridworld as G
def new_Q():
"""A table mapping each state to four action-values, all starting at 0."""
return defaultdict(lambda: [0.0, 0.0, 0.0, 0.0])
# ---------- Step 1: choose an action ----------
def epsilon_greedy(Q, s, eps, rng):
"""With probability eps pick a uniformly random action (explore); otherwise pick the highest-valued
action for s (exploit), breaking ties toward the lower action index."""
# TODO (Step 1): with prob eps return rng.randrange(4); else the argmax of Q[s],
# ties to the lower index.
raise NotImplementedError("Step 1: write epsilon_greedy()")
# ---------- Step 2: the temporal-difference update ----------
def td_update(Q, s, a, r, s2, done, alpha, gamma):
"""Move Q[s][a] toward the observed target. The target is r at a terminal step, otherwise r plus gamma
times the best value available in s2. Update in place."""
raise NotImplementedError("td_update() arrives in Step 2")
# ---------- Step 3: train ----------
def train(env, episodes=400, alpha=0.5, gamma=0.95, eps0=1.0, eps_min=0.05, seed=0,
max_steps=200, reward_fn=None, decay_episodes=250):
"""Run Q-learning for a number of episodes with epsilon decaying linearly from eps0 to eps_min. Return
(Q, returns) where returns[i] is the TRUE (unshaped) total reward of episode i. reward_fn, if given,
transforms the reward used for the update only: reward_fn(env, r, s, s2)."""
raise NotImplementedError("train() arrives in Step 3")
# ---------- Step 4: run the greedy policy ----------
def rollout(env, Q, max_steps=100):
"""Follow the greedy policy from the start with no exploration. Return
{"reached_goal", "steps", "total_reward"}."""
raise NotImplementedError("rollout() arrives in Step 4")
# ---------- Step 5: shape the reward ----------
def shaped_reward(env, r, s, s2, gamma=0.95):
"""Potential-based shaping: add gamma * phi(s2) - phi(s) to r, with phi(s) = -manhattan distance to the
goal. This gives a per-step gradient toward the goal without changing which policy is optimal."""
raise NotImplementedError("shaped_reward() arrives in Step 5")
def first_solved(env, episodes=400, **kw):
"""Train and return the first episode index after which the greedy policy reaches the goal, or None."""
Q, _ = train(env, episodes=episodes, **kw)
return rollout(env, Q)["reached_goal"]gridworld.pytry_it.py