SARSA

"State - Action - Reward - State - Action"

1. Apa bedanya dengan Q-Learning?

Q-Learning (Off-Policy): Agent itu nekat. Saat belajar, dia mengasumsikan di masa depan dia AKAN MELAKUKAN YANG TERBAIK (Greedy), padahal kenyataannya dia mungkin masih suka eksplorasi (ngawur).
"Ah, nanti aku pasti lewat jalan pintas, jadi aman." (Padahal nanti kepeleset).

SARSA (On-Policy): Agent itu realistis (atau penakut). Dia belajar dari APA YANG BENAR-BENAR DIA LAKUKAN selanjutnya, termasuk kebodohannya sendiri saat eksplorasi.
"Aku tau aku kadang ceroboh, jadi mending jangan lewat pinggir jurang."

2. Visualisasi: Cliff Walking (Jalan Jurang)

Safe
Safe
Safe
Safe
Safe
Safe
Risk
Risk
Risk
Risk
Risk
Risk
Start
JURANG
JURANG
JURANG
JURANG
GOAL
Q-Learning (Garis Merah)

Dia lewat TEPAT DI BIBIR JURANG. Karena itu jalan tercepat. Dia yakin gak bakal jatuh (Optimis).

SARSA (Garis Hijau)

Dia lewat JAUH DARI JURANG (Jalan memutar). Karena dia tau, kalau dia lewat pinggir jurang dan 'kepeleset' (eksplorasi acak), dia bakal mati. Jadi dia cari aman.

3. Rumus Update

$$ Q(S,A) \leftarrow Q(S,A) + \alpha [ R + \gamma \cdot \mathbf{Q(S', A')} - Q(S,A) ] $$
Perhatikan Bedanya:
Q-Learning pakai $\max Q(S', a')$ (Yang terbaik secara teori).
SARSA pakai $Q(S', A')$ (Aksi $A'$ yang BENAR-BENAR diambil selanjutnya).

4. Implementasi Python

# Bedanya cuma di loop training
# Kita harus pilih action selanjutnya DULUAN sebelum update

# ... (Inisialisasi sama) ...

for episode in range(1000):
    state, _ = env.reset()
    
    # [SARSA] Pilih aksi di awal
    if random.uniform(0, 1) < epsilon:
        action = env.action_space.sample()
    else:
        action = np.argmax(q_table[state])
        
    done = False
    while not done:
        # Lakukan aksi
        next_state, reward, terminated, truncated, _ = env.step(action)
        
        # [SARSA] Pilih aksi selanjutnya SEKARANG JUGA (Next Action)
        if random.uniform(0, 1) < epsilon:
            next_action = env.action_space.sample()
        else:
            next_action = np.argmax(q_table[next_state])
            
        # Update pakai next_action (Bukan Max!)
        old_val = q_table[state, action]
        next_val = q_table[next_state, next_action] # <-- Kuncinya disini
        
        q_table[state, action] = old_val + alpha * (reward + gamma * next_val - old_val)
        
        state = next_state
        action = next_action # Oper ke iterasi berikutnya
        
        done = terminated or truncated