"""Q-learning agent.

This file contains starter code for implementing a Q-learning agent.
You need to fill in two functions:
  - choose_action: select an action using an epsilon-greedy policy
  - update_q: update the Q-table using the Bellman equation

Both functions are written here with no reference to BabySnake or any
particular game — they work on any (state, action) Q-table, given a list of
possible actions. train_babysnake.py is the module that wires these functions
up to BabySnake specifically.

Before training, get your implementation passing the tests in
test_q_learning.py:

    python test_q_learning.py

Errors caught here are much easier to track down than errors discovered
during training. Once the tests pass, run:

    python train_babysnake.py
"""

import random


def choose_action(q_table, state, actions, epsilon):
    """Choose an action using an epsilon-greedy policy.

    With probability `epsilon`, return a random action from `actions`.
    Otherwise, return the action with the highest Q-value in `q_table`
    for the given `state`. If a (state, action) pair has not been seen
    before, treat its Q-value as 0.

    Arguments:
        q_table (dict): Maps (state, action) -> Q-value.
        state: The current state.
        actions (list): The actions available to choose from.
        epsilon (float): Exploration rate, between 0.0 and 1.0.

    Returns:
        One of the values in `actions`.

    Hint: random.random() returns a float in [0.0, 1.0).
          random.choice(actions) returns a random action.
          q_table.get(key, default) is handy for missing entries.
    """
    raise NotImplementedError("Fill in choose_action")


def update_q(q_table, state, action, reward, next_state, actions, alpha, gamma):
    """Update one entry of the Q-table using the Bellman equation.

    The update rule is:

        Q(s, a) <- Q(s, a) + alpha * (r + gamma * max_a' Q(s', a') - Q(s, a))

    where:
        s, a       — the state we were in and the action we took
        r          — the reward we received
        s'         — the state we ended up in
        max_a' ... — the best possible Q-value from the new state

    Arguments:
        q_table (dict): Maps (state, action) -> Q-value (modified in place).
        state: The state before the action.
        action: The action taken.
        reward (float): The reward received.
        next_state: The state after the action.
        actions (list): The actions available from `next_state`.
        alpha (float): Learning rate (how much to update).
        gamma (float): Discount factor (how much to value future rewards).

    Returns:
        None — modifies q_table in place.

    Hint: Q-values for unseen (state, action) pairs start at 0.
    """
    raise NotImplementedError("Fill in update_q")
