所以我应该让游戏成为课堂作业。
基本上,我决定重新创建一个单挑扑克游戏,运行不同的功能,例如int deal(a, b, x, y)
,其中a
和b
是英雄卡,x
而y
是别墅的。
这个功能特别让我有点难过。实际上,我循环遍历数组deck
,并将随机数分配给a
,b
,x
和y
。然后,我会将每个指定的值转换为真实的唯一卡片,然后将其返回int main()
。
我坚持的部分是“ as 每张卡都被选中,它会从数组中删除。似乎C中没有简单的方法可以简单地从{{{ 1}}数组。
deck
有什么想法吗?
答案 0 :(得分:4)
您不必删除任何内容。
随机播放deck
数组(使用Fisher-Yates shuffle或类似算法)。处理"顶部"甲板:
int top = 0;
card1 = deck[top++];
card2 = deck[top++];
card3 = deck[top++];
...
等。 top
变量是套牌中下一张可用卡的索引。
您的代码的大致轮廓将类似于
#define DECKSIZE 52
#define HANDSIZE 5
int main( void )
{
int deck[DECKSIZE] = { ... }; // initial deck;
size_t top = 0; // points to next available card
shuffle( deck, DECKSIZE );
int hero[HANDSIZE] = {0}; // 0 means no card has been drawn for
int villan[HANDSIZE] = {0}; // that element.
if ( deal( hero, HANDSIZE, deck, DECKSIZE, &top ) &&
deal( villan, HANDSIZE, deck, DECKSIZE, &top ) )
{
/**
* do stuff with hero and villan hands
*/
}
else
{
/**
* Not enough cards available in deck for two hands.
*/
}
};
您的deal
功能看起来像
int deal( int *hand, size_t handsize, int *deck, size_t decksize, size_t *top )
{
size_t i;
for ( i = 0; i < handsize && *top < decksize; i++ )
hand[i] = deck[(*top)++];
return i == handsize;
}
如果我们在发牌之前0
用完了牌,此功能将返回deck
,在这种情况下,您需要做某事。祝你好运!
如果你想处理部分手牌(例如更换3张牌),你可以做类似的事情
if ( deal( &hero[2], 3, deck, DECKSIZE, &top) )
...
此调用将使用hero[2]
中的三张新卡覆盖hero[4]
至deck
。每次拨打deal
,top
都会被提前指向套牌中的下一张可用卡片。
您可以编写一个discard
函数,将卡片返回到卡片组。这意味着保留一个单独的bottom
变量并更新:
int discard( int card, int *deck, size_t decksize, size_t *top, size_t *bottom )
{
int result = *bottom < *top && *bottom < decksize;
if ( result )
deck[(*bottom)++] = card;
return result;
}
显然,bottom
应严格小于套牌大小,并严格低于丢弃时的top
;否则,我们无法妥善管理我们的牌组或牌局。通过一些工作,你可以使你的数组&#34;循环&#34;,以便top
和bottom
&#34;环绕&#34;有必要的。如果您耗尽了套牌,您可以重新洗牌(减去手中的牌,这将是deck
和bottom
之间的top
条目,并重置top
和{{1 }} 有必要的。
在纸上玩一会儿,这应该是显而易见的。
修改强>
在这里解决问题:
此时您将卡片[5]分配给卡片,例如
这发生在bottom
循环中deal
函数中。我们第一次致电for
时,我们会告诉它处理deal
手:
hero
此时,deal( hero, HANDSIZE, deck, DECKSIZE, &top )
为0.假设我们一次处理5张卡片,第一次拨打top
会有效地执行此操作:
deal
当函数返回时,Loop iteration 0: hero[0] = deck[0]
Loop iteration 1: hero[1] = deck[1]
Loop iteration 2: hero[2] = deck[2]
Loop iteration 3: hero[3] = deck[3]
Loop iteration 4: hero[4] = deck[4]
变量已更新为5. next 时间我们调用top
,我们告诉它处理deal
手:
villain
同样,假设我们一次处理5张牌,循环有效地做到了这一点:
deal( villain, HANDSIZE, deck, DECKSIZE, &top )
第二次致电Loop iteration 0: villain[0] = deck[5];
Loop iteration 1: villain[1] = deck[6];
Loop iteration 2: villain[2] = deck[7];
Loop iteration 3: villain[3] = deck[8];
Loop iteration 4: villain[4] = deck[9];
后,deal
已更新为10.
每次使用top
变量拨打deal
时,它都会从top
开始在deck
指定的位置处理,并且每次通过循环时都会将{1}添加到top
。如果两个条件之一为真,则循环将退出:
top
- 我们已经处理了此手所需的所有卡片i == handsize
- 我们已经到达阵列的末尾,也不会发出更多的牌。所以,假设你已经交出了很多牌,并且牌组中只剩下3张牌 - 如果你试图再买5张牌,那么圈子会在你之前退出已经处理了所有5张牌,并且我们将返回0以表示牌组中不再有牌。
在哪个桌面随机洗牌?
在第一次调用*top == decksize
之前,您可以调用shuffle
函数执行此操作:
deal
int deck[DECKSIZE] = { ... };
...
shuffle( deck, DECKSIZE );
...
if ( deal( hero, HANDSIZE, deck, DECKSIZE, &top ) &&
deal( villain, HANDSIZE, deck, DECKSIZE, &top ) )
{
...
}
函数的简单(而非非常好)实现将是:
shuffle
基本上,这样做是将每个void shuffle( int *deck, size_t decksize )
{
for ( size_t i = 0; i < decksize; i++ )
{
int r = rand() % (decksize - i)
int tmp = deck[i+r];
deck[i+r] = deck[i];
deck[i] = tmp;
}
}
与deck[i]
到deck[i]
中随机选择的元素交换(意味着该元素可能保留在原位)。假设我们有5张牌。第一次通过循环时,deck[decksize-1]
指向第一张卡片。我们随机选择i
的偏移量并将其称为i
:
r
然后我们交换i --> 1
2
3
4 <-- r
5
和deck[i]
的内容,然后提前deck[i+r]
:
i
我们从其余元素中随机选择另一个 4
i --> 2
3
1
5
:
r
并进行另一次交换并提前 4
i --> 2
3
1
5 <-- r
:
i
泡沫,冲洗,重复 - 在循环结束时,阵列或多或少随机改组。
答案 1 :(得分:2)
不,C中没有办法从数组中删除元素。实际上,据我所知,没有办法从C ++,C#或Java中删除数组中的元素。
现在,既然如此,你有几个选择。您可以在代码中使用sentinel值将元素标记为不存在。您可以调整数组的大小以实际将其缩小一,并在删除元素时将所有元素向后重定位到一个位置。
在您的示例中,0或-1应该可以作为哨兵值使用。
答案 2 :(得分:0)
首先,让数组卡片静态。 接下来,引入另一个静态变量,该变量最初等于array = 52的长度。
现在当处理随机位置的牌时,它可以与牌组中的最后一张牌交换,用arraysize-1索引,并且阵列大小减少。
使这两个变量保持静态允许函数在调用之间维持状态。当然,更好的技术会将这些变量封装在一个结构中(允许例如多个游戏/重置套牌)。
简而言之,您真正想要解决的问题不是删除数组中的条目。相反,它是一个更具包容性的问题:如何从固定域执行重复随机选择而不重复选择值。上述方法是一种方法。下面使用20个插槽阵列的技术示例如下(对于短输出线(易于读取)选择值20。它可以很容易地为52,1000等):
<强>代码强>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define DECK_SIZE 20
typedef struct Deck
{
int cards[DECK_SIZE];
size_t size;
} Deck;
Deck init_deck()
{
// no shuffle is needed. each selection in draw_card will
// pick a random location in the remaining cards.
Deck deck;
for (int i=0; i<DECK_SIZE; ++i)
deck.cards[i] = i+1;
deck.size = DECK_SIZE;
return deck;
}
int draw_card(Deck *deck)
{
// reset when we run out of cards.
if (deck->size == 0)
{
printf("- reset \n");
deck->size = DECK_SIZE;
}
// generate random location in remaining cards.
// note: susceptible to modulo bias
size_t idx = rand() % deck->size;
// decrement size to consume card and index where
// the card will be swap-stored.
--deck->size;
// swap with last card in remaining cards
int tmp = deck->cards[deck->size];
deck->cards[deck->size] = deck->cards[idx];
deck->cards[idx] = tmp;
// return the drawn card
return deck->cards[deck->size];
}
int main()
{
Deck deck = init_deck();
srand((unsigned)time(NULL));
// draw 300 times. this should reset 15 times, and
// each time a new set of 1..20 should result
for (int i=0; i<300; ++i)
printf("%d ", draw_card(&deck));
fputc('\n', stdout);
}
输出(显然会有所不同)
7 16 3 20 9 13 6 4 1 12 18 10 14 2 8 17 11 5 15 19 - reset
6 20 14 16 11 2 10 13 4 12 18 5 3 7 19 9 17 8 15 1 - reset
14 1 8 15 13 2 19 16 11 17 5 18 9 12 7 6 3 20 4 10 - reset
18 17 12 2 15 19 1 4 14 10 20 16 9 5 11 13 6 8 3 7 - reset
4 18 5 1 19 16 8 10 9 14 13 17 12 20 7 2 15 6 11 3 - reset
14 16 18 1 5 10 17 3 19 9 8 2 7 13 12 20 4 15 11 6 - reset
16 15 12 13 6 1 17 10 9 7 11 20 8 19 2 18 3 4 14 5 - reset
7 1 8 16 17 5 2 12 13 6 18 20 9 11 14 19 15 3 4 10 - reset
20 13 4 18 7 17 12 15 5 14 2 16 11 3 9 10 1 19 8 6 - reset
5 19 4 17 18 13 8 2 12 7 9 1 11 10 3 14 6 15 16 20 - reset
3 5 10 7 1 15 19 13 16 12 9 8 6 20 4 11 17 18 14 2 - reset
11 14 4 7 15 9 16 18 8 13 12 5 10 19 2 6 20 1 3 17 - reset
10 18 2 4 12 20 14 11 16 13 3 9 8 6 5 7 17 1 15 19 - reset
19 12 20 11 13 9 5 1 10 15 7 2 17 6 3 4 8 14 16 18 - reset
10 3 19 4 6 14 18 11 1 7 9 16 8 13 17 20 2 5 15 12
注意每行上有20个选项,1..20中的每个数字每行一次。这项技术如何为您服务取决于您。一个有价值的挑战将是如何在重置(通常称为牌组翻转)发生时枚举现有玩家所持有的牌。这是一个需要解决的有趣问题,而不是一个看似直观的问题。
希望它有所帮助。
答案 3 :(得分:-2)
您可以使用几种解决方案来解决问题:
首先,使用vector类,尤其是insert和remove函数。这是一个很好的解释它是如何工作的: C++ Vectors
其次,我最喜欢的是使用数字或布尔值作为指标。例如,声明一个与你的牌组长度相同的简单布尔数组。所有元素的初始值都是真的。对于每张卡,你想从牌组中删除,只需将其值更改为false即表示它已消失。