Autor: IOAI 2025
În jocul Chameleon, doi jucători comunică folosind doar pictograme. Unul dintre jucători alege un cuvânt secret, apoi oferă un indiciu sub forma unei secvențe ordonate de pictograme. Celălalt jucător trebuie să ghicească termenul secret.
Fiecare pictogramă are o descriere textuală cunoscută. Ordinea pictogramelor este importantă: prima pictogramă indică de obicei ideea principală, iar următoarele pictograme adaugă context.
Pentru fiecare test, programul primește o secvență de pictograme și o listă de opțiuni posibile. Termenul corect se află întotdeauna în lista de opțiuni. Programul trebuie să returneze cel mult 10 opțiuni, ordonate de la cea mai probabilă la cea mai puțin probabilă.

Datasetul conține următoarele fișiere:
train.csvval.csvtest.csvhint_descriptions.csvFișierul hint_descriptions.csv conține descrierile pictogramelor:
| Coloană | Descriere |
|---|---|
hintID | identificatorul pictogramei |
description | descrierea textuală a pictogramei |
icon_file | numele fișierului imagine asociat pictogramei |
Pictogramele se găsesc în directorul:
hint_icons/Imaginile sunt oferite ca material auxiliar. Pentru rezolvare este suficientă folosirea descrierilor din hint_descriptions.csv.
train.csv, val.csv și test.csvFiecare rând reprezintă un caz de testare.
| Coloană | Descriere |
|---|---|
datapointID | identificatorul cazului |
subtaskID | identificatorul subtask-ului; pentru această problemă este 1 |
hints | lista pictogramelor din indiciu, separate prin caracterul \| |
hints_json | aceeași listă de pictograme, în format JSON |
options | lista opțiunilor posibile, separate prin caracterul \| |
options_json | aceeași listă de opțiuni, în format JSON |
În train.csv și val.csv există și coloana:
| Coloană | Descriere |
|---|---|
answer | termenul corect |
În test.csv, coloana answer nu este disponibilă.
datapointID,subtaskID,hints,hints_json,options,options_json,answertrain_000000,1,6|61|63,"[6, 61, 63]",sunflower|credit card|dinosaur|seal,"[""sunflower"", ""credit card"", ""dinosaur"", ""seal""]",sealÎn exemplul de mai sus, indiciul este format din pictogramele cu ID-urile 6, 61 și 63. Programul trebuie să ordoneze opțiunile primite astfel încât termenul corect, în acest caz seal, să apară cât mai sus în listă.
Participanții trebuie să trimită un fișier submission.csv cu următoarele coloane:
datapointID,subtaskID,answerExemplu:
test_000000,1,light bulb|battery|lamp|electricity|flashlight|fuse|lantern|generator|solar panel|power gridtest_000001,1,seal|penguin|octopus|submarine|crab|shell|diving suit|jellyfish|swan|fishColoana answer trebuie să conțină cel mult 10 opțiuni, separate prin caracterul |.
Ordinea opțiunilor este importantă. Prima opțiune este considerată cea mai probabilă.
Pentru fiecare rând din test.csv trebuie să existe exact un rând în submission.csv.
Pentru fiecare caz, răspunsul trebuie să respecte următoarele reguli:
|;Exemplu valid:
light bulb|battery|lamp|electricity|flashlight|fuse|lantern|generator|solar panel|power gridExemple invalide:
light bulb,battery,lampMotiv: opțiunile sunt separate prin virgulă, nu prin |.
light bulb|battery|light bulbMotiv: opțiunea light bulb apare de două ori.
Pentru fiecare caz se iau în considerare primele cel mult 10 opțiuni din răspuns.
Fie gold termenul corect și fie guesses lista de opțiuni trimisă de concurent.
Dacă termenul corect apare în primele 10 opțiuni, atunci:
Hits@10 = 1Altfel:
Hits@10 = 0Dacă termenul corect apare pe poziția rank, unde prima poziție are rank = 0, atunci:
NDCG@10 = 1 / log2(rank + 2)Dacă termenul corect nu apare în primele 10 opțiuni, atunci:
NDCG@10 = 0Scorul pentru un caz este:
score = 0.9 * Hits@10 + 0.1 * NDCG@10Scorul final este media scorurilor obținute pe toate cazurile evaluate.
Evaluatorul calculează două scoruri:
Clasamentul final se bazează pe scorul privat.
Următorul baseline construiește un text din descrierile pictogramelor și compară acest text cu opțiunile folosind un model de tip SentenceTransformer.
import jsonimport pandas as pdimport numpy as npfrom sentence_transformers import SentenceTransformerfrom sklearn.metrics.pairwise import cosine_similaritydef hints_to_sentence(hints, hint_description): sentence = "The following hints at our target word:\n" for i, hint in enumerate(hints): hint_text = str(hint_description[int(hint)]) if i == 0: sentence += "<HINT_PRIMARY>\n" sentence += hint_text + "\n" sentence += "</HINT_PRIMARY>\n" elif i < len(hints) - 1: sentence += "<HINT>\n" sentence += hint_text + "\n" sentence += "</HINT>\n" else: sentence += "<HINT>\n" sentence += hint_text + "\n" sentence += "</HINT>" return sentencedef find_most_similar(query, sentences, model, top_k=10): query_embedding = model.encode([query]) sentence_embeddings = model.encode(sentences) similarities = cosine_similarity(query_embedding, sentence_embeddings)[0] top_indices = np.argsort(similarities)[::-1][:top_k] return [sentences[idx] for idx in top_indices]def main(test_csv="test.csv", hints_csv="hint_descriptions.csv", out_csv="submission.csv"): test = pd.read_csv(test_csv) hints = pd.read_csv(hints_csv) hint_description = dict( zip( hints["hintID"].astype(int), hints["description"].astype(str) ) ) model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2") rows = [] for _, row in test.iterrows(): hint_ids = json.loads(row["hints_json"]) options = json.loads(row["options_json"]) query = hints_to_sentence(hint_ids, hint_description) ranked = find_most_similar(query, options, model, top_k=10) rows.append({ "datapointID": row["datapointID"], "subtaskID": int(row["subtaskID"]), "answer": "|".join(ranked) }) pd.DataFrame(rows).to_csv(out_csv, index=False)if __name__ == "__main__": main()answer, cele 10 răspunsuri trebuie să fie
distincte și trebuie scrise exact ca în dataset.
Răspunsurile se separă prin caracterul |, fără duplicate.