Tema 3 – Liga Boților (C++)
🤖 Liga Boților - Arena Studenților ⚔️
Construiește un motor de simulare pentru o arenă tactică în care echipele de boți concurează pentru energie, teritoriu și puncte de stil. Formatul este 100% comandă-per-linie, astfel încât output-ul tău poate fi redat ca input pentru un alt student – perfect pentru dueluri!
📅
Publicare: 7.12.2025 \
⏰
Deadline: 21.12.2025, ora 23:59 \
🎯
Punctaj Total: 10p (9p teste + 1p README + până la 5p bonus clasament) \
🧪
Teste: 20 scenarii automate (folder `teste/`) \
🏆
Clasament Live: rezultate.eu \
📝
Accept Assignment: Classroom Link
Autor: Trifu Marius-Constantin
Tehnologii:
✅ Limbaj: C++ (este voie cu STL)
✅ Testare: 20 teste dinamice
✅ Deploy: GitHub Actions
✅ Clasament: rezultate.eu
Cerințe:
1. Descriere
Jocul este un battle royale simplu pe hartă 2D. Ești un jucător într-o arenă rectangulară și te poți deplasa sau planta bombe. Simularea rulează pe runde (ticks), iar intrarea este o succesiune de comenzi trimise de arbitru (`REF`). După fiecare comandă procesată trebuie să emiți propria ta decizie (`PLAYER …`).
🎯 Obiectiv: Maximizează TOTAL_SCORE = damage dat adversarilor + supraviețuire - penalizări.
Mecanica Jocului
Ticks și Acțiuni:
Un tick = o rundă în care fiecare jucător face exact o acțiune
Acțiuni posibile: `MOVE` (deplasare o casetă), `PLANT_BOMB` (plantează bombă), `PLANT_WALL` (plantează perete), `WAIT` (stai pe loc)
După fiecare `REF …` primești, trebuie să răspunzi cu exact o acțiune `PLAYER …`
Hartă:
Arenă rectangulară de dimensiuni `LATIME × INALTIME` (coordonate 0-index)
Fiecare celulă poate conține: un jucător, o bombă, un perete, sau nimic
Mișcare:
Te poți deplasa cu o casetă pe rând în direcții: `N` (sus), `S` (jos), `E` (dreapta), `W` (stânga)
NU poți merge pe diagonală
Nu poți merge într-o celulă ocupată de alt jucător, bombă sau perete
Bombe:
Poți planta o bombă în celula curentă cu acțiunea `PLANT_BOMB`
Bomba explodează după `BOMB_TIMER` ticks de la plantare (ex: `BOMB_TIMER = 3` → explodează după 3 ticks)
La explozie, toate celulele din raza de explozie (`BOMB_RADIUS = 2`) primesc damage
Raza de explozie = 2 casete: Bomba afectează celulele la distanță 1 și 2 în toate direcțiile (N, S, E, W)
Exemplu: Bomba la (5,5) afectează: (5,4), (5,3), (5,6), (5,7), (4,5), (3,5), (6,5), (7,5) - total 8 celule
Damage-ul este `BOMB_DAMAGE` pentru fiecare celulă afectată
Un jucător poate avea maximum `MAX_BOMBS` bombe active simultan
Pereți:
Poți planta un perete în celula curentă cu acțiunea `PLANT_WALL`
Peretele blochează mișcările (nu poți merge prin el)
Peretele blochează explozii (dacă e între tine și bomba, primești mai puțin damage sau deloc, în funcție de poziție)
Peretele are rezistență: Trebuie să fie lovit de 2 bombe pentru a fi distrus
Un jucător poate avea maximum `MAX_WALLS` pereți activi simultan
Peretele este permanent până când este distrus de 2 explozii
Vizibilitate:
Scor:
Primești puncte pentru damage dat adversarilor
Primești bonus pentru supraviețuire (dacă rămâi în viață)
Penalizări pentru comenzi invalide sau ieșire din arenă
Fluxul unei simulari
1. Citești configurarea arenei (dimensiuni, parametri bombe, număr runde).
2. Primești comenzi de la arbitru una câte una (spawn adversar, mișcări, explozii etc.).
3. După fiecare linie citită **trebuie** să răspunzi cu exact 1 linie care descrie acțiunea ta pentru acel tick.
4. La final raportezi tabela scorurilor și cine câștigă.
📝 Respectă exact formatele! Fișierele de test vor fi în `teste/input/test01.in … test20.in`. Output-urile așteptate sunt în `teste/output/`.
2.1 Fișier de intrare (arena.txt)
| arena.txt |
ARENA LATIME INALTIME ROUNDS
BOMB_TIMER BOMB_RADIUS BOMB_DAMAGE MAX_BOMBS MAX_WALLS
PLAYER_HP INITIAL_X INITIAL_Y
STREAM
TICK <t>
SPAWN <team> <x> <y>
MOVE <team> <x> <y>
BOMB <team> <x> <y> <timer>
EXPLODE <x> <y>
WALL <team> <x> <y>
WALL_HIT <x> <y>
WALL_BROKEN <x> <y>
HIT <x> <y> <dmg>
...
END
|
Explicații:
`ARENA` – dimensiunile grilei (0-index), număr maxim de runde.
`BOMB_TIMER` – numărul de ticks până bomba explodează (ex: 3 = explodează după 3 ticks de la plantare).
`BOMB_RADIUS` – raza de explozie (fixată la 2 casete în toate direcțiile N, S, E, W).
`BOMB_DAMAGE` – damage-ul aplicat fiecărei celule afectate.
`MAX_BOMBS` – numărul maxim de bombe active simultan pe jucător.
`MAX_WALLS` – numărul maxim de pereți activi simultan pe jucător.
`PLAYER_HP` – punctele de viață inițiale ale jucătorului.
`INITIAL_X INITIAL_Y` – poziția inițială a jucătorului tău (echipa Alpha).
`STREAM` – începe secvența de comenzi live. Liniile vin în timp real / sequential; nu există număr declarat, trebuie să citești până la `END`.
Notă: `BOMB_RADIUS` este întotdeauna 2 în această temă (bomba afectează celulele la distanță 1 și 2 în direcțiile N, S, E, W).
Direcții disponibile: `N` (sus), `S` (jos), `E` (dreapta), `W` (stânga). NU sunt permise diagonalele.
Exemplu:
ARENA 10 8 50
3 1 20 3
100 0 0
STREAM
REF TICK 1
REF SPAWN Beta 9 7
REF MOVE Beta 9 6
REF TICK 2
REF MOVE Beta 9 5
...
END
|
| Comandă | Semnificație |
| TICK t | Semnalează începerea tick-ului `t`. |
| SPAWN team x y | Arbitru introduce un jucător pentru `team` (Alpha/Beta) la coordonatele (x, y). |
| MOVE team x y | Jucătorul rival (`team`) s-a deplasat la coordonatele (x, y). |
| BOMB team x y timer | Jucătorul `team` a plantat o bombă la (x, y) care explodează în `timer` ticks. |
| EXPLODE x y | O bombă a explodat la coordonatele (x, y). |
| WALL team x y | Jucătorul `team` a plantat un perete la coordonatele (x, y). |
| WALL_HIT x y | Un perete la (x, y) a fost lovit de o bombă (prima lovitură, peretele rămâne activ). |
| WALL_BROKEN x y | Un perete a fost distrus complet (a doua bombă) la coordonatele (x, y). |
| HIT x y dmg | Ai primit `dmg` damage la coordonatele (x, y) (de la explozie). |
Observații:
Vezi toate mișcările adversarilor (`MOVE Beta …`).
Vezi toate bombele plantate (`BOMB Beta …`).
Vezi toți pereții plantați (`WALL Beta …`).
Primești notificări când bombele explodează (`EXPLODE …`).
Primești notificări când pereții sunt loviți (`WALL_HIT …`) sau distruși (`WALL_BROKEN …`).
Primești damage când ești în raza de explozie (`HIT …`).
Elemente noi (extensibilitate):
Poți primi comenzi noi pentru elemente viitoare (ex: `TRAP team x y`, `POWERUP x y type`).
Pentru comenzi necunoscute, răspunde cu `WAIT`.
2.3 Output obligatoriu (arena.out)
| arena.out |
MOVE E
BOMB
WALL
WAIT
...
SCOREBOARD
DAMAGE_DEALT: value
SURVIVAL_BONUS: value
PENALTIES: value
TOTAL_SCORE: value
WINNER: Alpha/Beta/Draw
|
Reguli:
Exact 1 linie după fiecare comandă de intrare procesată.
Acțiuni disponibile: `MOVE <direction>`, `BOMB`, `WALL`, `WAIT`.
`MOVE <direction>` – te deplasezi cu o casetă în direcția `direction` (`N`, `S`, `E`, `W`).
`BOMB` – plantezi o bombă în celula curentă (dacă ai loc în `MAX_BOMBS`).
`WALL` – plantezi un perete în celula curentă (dacă ai loc în `MAX_WALLS`).
`WAIT` – stai pe loc (folosit când nu poți face altceva sau când comanda e necunoscută).
Output-ul trebuie să fie determinist (aceeași intrare → exact același output). Astfel putem lua fișierul rezultat și îl putem folosi ca input pentru un nou run (ex: `cat studentA.out | ./arena`).
Ultima parte `SCOREBOARD …` apare doar după `END`.
Notă: Nu mai trebuie să incluzi tick-ul în output (se știe din context).
Exemplu output:
MOVE E
BOMB
MOVE N
WAIT
SCOREBOARD
DAMAGE_DEALT: 60
SURVIVAL_BONUS: 50
PENALTIES: 0
TOTAL_SCORE: 110
WINNER: Alpha
|
Pentru dueluri:
Rulezi Student A → obții `arena.out`.
Transformi output-ul în comenzi de intrare (script disponibil în `teste/scripturi/oficializeaza_transcriere.py`).
Rulezi Student B cu acea transcriere: `cat arena.out.refeed | ./arena`.
Programul tău trebuie să ignore liniile de acțiune întâlnite în input (sunt tratate ca mișcări deja consumate) și să genereze răspunsuri noi.
2.5 Elemente noi (extensibilitate)
Formatul permite adăugarea de elemente noi în viitor:
Capcane: `TRAP team x y` - capcană plantată care dă damage când e activată
Power-ups: `POWERUP x y type` - obiecte speciale care dau bonusuri (ex: `SPEED`, `SHIELD`, `EXTRA_BOMB`)
Teleport: `TELEPORT team x1 y1 x2 y2` - jucătorul se teleportează
Alte elemente: Orice comandă nouă poate fi adăugată
Regulă: Pentru comenzi necunoscute, răspunde cu `WAIT`. Programul tău trebuie să fie robust la comenzi noi.
3. Reguli de Validare
🗺️ Mișcare
Poți merge doar în direcții: `N` (sus), `S` (jos), `E` (dreapta), `W` (stânga).
NU poți merge pe diagonală.
Nu poți merge într-o celulă ocupată de alt jucător, bombă sau perete.
Nu poți ieși din arenă: coordonatele trebuie să fie în `[0, LATIME) × [0, INALTIME)`.
Dacă comanda `MOVE` este invalidă → se tratează ca `WAIT` (penalizare).
💣 Bombe
Poți planta o bombă doar dacă ai mai puțin de `MAX_BOMBS` bombe active.
Nu poți planta o bombă într-o celulă care deja conține o bombă sau perete.
Bomba explodează după `BOMB_TIMER` ticks de la plantare (ex: timer=3 → explodează după 3 ticks).
La explozie, toate celulele din raza de explozie = 2 casete primesc `BOMB_DAMAGE` damage.
Raza de explozie = 2 casete în direcțiile N, S, E, W:
Bomba la (x, y) afectează: (x, y-1), (x, y-2), (x, y+1), (x, y+2), (x-1, y), (x-2, y), (x+1, y), (x+2, y)
Total: 8 celule (4 direcții × 2 casete fiecare)
Un jucător poate fi afectat de mai multe bombe simultan (damage se adună).
Pereții blochează explozii: Dacă un perete e între tine și bomba, primești mai puțin damage sau deloc (în funcție de poziție).
Pereții pot fi slăbiți/distruși: Dacă o bombă explodează în raza sa, peretele primește damage. După 2 bombe, peretele este distrus.
🧱 Pereți
Poți planta un perete doar dacă ai mai puțin de `MAX_WALLS` pereți activi.
Nu poți planta un perete într-o celulă care deja conține o bombă, perete sau jucător.
Peretele blochează mișcările (nu poți merge prin el).
Peretele blochează parțial explozii (dacă e între tine și bomba, primești mai puțin damage sau deloc).
Peretele are rezistență: Trebuie să fie lovit de 2 bombe pentru a fi distrus:
Prima bombă care lovește peretele (în raza sa) îl slăbește (peretele primește damage, dar rămâne activ)
A doua bombă care lovește peretele îl distruge complet (peretele dispare, primești `REF WALL_DESTROYED`)
Peretele este permanent până când este distrus de 2 explozii.
Strategie: Poți încolți adversarul cu pereți, dar el poate distruge pereții cu 2 bombe.
❤️ Viață și Damage
Jucătorul începe cu `PLAYER_HP` puncte de viață.
Când primești damage (`REF DAMAGE …`), scazi din HP.
Dacă HP ≤ 0 → jucătorul moare și nu mai poate face acțiuni.
Damage-ul se aplică imediat când bomba explodează.
⏱️ Sincronizare
Pentru fiecare `TICK t` trebuie să setezi contextul curent.
Comenzile cu alt tick decât cel curent sunt erori → tratează-le ca `WAIT`.
Două comenzi pot apărea cu același tick; le procesezi în ordine.
🪙 Scoring logic
`DAMAGE_DEALT` – damage total aplicat adversarilor (când bombele tale explodează și îi lovesc).
`SURVIVAL_BONUS` – bonus pentru supraviețuire (dacă rămâi în viață la final).
`PENALTIES` – se adună pentru:
Comenzi invalide (MOVE în celulă ocupată, PLANT_BOMB când ai MAX_BOMBS, etc.)
Ieșire din arenă
Lipsă răspuns (nu ai emis `PLAYER …` după un `REF …`)
4. Implementare
`main.cpp` doar creează obiectul principal (`ArenaController controller; controller.run(argc, argv);`).
Folosește clase pentru: `Arena` (hartă), `Player` (poziție, HP, bombe active, pereți activi), `Bomb` (poziție, timer), `Wall` (poziție), `Scoreboard`.
Tratează intrarea ca flux: citești linie → parsezi → execuți → produci output imediat.
Este permisă bufferizarea, dar output-ul trebuie să păstreze ordinea exactă a comenzilor de intrare.
Comenzile necunoscute → `WAIT`.
Gestionare bombe: Menține o listă de bombe active cu timer-uri. La fiecare tick, decrementezi timer-ul. Când timer = 0, bomba explodează cu raza 2 (celule la distanță 1 și 2 în direcțiile N, S, E, W).
Gestionare pereți: Menține o listă de pereți activi cu un contor de damage. Când o bombă explodează în raza sa, peretele primește damage. După 2 bombe, peretele este distrus.
Blocare explozii: Când o bombă explodează, verifică dacă pereții blochează damage-ul către anumite celule (dacă un perete e între bomba și jucător, damage-ul este redus sau blocat).
Vizibilitate: Toate bombele și pereții plantați sunt vizibili (vezi `REF BOMB_PLANTED …`, `REF WALL_PLANTED …`), deci poți planifica strategia.
Poți folosi STL (vector, map, etc.) pentru a gestiona bombele, pereții și jucătorii.
5. Punctaj
5.1 Teste automate (9p)
5.2 README (1p)
5.3 Bonus Clasament (până la 5p)
Clasament calculat după `TOTAL_SCORE` cumulat pe toate testele.
Top 1 → +5p, 2-3 → +4p, 4-5 → +3p, 6-10 → +2p, 11-20 → +1p.
6. Dueluri între studenți
1. Rulați `make record TEST=test07` pentru a salva transcriptul oficial (`out/test07.log`).
2. Trimiteți fișierul altui student.
3. Acesta îl poate reda cu `cat out/test07.log | ./arena`.
4. Pentru turnee, există scriptul `scripts/run_duel.sh studentA studentB testID` care combină output-urile și declară câștigătorul după reguli.
Programul vostru trebuie să fie robust la:
Comenzi duplicat (ex: același `SPAWN` repetat).
Ticks lipsă (trebuie să faceți `WAIT` până revine).
Output folosit ca input (linii de acțiune în stream).
7. Comunicare în Timp Real între Programe
Pentru dueluri live sau testare interactivă, ai mai multe opțiuni:
7.1 Named Pipes (FIFO) - Timp Real
Creezi un pipe și conectezi programele:
# Creează pipe-ul
mkfifo /tmp/arena_pipe
# Rulează Student A (scrie în pipe)
./arena_studentA < input.txt > /tmp/arena_pipe &
# Rulează Student B (citește din pipe)
./arena_studentB < /tmp/arena_pipe > output.txt
# Curăță după
rm /tmp/arena_pipe
Avantaje: Comunicare instantanee, fără fișiere intermediare.
Dezavantaje: Trebuie să gestionezi procesele manual.
7.2 Pipe Normal + tee (Salvare + Redirecționare)
Scrie în fișier ȘI trimite mai departe simultan:
# Student A scrie la stdout, tee salvează în fișier ȘI trimite la Student B
./arena_studentA < input.txt | tee arena_studentA.out | ./arena_studentB > arena_studentB.out
Avantaje: Simplu, salvează ambele output-uri.
Dezavantaje: Nu e “timp real” strict (bufferizare posibilă).
7.3 Process Substitution (Bash)
Bash creează automat pipe-uri temporare:
# Student A scrie într-un "fișier virtual" care e de fapt input pentru Student B
./arena_studentB < <(./arena_studentA < input.txt) > output.txt
# SAU invers: Student B primește output-ul lui A ca input
./arena_studentB <(./arena_studentA < input.txt) > output.txt
Avantaje: Sintaxă curată, bash gestionează pipe-urile.
Dezavantaje: Funcționează doar în bash, nu în sh.
7.4 Exemplu Makefile pentru Dueluri
# Duel între două executabile
duel: arena_studentA arena_studentB
@echo "🚀 Pornesc duelul..."
@mkfifo /tmp/duel_pipe 2>/dev/null || true
@timeout 30 ./arena_studentA < teste/input/test01.in | \
tee out/studentA.log | \
./arena_studentB > out/studentB.log || true
@rm -f /tmp/duel_pipe
@echo "✅ Duel terminat. Verifică out/studentA.log și out/studentB.log"
# Duel cu named pipe explicit
duel-fifo: arena_studentA arena_studentB
@mkfifo /tmp/arena_fifo
@./arena_studentA < teste/input/test01.in > /tmp/arena_fifo & \
./arena_studentB < /tmp/arena_fifo > out/duel_result.out; \
rm -f /tmp/arena_fifo
Format pentru dueluri: Output-ul lui Student A trebuie transformat în comenzi de intrare înainte de a fi dat lui Student B. Vezi secțiunea 6 pentru scriptul de transcriere.
8. Încărcare și Testare
Comenzi Make:
make # compilează și rulează toate testele din teste/input
make build # doar compilare
make run # rulează arena.txt -> arena.out
make test05 # rulează doar testul 5
make record TEST=test12 # rulează testul și păstrează transcriptul pentru duel
GitHub Actions rulează `make` + trimite rezultatele la `rezultate.eu`. Output-ul scriptului `trimite_rezultate.py` va include `TOTAL_SCORE`, nickname etc.
9. Exemplu Simplificat
Scenariu: Arenă 6×6, bombe cu timer 3, raza 2, damage 20, max 2 bombe, max 3 pereți. Tu (Alpha) ești la (0,0), adversarul (Beta) la (5,5).
| Input |
ARENA 6 6 10
3 2 20 2 3
100 0 0
STREAM
TICK 1
SPAWN Beta 5 5
MOVE Beta 5 4
TICK 2
MOVE Beta 5 3
BOMB Beta 5 3 3
TICK 3
MOVE Beta 5 2
WALL Beta 4 2
TICK 4
MOVE Beta 4 1
TICK 5
EXPLODE 5 3
HIT 5 3 20
HIT 5 2 20
HIT 5 4 20
HIT 5 1 20
HIT 4 3 20
HIT 3 3 20
WALL_HIT 4 2
TICK 6
EXPLODE 2 0
HIT 2 0 20
HIT 1 0 20
HIT 0 0 20
HIT 3 0 20
WALL_BROKEN 4 2
END
|
| Output |
MOVE E
MOVE E
BOMB
WALL
MOVE N
WAIT
SCOREBOARD
DAMAGE_DEALT: 20
SURVIVAL_BONUS: 50
PENALTIES: 0
TOTAL_SCORE: 70
WINNER: Alpha
|
Explicație:
Tick 1: Te deplasezi spre dreapta (E) pentru a te apropia de adversar.
Tick 2: Continui spre dreapta.
Tick 3: Plantezi o bombă la (2,0) care va exploda în 3 ticks (raza 2 = afectează celulele la distanță 1 și 2).
Tick 4: Plantezi un perete la (1,0) pentru a te proteja de explozia adversarului.
Tick 5: Bomba adversarului explodează la (5,3) cu raza 2, peretele tău la (4,2) primește prima lovitură (`REF WALL_DAMAGED`).
Tick 6: Bomba ta explodează la (2,0) cu raza 2, peretele adversarului la (4,2) primește a doua lovitură și este distrus (`REF WALL_DESTROYED`).
La final: Ai supraviețuit, ai dat damage adversarului, și ai distrus peretele adversarului cu 2 bombe.
10. Tips & Checklist
✅ Stochează istoric comenzi pentru a putea reface dueluri.
✅ Normalizează toate coordonatele/ids.
✅ Scrie teste proprii folosind `scripts/generate_random_stream.py`.
✅ Asigură-te că output-ul tău nu are spații în plus.
⛔ Nu amesteca logică de I/O cu logica de simulare – folosește un controller clar.
Checklist:
[ ] `make build` fără warnings
[ ] Toate testele oficiale trecute local
[ ] README complet
[ ] Transcript deterministic
[ ] Trimite la timp pe GitHub
Succes și abia așteptăm duelurile! 🥊