Witamy na hackathonie Noc Sztucznej Inteligencji! Twoim zadaniem jest stworzenie inteligentnego agenta, który potrafi grać w Uno i pokonać swoich przeciwników.
- Szybki Start
- Struktura Projektu
- Jak Stworzyć Swojego Agenta
- Struktura Stanu Gry
- Trenowanie i Testowanie
- Zasady Gry
- Środowisko Gry
- Przykłady
- Ocena i Kryteria
# Sklonuj repozytorium
git clone <url-repozytorium>
cd Noc-Sztucznej-Inteligencji-25
# Zainstaluj wymagane biblioteki
pip install rlcard gymnasium numpy joblib torch# Uruchom przykładową grę z losowym agentem
python main.pyNoc-Sztucznej-Inteligencji-25/
│
├── uno/
│ ├── env.py # Środowisko gry Uno
│ ├── agents.py # Implementacje agentów (TU PRACUJESZ!)
│ └── tournament.py # System turniejowy
│
├── main.py # Główny plik do trenowania i testowania
├── uno_rules.md # Szczegółowe zasady gry Uno
└── README.md # Ten plik
Znajdź klasę SubmissionAgent:
class SubmissionAgent(BaseAgent):
def select_action(self, state, legal_actions):
# TODO: Zaimplementuj logikę swojego agenta tutaj
return legal_actions[0]Twój agent musi implementować metodę select_action, która:
Przyjmuje parametry:
state: Słownik zawierający pełny stan gry (szczegóły poniżej)legal_actions: Lista dostępnych akcji (indeksów kart, które można zagrać)
Zwraca:
- Wybraną akcję (liczbę całkowitą z listy
legal_actions)
class SubmissionAgent(BaseAgent):
def select_action(self, state, legal_actions):
# Przykład: Priorytetyzuj karty akcji
# Dostęp do informacji o kartach w ręce
hand_cards = state['raw_obs']['hand']
target_card = state['raw_obs']['target']
# Karty Draw 2: 48-51
for action in legal_actions:
if 48 <= action <= 51:
return action
# Karty Skip: 40-43
for action in legal_actions:
if 40 <= action <= 43:
return action
# W przeciwnym razie zagraj pierwszą dostępną kartę
return legal_actions[0]Stan gry jest przekazywany jako słownik Pythona z następującymi kluczami:
state = {
'obs': np.ndarray, # Tensor obserwacji (4, 4, 15)
'legal_actions': dict, # Słownik dostępnych akcji
'raw_obs': dict, # Czytelne dla człowieka informacje o grze
'raw_legal_actions': list, # Lista nazw dostępnych akcji
'action_record': list # Historia zagranych akcji
}Jest to najważniejsza część stanu dla zaawansowanych agentów. To tensor NumPy o kształcie (4, 4, 15).
Wymiar 0 - Płaszczyzny (4 płaszczyzny):
- Płaszczyzna 0: Karty dostępne w talii/niewidoczne
- Płaszczyzna 1: Karty w Twojej ręce
- Płaszczyzna 2: Karty zagrane/widoczne
- Płaszczyzna 3: Dodatkowe informacje o stanie gry
Wymiar 1 - Kolory (4 kolory):
- Indeks 0: Czerwone karty
- Indeks 1: Zielone karty
- Indeks 2: Niebieskie karty
- Indeks 3: Żółte karty / Karty Wild
Wymiar 2 - Rangi Kart (15 typów na kolor):
- Indeksy 0-9: Karty liczbowe (0-9)
- Indeks 10: Skip
- Indeks 11: Reverse
- Indeks 12: Draw 2
- Indeks 13: Wild (tylko dla koloru 3)
- Indeks 14: Wild Draw 4 (tylko dla koloru 3)
- 1: Karta jest obecna/prawdziwa
- 0: Karta jest nieobecna/fałszywa
# Pobierz tensor obserwacji
obs_tensor = state['obs'] # Kształt: (4, 4, 15)
# Sprawdź karty w mojej ręce (płaszczyzna 1)
my_hand = obs_tensor[1] # Kształt: (4, 15)
# Sprawdź, czy mam czerwoną 5
has_red_5 = obs_tensor[1, 0, 5] == 1 # Płaszczyzna 1, Kolor 0 (czerwony), Ranga 5
# Sprawdź, czy mam zieloną kartę Skip
has_green_skip = obs_tensor[1, 1, 10] == 1 # Płaszczyzna 1, Kolor 1 (zielony), Indeks 10 (Skip)
# Policz wszystkie karty w mojej ręce
num_cards_in_hand = np.sum(obs_tensor[1])
# Spłaszcz dla prostszego przetwarzania (np. dla sieci neuronowej)
flattened_obs = obs_tensor.flatten() # Kształt: (240,) = 4 * 4 * 15Jest to słownik zawierający czytelne dla człowieka informacje o grze:
raw_obs = {
'hand': ['r-5', 'g-skip', 'y-7', ...], # Twoje karty jako stringi
'target': 'b-4', # Karta na wierzchu stosu
'played_cards': ['b-skip', 'b-4'], # Historia zagranych kart
'num_cards': [5, 7], # Liczba kart każdego gracza
'num_players': 2, # Liczba graczy
'current_player': 0 # ID aktualnego gracza
}'r-5': Czerwona 5'g-skip': Zielony Skip'b-draw_2': Niebieski Draw 2'y-reverse': Żółty Reverse'wild': Karta Wild'wild-draw-4': Wild Draw 4
# Pobierz czytelne informacje
raw_obs = state['raw_obs']
# Sprawdź swoje karty
my_hand = raw_obs['hand'] # Lista stringów
print(f"Mam {len(my_hand)} kart: {my_hand}")
# Sprawdź kartę na stole
target = raw_obs['target'] # String jak 'r-5'
print(f"Karta na stole: {target}")
# Sprawdź liczbę kart przeciwnika
opponent_cards = raw_obs['num_cards'][1] # Gracz 1
print(f"Przeciwnik ma {opponent_cards} kart")
# Analiza karty na stole
if target.startswith('r-'):
print("Karta docelowa jest czerwona")
if 'skip' in target:
print("Karta docelowa to Skip")Lista liczb całkowitych reprezentujących dostępne akcje (0-60):
legal_actions = [34, 42, 60] # Możesz zagrać kartę 34, 42 lub dobrać (60)| Zakres Akcji | Typ Karty | Szczegóły |
|---|---|---|
| 0-9 | Czerwone liczby | r-0 do r-9 |
| 10-19 | Zielone liczby | g-0 do g-9 |
| 20-29 | Niebieskie liczby | b-0 do b-9 |
| 30-39 | Żółte liczby | y-0 do y-9 |
| 40-43 | Skip | r-skip, g-skip, b-skip, y-skip |
| 44-47 | Reverse | r-reverse, g-reverse, b-reverse, y-reverse |
| 48-51 | Draw 2 | r-draw_2, g-draw_2, b-draw_2, y-draw_2 |
| 52-55 | Wild | wild (4 kopie) |
| 56-59 | Wild Draw 4 | wild-draw-4 (4 kopie) |
| 60 | Akcja Dobierania | Dobierz kartę ze stosu |
Zmodyfikuj main.py:
from uno.env import UnoEnv
from uno.agents import SubmissionAgent, RandomAgent
from uno.tournament import Tournament
# Testowanie przeciwko losowemu agentowi
my_agent = SubmissionAgent()
opponent_agent = RandomAgent()
env = UnoEnv(render_mode="human") # "human" pokazuje przebieg gry
tournament = Tournament(env, agents=[my_agent, opponent_agent])
# Zagraj 100 gier
payoffs = tournament.run_tournament(num_games=100)env = UnoEnv(render_mode=None) # Brak wizualizacjiSzczegółowe zasady znajdziesz w pliku uno_rules.md. Kluczowe informacje:
Możesz zagrać kartę, jeśli pasuje ona do górnej karty na stosie przez:
- Kolor (np. Czerwona 5 na Czerwonej 9)
- Liczbę (np. Niebieska 7 na Zielonej 7)
- Akcję (np. Zielony Skip na Żółtym Skip)
- Karty Wild można zagrać zawsze
| Karta | Efekt |
|---|---|
| Skip | Pomija kolejnego gracza |
| Reverse | Odwraca kierunek gry |
| Draw 2 | Następny gracz dobiera 2 karty i traci turę |
| Wild | Zmienia kolor (gracz wybiera) |
| Wild Draw 4 | Następny gracz dobiera 4 karty, zmienia kolor |
- +1: Wygrałeś grę (pozbyłeś się wszystkich kart)
- -1: Przegrałeś grę (przeciwnik pozbył się wszystkich kart)
- 0: Gra trwa
obs, reward, done, truncated, info = env.step(action)obs: Nowy stan gry (słownik)reward: Nagroda (0 podczas gry, +1/-1 na końcu)done: Boolean - czy gra się skończyłatruncated: Boolean - zawsze False (Nie był potrzebny w naszej imlementacji, ale trzymamy sie standary OpenAI Gym)info: Słownik zplayer_idilegal_actions
import numpy as np
class RandomAgent(BaseAgent):
def select_action(self, state, legal_actions):
return np.random.choice(legal_actions)class HeuristicAgent(BaseAgent):
def select_action(self, state, legal_actions):
raw_obs = state['raw_obs']
# Unikaj dobierania, jeśli możesz zagrać
if 60 in legal_actions and len(legal_actions) > 1:
legal_actions = [a for a in legal_actions if a != 60]
# Sprawdź, czy przeciwnik ma mało kart
opponent_cards = raw_obs['num_cards'][1]
if opponent_cards <= 2:
# Przeciwnik prawie wygrywa! Graj defensywnie
# Priorytetyzuj karty akcji, żeby utrudnić
for action in legal_actions:
if 40 <= action <= 59: # Karty akcji i Wild
return action
# W przeciwnym razie graj normalnie
return legal_actions[0]import torch
import torch.nn as nn
class NeuralAgent(BaseAgent):
def __init__(self):
self.model = nn.Sequential(
nn.Linear(240, 128), # 240 = 4 * 4 * 15 (spłaszczony obs)
nn.ReLU(),
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, 61) # 61 możliwych akcji
)
def select_action(self, state, legal_actions):
# Spłaszcz tensor obserwacji
obs_flat = state['obs'].flatten()
obs_tensor = torch.FloatTensor(obs_flat).unsqueeze(0)
# Przewiduj wartości akcji
with torch.no_grad():
action_values = self.model(obs_tensor).squeeze()
# Maskuj nielegalne akcje
mask = torch.full((61,), float('-inf'))
mask[legal_actions] = 0
masked_values = action_values + mask
# Wybierz najlepszą legalną akcję
best_action = torch.argmax(masked_values).item()
return best_actionTwój agent będzie oceniany na podstawie:
- Skuteczności Procent wygranych gier przeciwko botom innych uczestników. Każdy bot zagra z każdym innym dostatecznie dużo gier. Zwicięsce wybieramy po sumie zwyciestw.
- Zaimplementuj swojego agenta
- Prześlij plik z kodem źródłowiem w zipie, jeśli metoda używa wyuczonego agenta prześlij również jego wagi, (na przykład w pliku .pt lub .pkl). Pliki powinne być wysłane na skrzynkę
[email protected], w mailu powinna zlaleść sie informacja o członkach drużyny nadsyłającej pliki.
agent = QLearningAgent()
env = UnoEnv(render_mode=None) # Używaj render_mode=None żeby szybciej uczyć agenta
num_episodes = 1000
for episode in range(num_episodes):
obs, info = env.reset()
done = False
while not done:
current_player_id = info["player_id"]
legal_actions = info["legal_actions"]
action = agent.select_action(obs, legal_actions)
next_obs, reward, done, truncated, info = env.step(action)
if current_player_id == 0:
agent.learn(obs, action, reward, next_obs, done, episode)
obs = next_obs
# Zapisz wytrenowanego agenta
joblib.dump(agent, "my_trained_agent.pkl")Masz pytania? Potrzebujesz pomocy?
- Sprawdź plik
uno_rules.mddla szczegółów o grze - Zapytaj na discordzie wydarzenia lub na mailu
[email protected]
Niech najlepszy agent wygra! Baw się dobrze podczas Nocy Sztucznej Inteligencji 2025!
Stworzono dla hackathonu Noc Sztucznej Inteligencji 2025 przez członków Koła Naukowego Statystyki Matematycznej 'GAUSS'