Um MDP é definido como uma tupla (S, A, P, R, γ), onde:
No nosso caso:
# Representação do estado no código
board = np.zeros((3, 3), dtype=int)
# 0: vazio
# 1: X (agente)
# -1: O (oponente)
O Q-Learning atualiza os valores Q usando a fórmula:
Q(s,a) ← Q(s,a) + α[R + γ max Q(s',a') - Q(s,a)]
Onde:
# Implementação da atualização Q
def update(self, state, action, reward, next_state, next_valid_moves):
state_key = self._get_state_key(state)
action_key = str(action)
next_state_key = self._get_state_key(next_state)
next_max_value = max([self.q_table[next_state_key][str(move)]
for move in next_valid_moves]) if next_valid_moves else 0
current_q = self.q_table[state_key][action_key]
self.q_table[state_key][action_key] = current_q + self.alpha * (
reward + self.gamma * next_max_value - current_q
)
Balanceia exploração e exploitação usando probabilidade ε:
def get_action(self, state, valid_moves):
if random.random() < self.epsilon: # Exploração
return random.choice(valid_moves)
return self._get_best_action(state, valid_moves) # Exploitação
Probabilidade de explorar decresce com o tempo: P(explorar) = ε P(exploitar) = 1 - ε
V(s) = max_a Q(s,a)
Representa o valor esperado de longo prazo começando no estado s.
Q(s,a) = R(s,a) + γ Σ P(s'|s,a) max_a' Q(s',a')
No nosso caso determinístico: Q(s,a) = R(s,a) + γ max_a' Q(s',a')
A equação de Bellman estabelece a relação recursiva entre valores de estados: V(s) = max_a [R(s,a) + γ Σ P(s'|s,a)V(s')]
O estado é codificado como uma matriz 3x3:
[[0, 1, -1],
[0, 0, 1],
[-1, 0, 0]]
if self._check_winner() == self.current_player:
reward = 1
done = True
elif self._check_winner() is not None:
reward = -1
done = True
elif len(self.get_valid_moves()) == 0:
done = True
reward = 0
A transição de estados é determinística: P(s'|s,a) = 1 para o próximo estado resultante da ação P(s'|s,a) = 0 para todos os outros estados
O Q-Learning converge para a política ótima sob as seguintes condições:
Taxa de Vitória
win_rate = wins / total_games
Convergência dos Valores Q
q_value_change = |Q_new(s,a) - Q_old(s,a)|
Entropia da Política
policy_entropy = -Σ π(a|s) log π(a|s)
Armazenamento Eficiente
self.q_table = defaultdict(lambda: defaultdict(float))
Simetrias do Tabuleiro
Velocidade vs Precisão
Exploração vs Exploitação
plt.figure(figsize=(10, 6))
plt.plot(range(len(win_rates)), win_rates, 'b-')
plt.xlabel('Episódios (x100)')
plt.ylabel('Taxa de Vitória')
plt.title('Progresso do Treinamento do Agente')
plt.grid(True)
plt.show()
Visualização dos valores Q para cada posição:
Estado Atual:
[[ 0.8 0.4 0.6]
[ 0.3 0.9 0.2]
[ 0.5 0.7 0.4]]