aW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCB0b3JjaAppbXBvcnQgdG9yY2gubm4gYXMgbm4KaW1wb3J0IHRvcmNoLm9wdGltIGFzIG9wdGltCgojID09PT09PSBEJmVhY3V0ZTtmaW5pdGlvbiBkZSBsJ2FnZW50ID09PT09PQpjbGFzcyBBZ2VudDoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBuX2pvaW50cz0yKToKICAgICAgICBzZWxmLm5fam9pbnRzID0gbl9qb2ludHMKICAgICAgICBzZWxmLmFuZ2xlcyA9IG5wLnplcm9zKG5fam9pbnRzKSAgICMgYW5nbGUgZGUgY2hhcXVlIGFydGljdWxhdGlvbgogICAgICAgIHNlbGYudmVsb2NpdGllcyA9IG5wLnplcm9zKG5fam9pbnRzKQogICAgICAgIHNlbGYucG9zaXRpb24gPSAwLjAgICMgcG9zaXRpb24gaG9yaXpvbnRhbGUgZHUgY29ycHMKCiAgICBkZWYgc3RlcChzZWxmLCB0b3JxdWVzLCBkdD0wLjEpOgogICAgICAgICMgcGh5c2lxdWUgc2ltcGxpZmkmZWFjdXRlO2UKICAgICAgICBzZWxmLnZlbG9jaXRpZXMgKz0gdG9ycXVlcyAqIGR0CiAgICAgICAgc2VsZi5hbmdsZXMgKz0gc2VsZi52ZWxvY2l0aWVzICogZHQKICAgICAgICBzZWxmLnBvc2l0aW9uICs9IG5wLnN1bShucC5zaW4oc2VsZi5hbmdsZXMpKSAqIGR0ICAjIGFwcHJveGltYXRpb24gZHUgZCZlYWN1dGU7cGxhY2VtZW50CgojID09PT09PSBSJmVhY3V0ZTtzZWF1IG5ldXJvbmFsIHNpbXBsZSA9PT09PT0KY2xhc3MgUG9saWN5KG5uLk1vZHVsZSk6CiAgICBkZWYgX19pbml0X18oc2VsZiwgbl9pbnB1dHMsIG5fb3V0cHV0cyk6CiAgICAgICAgc3VwZXIoKS5fX2luaXRfXygpCiAgICAgICAgc2VsZi5mYyA9IG5uLlNlcXVlbnRpYWwoCiAgICAgICAgICAgIG5uLkxpbmVhcihuX2lucHV0cywgNjQpLAogICAgICAgICAgICBubi5UYW5oKCksCiAgICAgICAgICAgIG5uLkxpbmVhcig2NCwgbl9vdXRwdXRzKSwKICAgICAgICAgICAgbm4uVGFuaCgpCiAgICAgICAgKQogICAgZGVmIGZvcndhcmQoc2VsZiwgeCk6CiAgICAgICAgcmV0dXJuIHNlbGYuZmMoeCkKCiMgPT09PT09IFNpbXVsYXRpb24gbXVsdGktYWdlbnRzID09PT09PQpuX2FnZW50cyA9IDUKYWdlbnRzID0gW0FnZW50KCkgZm9yIF8gaW4gcmFuZ2Uobl9hZ2VudHMpXQpwb2xpY3kgPSBQb2xpY3kobl9pbnB1dHM9NCwgbl9vdXRwdXRzPTIpICAjIDIgam9pbnRzICsgMiB2aXRlc3NlcwpvcHRpbWl6ZXIgPSBvcHRpbS5BZGFtKHBvbGljeS5wYXJhbWV0ZXJzKCksIGxyPTAuMDEpCgpmb3IgZXBpc29kZSBpbiByYW5nZSgxMDAwKToKICAgIHRvdGFsX3Jld2FyZCA9IDAKICAgIGZvciBhZ2VudCBpbiBhZ2VudHM6CiAgICAgICAgc3RhdGUgPSB0b3JjaC50ZW5zb3IobnAuY29uY2F0ZW5hdGUoW2FnZW50LmFuZ2xlcywgYWdlbnQudmVsb2NpdGllc10pLCBkdHlwZT10b3JjaC5mbG9hdDMyKQogICAgICAgIGFjdGlvbiA9IHBvbGljeShzdGF0ZSkKICAgICAgICBhZ2VudC5zdGVwKGFjdGlvbi5kZXRhY2goKS5udW1weSgpKQogICAgICAgIHJld2FyZCA9IGFnZW50LnBvc2l0aW9uICAjIHBsdXMgaWwgYXZhbmNlLCBtaWV1eCBjJ2VzdAogICAgICAgIHRvdGFsX3Jld2FyZCArPSByZXdhcmQKCiAgICAjIEJhY2twcm9wIHNpbXBsZSAocG91ciBpbGx1c3RyZXIpCiAgICBsb3NzID0gLXRvcmNoLnRlbnNvcih0b3RhbF9yZXdhcmQgLyBuX2FnZW50cywgcmVxdWlyZXNfZ3JhZD1UcnVlKQogICAgb3B0aW1pemVyLnplcm9fZ3JhZCgpCiAgICBsb3NzLmJhY2t3YXJkKCkKICAgIG9wdGltaXplci5zdGVwKCkKCiAgICBpZiBlcGlzb2RlICUgNTAgPT0gMDoKICAgICAgICBwcmludChmJnF1b3Q7RXBpc29kZSB7ZXBpc29kZX0sIG1veWVubmUgZGlzdGFuY2U6IHt0b3RhbF9yZXdhcmQvbl9hZ2VudHM6LjJmfSZxdW90Oyk=
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
# ====== Définition de l'agent ======
class Agent:
def __init__(self, n_joints=2):
self.n_joints = n_joints
self.angles = np.zeros(n_joints) # angle de chaque articulation
self.velocities = np.zeros(n_joints)
self.position = 0.0 # position horizontale du corps
def step(self, torques, dt=0.1):
# physique simplifiée
self.velocities += torques * dt
self.angles += self.velocities * dt
self.position += np.sum(np.sin(self.angles)) * dt # approximation du déplacement
# ====== Réseau neuronal simple ======
class Policy(nn.Module):
def __init__(self, n_inputs, n_outputs):
super().__init__()
self.fc = nn.Sequential(
nn.Linear(n_inputs, 64),
nn.Tanh(),
nn.Linear(64, n_outputs),
nn.Tanh()
)
def forward(self, x):
return self.fc(x)
# ====== Simulation multi-agents ======
n_agents = 5
agents = [Agent() for _ in range(n_agents)]
policy = Policy(n_inputs=4, n_outputs=2) # 2 joints + 2 vitesses
optimizer = optim.Adam(policy.parameters(), lr=0.01)
for episode in range(1000):
total_reward = 0
for agent in agents:
state = torch.tensor(np.concatenate([agent.angles, agent.velocities]), dtype=torch.float32)
action = policy(state)
agent.step(action.detach().numpy())
reward = agent.position # plus il avance, mieux c'est
total_reward += reward
# Backprop simple (pour illustrer)
loss = -torch.tensor(total_reward / n_agents, requires_grad=True)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if episode % 50 == 0:
print(f"Episode {episode}, moyenne distance: {total_reward/n_agents:.2f}")