Bringing Down the House

Time limit: 0.6s Memory limit: 60MB Input: bdth.in Output: bdth.out

În timp ce Henry, Hetty și Harry dorm în casele lor din Anglia, Charles a plecat să facă în continuare curățenie – în cazinourile din Las Vegas. El va petrece 1010 zile în acel oraș, încercând să câștige cât mai mulți bani la masa de Blackjack. În cazinourile frecventate de Charles, jocul de Blackjack se desfășoară după următoarele reguli:

Desfășurarea jocului

În fiecare din cele 1010 seri, Charles pleacă de acasă cu o sumă de bani moneymoney și se așează la o masă de joc la care se mai alfă doar dealerul (cel care împarte cărțile). Jocul se desfășoară în etape, numite mâini. O mână se desfășoară astfel:

  1. Charles pariază o sumă de bani betbet pe mâna curentă. Natural, Charles nu are voie să parieze mai mulți bani decât deține în acel moment.
  2. Dealerul pune pe masă o carte cu fața în sus – aceasta este prima carte a dealerului.
  3. Charles primește două cărți.
  4. Cât timp suma valorilor cărților lui Charles (vom nota această sumă cu CC) nu depășește 2121, Charles are opțiunea să mai ceară o carte. Acest pas se repetă până Charles anunță că nu mai dorește cărți. Charles este obligat să anunțe că nu mai dorește cărți dacă CC a depășit 2121.
  5. Dealerul trage cărți din pachet până când suma valorilor cărților lui (să o notăm cu DD) depășește 1616.
  6. Charles primește înapoi bani corespunzător rezultatului mâinii, astfel:
    • 2×bet2 \times bet – dacă C>DC > D și C21C \leq 21, sau dacă C21C \leq 21 și D>21D > 21.
    • betbet – dacă C=DC = D și C21C \leq 21, sau dacă C>21C > 21 și D>21D > 21
    • 00 – dacă C<DC < D și D21D \leq 21, sau dacă C>21C > 21 și D21D \leq 21.

Cărțile

În jocul de Blackjack se folosesc cărțile aferente a nPacksnPacks pachete standard de 5252 de cărți (44 cărți pentru fiecare număr de la 22 la 1010, plus 44 ași (A)(A), 44 popi (K)(K), 44 dame (Q)(Q) și 44 valeți (J)(J)). Cărțile numererotate au valoarea egală cu numărul înscris pe ele, popii, damele și valeții au valoarea 1010, iar un as poate să ia fie valoarea 11 fie valoarea 1111 (la decizia jucătorului).

Suma valorilor unui set de cărți se definește ca cea mai mare sumă ce se poate forma din valorile acelor cărți astfel încât aceasta să nu depășească 2121. În caz că acest lucru nu este posibil, suma valorilor setului de cărți se definește ca fiind cea mai mică sumă ce se poate forma din valorile acelor cărți. Exemple: suma valorilor setului (A,J)(A, J) este 2121, a setului (A,A)(A, A) este 1212 (unul din ași ia valoarea 11, celălalt 1111), a setului (A,9,8)(A, 9, 8) este 1818, iar a setului (A,10,9,8)(A, 10, 9, 8) este 2828.

La începutul jocului, cărțile sunt amestecate și sunt puse într-un teanc cu fața în jos. Pe parcursul jocului, cărțile se împart din vârful teancului. Odată folosite pentru o mână, cărțile nu vor mai fi folosite pentru o alta până la o reamestecare a pachetului. După ce se termină o mână și în teanc se află mai puțin de 4545 de cărți, toate cărțile sunt reamestecate. Jocul din seara respectivă continuă până când cărțile sunt amestecate TT-a oară.

Cerință

După 1010 nopți de joc intens, Charles s-a întors acasă cu un profit considerabil. Voi puteți atinge același succes ca Charles? Scrieți un program care ține locul lui Charles într-un joc de Blackjack jucat după regulile descrise mai sus, cu scopul de a câștiga cât mai mulți bani la finalul jocului.

Interacțiune

Pentru a rezolva această problemă va trebui ca în sursa voastră să existe următoarele 33 funcții:

void startGame(int money, int nPacks);

Această funcție este apelată de exact 1010 ori, semnificând faptul că jucătorul începe un joc nou. Ea primește ca parametru suma moneymoney de bani cu care începe să joace concurentul în acel joc și numărul nPacksnPacks de pachete care se vor folosi pentru joc.

void playHand();

Această funcție va fi apelată de fiecare dată când se va juca o mână. Interacțiunea dintre programul comisiei și această funcție se realizează conform cu pașii de desfășurare a unei mâini descriși în enunț, și anume:

  1. Concurentul apelează funcția
    int placeBet(int bet)
    
    pentru a paria o sumă de bani bet pe mâna curentă. Funcția returnează suma de bani pe care o mai are concurentul după ce a făcut pariul.
  2. Concurentul apelează o singură dată funcția
    int getDealerCard()
    
    care returnează valoarea primei cărți a dealerului.
  3. Concurentul apelează de două ori funcția
    int getCard()
    
    pentru a afla valorile primelor două cărți ale sale. Funcția returnează valoarea unei cărți primite de concurent.
  4. Cât timp suma CC a valorilor cărților sale nu depășește 2121, concurentul poate apela din nou getcard pentru a cere o nouă carte. Funcția returnează valoarea cărții primite. Acest pas se repetă până concurentul apelează funcția
    void stand()
    
    prin care anunță că nu mai dorește cărți. Atenție! În cazul în care, după ce concurentul a primit o carte, suma CC a depășit 2121, concurentul este obligat să apeleze stand.
  5. Pentru a afla cărțile pe care dealerul le trage din pachet, concurentul apelează repetat
    int getOutcome()
    
    fiecare apel al acestei funcții returnând valoarea unei cărți trase de dealer sau 00 dacă dealerul s-a oprit din a trage cărți. După ce funcția getOutcome a returnat 0 aceasta nu se mai apelează pentru mâna curentă. Reamintim că dealerul trage cărți din pachet până când suma valorilor cărților lui (să o notăm cu DD) depășește 1616.
  6. Concurentul apelează o singură dată
    int cashIn()
    
    pentru a afla câți bani primește înapoi. Funcția returnează suma primită de concurent după ce a jucat o mână. Reamintim că această sumă este:
    • 2×bet2 \times bet – dacă C>DC > D și C21C \leq 21, sau dacă C21C \leq 21 și D>21D > 21.
    • betbet – dacă C=DC = D și C21C \leq 21, sau dacă C>21C > 21 și D>21D > 21
    • 00 – dacă C<DC < D și D21D \leq 21, sau dacă C>21C > 21 și D<21D < 21.

Ca să rezumăm, ordinea în care jucătorul apelează funcțiile pentru o mână este: placeBet(int bet) \rightarrow getDealerCard() \rightarrow getCard() ×2\times 2 \rightarrow getCard() ×\times (de câte ori mai dorește jucătorul, maxim până depășește 2121) \rightarrow stand() \rightarrow getOutcome() ×\times (până retunează 00) \rightarrow cashIn().

void reshuffle();

Această funcție se apelează după terminarea unei mâini pentru a semnaliza concurentului că toate cărțile folosite în mâinile precedente au fost repuse în teanc iar teancul a fost reamestecat.

Restricții și precizări

  • 10nPacks2010 \leq nPacks \leq 20
  • 20 000money100 00020 \ 000 \leq money \leq 100 \ 000
  • 200T500200 \leq T \leq 500
  • Pentru orice apel al funcției placeBet(int bet), 0betm0 \leq bet \leq m, unde mm reprezintă suma de bani pe care o are concurentul în acel moment.
  • Un apel al funcțiilor getCard, getDealerCard sau getOutcome va returna 1111 în cazul în care cartea returnată este un as, și 1010 în cazul în care cartea returnată este un 1010, un popă, o damă sau un valet.
  • În cazul în care, după ce s-a terminat de jucat o mână și concurentul are cel puțin 1 000 000 0001 \ 000 \ 000 \ 000 bani, jocul se termină și concurentul primește punctaj maxim pe acel test.
  • Punctajul concurentului se va stabili în felul următor: se rulează sursa concurentului și se iau cele 1010 sume de bani cu care concurentul termină fiecare joc și se sortează descrescător. Se elimină primele două și ultimele două din acest șir și se stabilește SconcurentS_{concurent} = suma valorilor din acest șir. În mod similar se definește pentru sursa comisiei ScomisieS_{comisie}. Definim raport=Sconcurent/Scomisieraport = S_{concurent}/S_{comisie}. Punctajul concurentului pe un test este round(raport5×10)round(raport^{5} \times 10).

Exemplu de interacțiune

grader: startGame(20000, 10)
grader: playHand()
concurent: placeBet(100)
grader: return 19900
concurent: getDealerCard()
grader: return 9
concurent: getCard()
grader: return 11
concurent: getCard()
grader: return 10
concurent: cashIn()
grader: return 200
concurent: getOutcome()
grader: return 5
concurent: getOutcome()
grader: return 6
concurent: getOutcome()
grader: return 0

grader: startGame(2000, 10)
grader: playHand()
concurent: placeBet(100)
grader: return 1900
concurent: getDealerCard()
grader: return 10
concurent: getCard()
grader: return 10
concurent: getCard()
grader: return 9
concurent: getCard()
grader: return 4
concurent: stand()
concurent: getOutcome()
grader: return 10
concurent: getOutcome()
grader: return 0
concurent: cashIn()
grader: return 0

grader: reshuffle()

Explicație

Funcția startGame este apelată cu parametrii nPacks=10nPacks = 10, semnificând că în teancul de cărți se vor afla cărțile a 1010 pachete standard, și money=20 000money = 20 \ 000, reprezentând suma de bani pe care o are inițial concurentul.

Se semnalizează concurentului începerea unei mâini.

Concurentul pariază 100100 că va câștiga mâna curentă

Dealerul a primit un 99.

Concurentul apelează de 22 ori getCard pentru a primi primele 22 cărți. El primește un as și un 1010. Suma cărților concurentului este 2121.

Concurentul anunță că nu mai dorește cărți.

Dealerul primește un 55 și un 66. Suma cărților dealerului este 2020.

Concurentul a câștigat mâna și primește 200200 de bani. Acum are 2 1002 \ 100 bani.

Începe a doua seară de joc.

Concurentul mai cere o carte după primele 22. Aceasta este 44 și face ca suma să devină 2323.

Concurentul este obligat să apeleze stand acum.

Concurentului îi este semnalizat faptul că teancul de cărți a fost reamestecat.

Log in or sign up to be able to send submissions!