#include "oyun.h" #include "sabit.h" #include "gerec.h" #include "davranis.h" #include "duzey.h" #include #include #include #include #include #include #include using namespace std; // Yere atilan kagitlarin ekranin hangi sutununda olacagini // belirler size_t const yerSutunu = gorunenAdUzunlugu + 20; // Yere atilan kagittan once yazdirilacak bosluk std::string const yerinYeri(yerSutunu, ' '); class RastgeleSayi { public: int operator() (int N) const { // TODO: Bu algoritma bozuk! return static_cast( N * (rand() / ((unsigned)RAND_MAX + 1.0))); } }; Deste elliIkiKur() { Deste deste; // Her renkten her kagidi ustu kapali olarak koy for (Renk renk = Renk_ilk; renk != Renk_son; ++renk) { for (Deger deger = Deger_ilk; deger != Deger_son; ++deger) { deste.push_back(Kagit(renk, deger, Kagit::kapali)); } } // Desteyi karistir RastgeleSayi uyduran; random_shuffle(deste.begin(), deste.end(), uyduran); return deste; } // Desteden kagit ceker; Kagidin yeni durumunu 'durum' // parametresi belirler Kagit kagitCek(Deste & deste, Kagit::Durum durum) { Kagit kagit(deste.back()); kagit.durumDegistir(durum); deste.pop_back(); return kagit; } // Desteden belirtilen sayi ve durumda kagit ceker ve bir // kucuk deste (el) olarak dondurur Deste kagitDagit(Deste & deste, size_t kagitSayisi, Kagit::Durum durum) { Deste el; for (size_t i = 0; i != kagitSayisi; ++i) { el.push_back(kagitCek(deste, durum)); } return el; } // Verilen dizgiyi satira ortalayarak yazar void ortayaYaz(string const & dizgi) { size_t const yarisi = dizgi.size() / 2; if (yerSutunu > yarisi) { boslukBirak(cout, yerSutunu - yarisi); } cout << dizgi; } // Verilen kagidin acik halini dondurur Kagit kagidinAcikHali(Kagit const & kagit) { Kagit sonuc(kagit); sonuc.durumDegistir(Kagit::acik); return sonuc; } // Yalnizca hata ayiklarken kullanilan bir islev void desteyiGoster(Deste const & deste) { transform(deste.begin(), deste.end(), ostream_iterator(cout), kagidinAcikHali); } // Verilen desteden uc kapali bir kagit cekerek yeri // hazirlar. Oyunun davranisina gore (bkz. davranis.h) ve // eger gerekirse, acilan valeyi en alta koyar. Deste yeriHazirla(Deste & deste, Anlatan & anlatan) { size_t const kapaliKagitSayisi = eldekiKagitSayisi - 1; Deste yer = kagitDagit(deste, kapaliKagitSayisi, Kagit::kapali); Kagit usttekiKagit = kagitCek(deste, Kagit::acik); if (acilanValeyiEnAltaKoy) { while (usttekiKagit.deger() == Deger_vale) { ostringstream cikis; cikis << usttekiKagit << " acildi; en alta koyuldu"; anlatan.gelismeBildir(cikis.str()); deste.insert(deste.begin(), usttekiKagit); usttekiKagit = kagitCek(deste, Kagit::acik); } } yer.push_back(usttekiKagit); return yer; } // Baslangicta yerde bulunan kagitlari gosterir. Her kagidin // arasina satir basi da koydugu icin kagitlar alt alta // cikarlar. void yeriGoster(Deste const & yer) { string const aralik = string("\n") + yerinYeri; cout << aralik; toplulukYazdir(yer, cout, aralik); cout << ' '; } // Kagidin degerinin yeri alip almadigini bildirir // // first: alir // second: pisti pair yeriAlir(Deste const & yer, Deger deger) { bool alir = false; bool pisti = false; if ( ! yer.empty()) { if (deger == yer.back().deger()) { alir = true; if (yer.size() == 1) pisti = true; } else if (deger == Deger_vale) { alir = true; } } return make_pair(alir, pisti); } // 'adet' tane rastgele secilmis ad dondurur vector rastgeleAdlar(size_t adet) { // Dikkat: Ayni ad tekrarlanmamalidir static char const * const adlar[] = { "Ali", "Can", "Cem", "Ahmet", "Mehmet", "Han", "Ergin", "Kaya", "Hasan", "Ersin", "Oya", "Gul", "Ayse", "Ebru", "Damla", "Hulya", "Pinar", "Nur", "Gamze", "Asli", "Semih", "Nurcan", "Yasar", "Efe", "Sila", "Ersan", "Derya" }; assert(adet <= toplamOge(adlar)); // Bilinen butun adlari 'sonuc' vector'une koyar vector sonuc(adlar, adlar + toplamOge(adlar)); // 'sonuc'un icerigini karistirir RastgeleSayi uyduran; random_shuffle(sonuc.begin(), sonuc.end(), uyduran); // 'sonuc'un bastan 'adet' tanesini yeni bir vector // olarak dondurur return vector(sonuc.begin(), sonuc.begin() + adet); } Oyuncu * insanOyuncuyuTani(Takim & takim) { string const insanAdi = giristenOku("Adiniz ne"); DusunceZinciri const dusunceZinciri( dusunceSeceneginiOku(insanAdi, yerler[insaninYeri])); return new Oyuncu(insanAdi, yerler[insaninYeri], takim, dusunceZinciri); } // Takimlardan birisi hedef puani asinca 'true' dondurur. // Eger iki takim da asmissa daha fazla puani olan kazanir. // Eger iki takimin puani da ayniysa, henuz kazanan yoktur. bool kazananVar(Takimlar const & takimlar, int hedefPuan) { size_t hedefeVaranlar = 0; for (size_t i = 0; i != takimlar.size(); ++i) { int const puan = takimlar[i].puan(); if (puan > hedefPuan) { // En yuksek puani bu takim almis; hedefi arttiralim hedefPuan = puan; // Bu biraz sonra bir arttirilacak hedefeVaranlar = 0; } if (puan == hedefPuan) { // Biz de kazaniyoruz ++hedefeVaranlar; } } // Ancak tek bir takim hedefe varmissa kazanan bellidir return hedefeVaranlar == 1; } Oyuncular oyunculariTani(size_t oyuncuSayisi, Takimlar & takimlar) { assert((oyuncuSayisi == 2) || (oyuncuSayisi == 4)); Oyuncular oyuncular; // Herhangi bir secim olarak, insan oyuncuyu ilk takima // koyuyoruz Oyuncu * insanOyuncu = insanOyuncuyuTani(takimlar[0]); oyuncular.push_back(insanOyuncu); vector const adlar(rastgeleAdlar(oyuncuSayisi)); int adSecici = -1; // Insan oyuncuyu tanidigimiz icin 1'den basliyoruz for (size_t i = 1; i != oyuncuSayisi; ++i) { ++adSecici; // Insan oyuncuyla ayni adda baska oyuncu secmek // istemiyoruz if (adlar[adSecici] == insanOyuncu->ad()) { // Bu yalnizca bir kere olabilir ++adSecici; } string const ad = adlar[adSecici]; size_t const takim = i % tarafSayisi; size_t yer = i; // Iki kisilik oyunda yalnizca Guney/Kuzey vardir if (oyuncuSayisi == 2) yer *= 2; DusunceZinciri const dusunceZinciri( dusunceSeceneginiOku(ad, yerler[yer])); oyuncular.push_back(new Oyuncu(ad, yerler[yer], takimlar[takim], dusunceZinciri)); } return oyuncular; } void Oyun::herkeseGoster(Kagit const & kagit) { for (size_t i = 0; i != oyuncular_.size(); ++i) { oyuncular_[i]->gor(kagit); } } void bekle(long nanoSaniye) { timespec sure = { 0, nanoSaniye }; bool bekledik = false; while (!bekledik) { timespec kalan = { 0, 0L }; if (nanosleep(&sure, &kalan) == -1) { // Erken sonlandik cout << kalan.tv_sec << ' ' << kalan.tv_nsec << '\n'; sure = kalan; } else { bekledik = true; } } } void dusunuyorGibiYap(long nanosaniye) { char const karakterler[] = "|/-\\|-\\"; size_t const adet = sizeof(karakterler) - 1; while (nanosaniye > 0) { for (size_t i = 0; i != adet; ++i) { cout << karakterler[i]; long const beklemeSuresi = beklemeBirimi * 2; bekle(beklemeSuresi); nanosaniye -= beklemeSuresi; cout << '\b' << flush; } } } void kagidiMasayaBirak(Kagit const & kagit, string const & satirBasi) { int uzaklik = yerSutunu - satirBasi.size(); if (uzaklik <= 0) uzaklik = 2; uzaklik /= 2; uzaklik *= 2; cout << flush; for (int i = 0; i <= uzaklik; i += 2) { cout << '\r' << satirBasi; fill_n(ostream_iterator(cout), i, ' '); cout << kagit << flush; bekle(beklemeBirimi); } cout << ' '; } void Oyun::oyuncuOynat(Oyuncu & oyuncu, Oyuncu & sagdaki, Deste & yer, bool tekKagit) { string const satirBasi = oyuncu.satirBasi() + " "; cout << '\n' << satirBasi << flush; // Oyuncunun sectigi kagidi ogren pair const secim = oyuncu.kagitSec(yer); Kagit const atilan = *secim.first; oyuncu.cikart(secim.first); pair const sonuc = yeriAlir(yer, atilan.deger()); bool const alir = sonuc.first; bool const pisti = sonuc.second; if (alir) { // En son kimin kazandigini hatirla; sona kalan // kagitlar da onun olacak. sonKazanan_ = &oyuncu; } else { // Sagdaki oyuncuya onun solundakinin almadigin // bildir. Boylece sagdaki oyuncunun hangi // kagidin daha saglam oldugu konusunda fikri // olabilir. (Bazen yanlis da olsa :) sagdaki.solundakiAlmadi(yer.back()); } if (!hizliOynat && (!oyuncu.insan() || (tekKagidiHemenOyna && tekKagit))) { // Bilgisayara dusunuyor havasi vermek icin // gereksizce bekle. cout << flush; dusunuyorGibiYap(secim.second); kagidiMasayaBirak(atilan, satirBasi); } else { if (yerSutunu > satirBasi.size()) { boslukBirak(cout, yerSutunu - satirBasi.size() + 2); } cout << atilan << ' '; } anlatan_.yeniKagit(); herkeseGoster(atilan); yer.push_back(atilan); if (alir) { oyuncu.takim().kazan(yer); anlatan_.gelismeBildir(pisti ? "PISTI!" : "aldi"); } } void Oyun::sonucBildir(char const * baslik) const { char const * const ayrac = " ----- "; ostringstream cikis; cikis << ayrac << baslik << "; "; toplulukYazdir(takimlar_, cikis, ", "); cikis << ayrac; cout << '\n'; ortayaYaz(cikis.str()); cout << "\n\n"; } void Oyun::masaHazirla(Deste & deste, Deste & yer) { deste = elliIkiKur(); yer = yeriHazirla(deste, anlatan_); // Her takima yeni bir elin basladigini haber ver for_each(takimlar_.begin(), takimlar_.end(), mem_fun_ref(&Takim::yeniEl)); // Her oyuncuya yeni bir destenin karistirildigini haber // ver for_each(oyuncular_.begin(), oyuncular_.end(), mem_fun(&Oyuncu::yeniDeste)); // Oyunu anlatandan cikisa yeni el ve destenin // basladigini yazdirmasini iste anlatan_.yeniEl(); anlatan_.yeniDeste(); herkeseGoster(yer.back()); } void Oyun::kagitlariDagit(Deste & deste) { for (size_t i = 0; i != oyuncular_.size(); ++i) { size_t const oyuncu = (dagitan_ + i + 1) % oyuncular_.size(); // Burada kagitlari acik dagitmamizin nedeni, yere // atildiklarinda gorulmelerini istememiz. (Bunun // icin daha iyi bir yol da bulunabilirmis gibi // geliyor.) oyuncular_[oyuncu]->yeniEl(kagitDagit(deste, eldekiKagitSayisi, Kagit::acik)); } anlatan_.yeniEl(); anlatan_.gelismeBildir("kagit dagitildi"); if (deste.empty()) { anlatan_.gelismeBildir("son el"); } } void Oyun::desteOynat() { // Yeni destenin dagitildigini bildir anlatan_.yeniDeste(); // Kimin dagittigini bildir cout << yerinYeri << " "; size_t const dagitanNumarasi = dagitan_ % oyuncular_.size(); anlatan_.gelismeBildir(oyuncular_[dagitanNumarasi]->ad() + " dagitiyor"); Deste deste; Deste yer; masaHazirla(deste, yer); yeriGoster(yer); // Anlatandan yeni bir kagit atildigini soylemesini iste anlatan_.yeniKagit(); while ( ! deste.empty()) { kagitlariDagit(deste); for (size_t kagitSayisi = 0; kagitSayisi != eldekiKagitSayisi; ++kagitSayisi) { for (size_t i = 0; i != oyuncular_.size(); ++i) { size_t const sagdaki = (dagitan_ + i) % oyuncular_.size(); size_t const oyuncu = (dagitan_ + i + 1) % oyuncular_.size(); if (yer.empty()) { cout << '\n'; ortayaYaz("] ["); anlatan_.yeniKagit(); anlatan_.gelismeBildir("yer bos"); } oyuncuOynat(*oyuncular_[oyuncu], *oyuncular_[sagdaki], yer, kagitSayisi == (eldekiKagitSayisi - 1)); } } } // Deste bittikten sonra, yerde kalan kagitlar son alan // takimin olur size_t const yerdekiKagitSayisi = yer.size(); if (yerdekiKagitSayisi && sonKazanan_) { sonKazanan_->takim().kazan(yer); char const * const kagitSozu = ((yerdekiKagitSayisi == 1) ? "kagidi" : "kagitlari"); ostringstream cikis; cikis << "kalan " << kagitSozu << ' ' << sonKazanan_->ad() << " aldi"; anlatan_.gelismeBildir(cikis.str()); } cout << '\n'; } Oyun::Oyun(size_t oyuncuSayisi, size_t hedefPuan) : hedefPuan_(hedefPuan), takimlar_(tarafSayisi), oyuncular_(oyunculariTani(oyuncuSayisi, takimlar_)), sonKazanan_(0), dagitan_(RastgeleSayi()(oyuncuSayisi)) {} Oyun::~Oyun() { // TODO: Bu temizlik yerine boost::shared_ptr kullanilmalidir for (size_t i = 0; i != oyuncular_.size(); ++i) { delete oyuncular_[i]; } } void Oyun::oynat() { // Takimlara yeni oyun basladigini bildir for_each(takimlar_.begin(), takimlar_.end(), mem_fun_ref(&Takim::yeniOyun)); sonucBildir("Oyun basladi"); bool bitti = false; while ( ! bitti) { desteOynat(); bitti = kazananVar(takimlar_, hedefPuan_); sonucBildir(bitti ? "Oyun bitti" : "Ara sonuc"); // Kagitlari simdi bir sonraki oyuncu dagitacak ++dagitan_; } }