Я пытаюсь создать колоду карт с помощью C ++. Я уже написал весь код, но есть проблема, которую я не могу понять:
Deck::Deck(){
Card card;
bool match = false;
for (int i=0;i<47;i++){
do{
card.setCard();
match = cardInDeck(card, i);
}while(match == true);
match = false;
cards[i] = card;
}
numDrawn = 0;
}
В моем конструкторе для класса Deck у меня есть цикл for (), который генерирует все 52 карты и гарантирует, что в колоде нет соответствующих карт. По крайней мере, так должно быть. Дело в том, что я не могу заставить цикл повторяться более 47 раз, но он все еще работает. Любое число свыше 47 приводит к тому, что экран консоли становится пустым во время выполнения, за исключением мигающего курсора. Я не совсем уверен, что это о числах больше 47, которые заставляют его перестать работать. Я тщательно его протестировал, и каждое число от 0 до 48 работает.
Может быть, где-то в моем коде есть небольшая ошибка, которую я просто не вижу. Я действительно не знаю. Но я был бы очень признателен за любую помощь.
Вот мой полный код:
#include<iostream>
#include<stdlib.h>
using namespace std;
void run();
class Card{
private:
char suit;
int value;
public:
Card();
void setCard();
void getCard();
int getValue();
int getSuit();
};
class Deck{
private:
Card cards[52];
int numDrawn;
public:
Deck();
void shuffle();
void draw();
bool cardInDeck(Card card, int index);
};
int main(){
run();
}
Card::Card(){
srand(time(NULL));
value = rand() % 12 + 1;
suit = rand() % 4 + 1;
}
void Card::setCard(){
value = rand() % 12 + 1;
suit = rand() % 4 + 1;
}
void Card::getCard(){
cout<<" ----"<<endl<<"| |"<<endl<<"| ";
if (value == 1) cout<<'A';
else if (value == 10) cout<<'J';
else if (value == 11) cout<<'Q';
else if (value == 12) cout<<'K';
else cout<<value;
if (suit == 1) cout<<(char)3;
else if (suit == 2) cout<<(char)4;
else if (suit == 3) cout<<(char)5;
else cout<<(char)6;
cout<<" |"<<endl<<"| |"<<endl<<" ----"<<endl;
}
int Card::getSuit(){
return suit;
}
int Card::getValue(){
return value;
}
bool Deck::cardInDeck(Card card, int index){
bool match;
for(int i=0;i<=index;i++){
if((card.getValue() == cards[i].getValue()) && (card.getSuit() == cards[i].getSuit())){
match = true;
break;
}
else match = false;
}
return match;
}
Deck::Deck(){
Card card;
bool match = false;
for (int i=0;i<47;i++){
do{
card.setCard();
match = cardInDeck(card, i);
}while(match == true);
match = false;
cards[i] = card;
}
numDrawn = 0;
}
void Deck::shuffle(){
Card card;
bool match = false;
for (int i=0;i<52;i++){
do{
card.setCard();
match = cardInDeck(card, i);
}while(match == true);
match = false;
cards[i] = card;
}
numDrawn = 0;
}
void Deck::draw(){
cards[numDrawn].getCard();
numDrawn++;
}
void run(){
Deck cards;
char choice;
int cardsDrawn = 0;
cout<<"Enter 's' to shuffle the deck, 'd' to draw a card, or 'x' to exit: ";
do{
cin>>choice;
switch(choice){
case 'X':
case 'x':break;
case 'S':
case 's':cards.shuffle();
cout<<"Deck shuffled."<<endl;
cardsDrawn = 0;
break;
case 'D':
case 'd':if (cardsDrawn == 52){
cout<<"Out of cards. Deck reshuffled."<<endl;
cards.shuffle();
cardsDrawn = 0;
break;
}
else{
cards.draw();
cardsDrawn++;
break;
}
default: cout<<"Invalid entry.\a Enter a valid option('s','d','x'): ";
}
}while((choice != 'x') && (choice != 'X'));
}
Есть 13 ценности в колоде из 52 карт
Card::Card(){
srand(time(NULL));
value = rand() % 13 + 1;
suit = rand() % 4 + 1;
}
void Card::setCard(){
value = rand() % 13 + 1;
suit = rand() % 4 + 1;
}
12 * 4
->
48
13 * 4
->
52
Ваш исходный код с 12 значениями может создать только 48 различных карт, поэтому вы получаете бесконечный цикл при попытке создать 52.
Отредактировано:
Кстати, вы должны следовать Эрик и совет Ханса Пассанта (см. комментарии к вашему вопросу). То, как вы делаете перетасовку, неправильно способ сделать это в том смысле, что существует гораздо более простой / более естественный / более чистый способ. Увидеть ниже,
/**
* Forward counting implementation of Fisher-Yates / Knuth shuffle.
* see https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
*/
template< typename A >
void shuffle ( A& a, int i, const int j ) {
// one item left => no need to shuffle
const int _j = j - 1;
for ( ; i < _j ; ++i ) {
// pick item uniformly at random to put at ith position
// once moved the item will stay in place
const int k = i + rand() % ( j - i );
// swap
auto tmp = a[i];
a[i] = a[k];
a[k] = tmp;
}
}
тогда вам нужно будет сгенерировать все 52 разных карты один раз, вот так
Card::Card( const int value, const int suit ) {
this->value = value;
this->suit = suit;
}
// we do not need this anymore
// void Card::setCard(){
// value = rand() % 13 + 1;
// suit = rand() % 4 + 1;
// }
Card cards[52];
int i = 0;
for ( int suit = 1 ; suit <= 4 ; ++suit ) {
for ( int value = 1 ; value <= 13 ; ++value ) {
cards[i] = Card( value, suit );
++i;
}
}
и, наконец, перетасовать колоду
shuffle( cards, 0, 52 );
Больше ссылок по этой общей проблеме: http://bost.ocks.org/mike/shuffle а также http://blog.codinghorror.com/the-danger-of-naivete.
Также, пожалуйста, подумайте (как drescherjm предложено в его комментарии), чтобы отправить вызов srand за пределы этого класса. Вызов srand сбрасывает начальное число для функции rand, и в очень простой схеме он должен вызываться только один раз в самом начале вашей основной функции. В вашем случае, без вызова setCard () для каждой имеющейся у вас карты, вы можете получить 52 раза одну и ту же карту, даже если они были сгенерированы. случайным образом ( увидеть http://en.wikipedia.org/wiki/Pseudorandomness ).
У меня есть время, вы должны посмотреть на Случайный заголовок стандартной библиотеки C ++ который предоставляет гораздо больше C rand lib. Там даже есть метод случайного <алгоритм>!
Вы генерируете карты и отбрасываете уже присутствующие. Лучшим способом было бы генерировать их все линейно, и они перемешивали колоду.
Чем больше растет ваша колода, тем дольше она находит новую действительную карту
Просто начните со свежей упаковки. А затем пройдите через них, меняя текущий на случайный. Сделайте это семь раз, как они сделали.
Увидеть https://www.math.hmc.edu/funfacts/ffiles/20002.4-6.shtml
Сортировать код
int pack[52];
for (int i = 0; i < 52; ++i) { pack[i] = i; }
for (int shuffle = 0; shuffle < 7; ++ shuffle) {
for (int i=0; i < 52; ++i) {
j = rand() % 52;
t = pack[i];
pack[i] = pach[j];
pack[j] = t;
}
}
Это устраняет проблему при загрузке пакета и тасует его.
Вот Это полностью функциональный пример, который я сделал в классе в прошлом семестре, он работает довольно хорошо.
Deck::Deck()
{
cout << "Constructor was run" << endl;
nextCard = 0;
cards = new Card[numCards];
int c = 0;
for (int s = 0; s < Card::numSuits; s++)
{
for (int r = 1; r <= Card::numRanks; r++)
{
Card cd(s, r);
cards[c++] = cd;
}
}
srand(static_cast<int>(time(0)));
}
Я предпочел бы сначала подготовить полную колоду без перетасовки, а затем каждый раз, когда пользователь берет карту, выбирать случайную из колоды, заменять ее место в колоде последней картой в колоде и уменьшать размер колоды.
int deck[52];
int size=0;
void init()
{
for (int i=0; i<52; i++)
deck[i]=i;
size=52;
}
int draw()
{
if (!size)
init();
int i = rand()%size;
size--;
int card = deck[i];
deck[i]=deck[size];
return card;
}