[英]Deleting an element from an array in C
so Im supposed to make a game an assignment for class. 因此,我应该将游戏分配给班级。
Essentially, I decided to re-create a heads up Poker game, running different functions such as int deal(a, b, x, y)
where a
and b
are the hero's cards, and x
and y
are the villan's. 本质上,我决定重新创建一个平视的扑克游戏,运行不同的功能,例如
int deal(a, b, x, y)
,其中a
和b
是英雄的牌, x
和y
是反派的。
This function in particular, has me a bit stumped. 特别是这个功能让我有些难过。 Effectively, I loop through the array
deck
, and assign random numbers to a
, b
, x
and y
. 实际上,我遍历了数组
deck
,并为a
, b
, x
和y
分配了随机数。 Then, I will translate each assigned value into a real, unique card, and return that to int main()
. 然后,我将每个分配的值转换为一个真实的唯一卡,并将其返回给
int main()
。
The part that I am stuck at is " as each card is selected, it is deleted from the array. It seems that there is no easy way in C to simply remove an element from deck
array. 我被卡住的部分是“ 当选择了每张卡时,它会从阵列中删除。似乎C中没有简单的方法从
deck
阵列中删除元素。
int deal (a, b, x, y)
{
int deck[52] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52};
int heroFirst;
int heroSecond;
int villainFirst;
int villainSecond;
srand(0);
}
Any thoughts? 有什么想法吗?
You don't have to delete anything. 您无需删除任何内容。
Shuffle your deck
array (using a Fisher-Yates shuffle or similar algorithm). 洗牌的
deck
阵列(使用费雪耶茨洗牌或类似的算法)。 Deal each card from the "top" of the deck: 从卡组的“顶部”处理每张卡:
int top = 0;
card1 = deck[top++];
card2 = deck[top++];
card3 = deck[top++];
...
etc. The top
variable is the index of the next available card in the deck. 等等。
top
变量是卡组中下一张可用卡的索引。
The general outline of your code will be something like 您的代码的总体轮廓将类似于
#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.
*/
}
};
Your deal
function would look something like 您的
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;
}
This function will return 0
if we run out of cards in deck
before we've dealt the hand, in which case you'll need to do...something. 如果我们在发
deck
之前牌deck
的牌用完了,则此函数将返回0
,在这种情况下,您需要执行某些操作。 Good luck! 祝好运!
If you want to deal a partial hand (such as to replace 3 cards), you'd do something like 如果您想发偏手(例如替换三张牌),则可以执行类似的操作
if ( deal( &hero[2], 3, deck, DECKSIZE, &top) )
...
This call will overwrite hero[2]
through hero[4]
with three new cards drawn from deck
. 该调用将使用从
deck
抽取的三张新牌覆盖hero[2]
至hero[4]
。 With each call to deal
, top
will be advanced to point to the next available card in the deck. 每次
deal
, top
都会前进,以指向卡片组中下一张可用的卡。
You can write a discard
function that returns cards to the deck. 您可以编写一个
discard
功能,以将卡返回到牌组。 It means keeping a separate bottom
variable and updating that: 这意味着要保留一个单独的
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;
}
Obviously, bottom
should be strictly less than the deck size and strictly less than top
on a discard; 显然,
bottom
应该严格小于甲板的尺寸,并且必须严格小于丢弃物的top
; otherwise, we haven't managed our deck or hands properly. 否则,我们无法正确处理牌组或牌手。 With a little work, you could make your array "circular", such that
top
and bottom
"wrap around" as necessary. 只需做一些工作,就可以使阵列“圆形”,从而根据需要使
top
和bottom
“环绕”。 If you exhaust the deck, you can reshuffle (minus the cards in hand, which will be the deck
entries between bottom
and top
) and reset top
and bottom
as necessary. 如果你用尽了甲板上,你可以重新洗牌(减去手上的牌,这将是
deck
之间的条目bottom
和top
)和复位top
和bottom
是必要的。
Play with this on paper for a little while, and it should become obvious. 在纸上玩一会儿,它应该很明显。
EDIT 编辑
Addressing questions here: 在这里解决问题:
At which point do you assign deck[5] to a card, for instance
例如,您在哪一点将卡牌[5]分配给一张牌
That happens in the deal
function, in the for
loop. 这发生在
deal
函数的for
循环中。 The first time we call deal
, we tell it to deal to the hero
hand: 第一次调用
deal
,我们告诉它交易到hero
手上:
deal( hero, HANDSIZE, deck, DECKSIZE, &top )
At this point, top
is 0. Assuming we're dealing 5 cards at a time, the first call to deal
effectively does this: 此时,
top
为0。假设我们一次要处理5张卡片,则第一个有效地进行deal
电话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]
When the function returns, the top
variable has been updated to 5. The next time we call deal
, we tell it to deal to the villain
hand: 当函数返回时,
top
变量已更新为5。 下次我们调用deal
,我们告诉它处理villain
:
deal( villain, HANDSIZE, deck, DECKSIZE, &top )
Again, assuming we're dealing 5 cards at a time, the loop effectively does this: 再一次,假设我们一次要处理5张卡,则循环将有效地做到这一点:
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];
After the second call to deal
, top
has been updated to 10. 在第二个
deal
请求之后, top
已更新为10。
Each time you call deal
with the top
variable, it will start dealing from deck
at the position specified by top
, and each time through the loop it will add 1 to top
. 每次通话时间
deal
与top
变量,它会开始从交易deck
在由指定的位置top
,并通过循环每次它会增加1至top
。 The loop will exit if one of two conditions is true: 如果满足以下两个条件之一,则循环将退出:
i == handsize
- we've dealt all the cards necessary for this hand i == handsize
手牌i == handsize
-我们已经处理了这手牌所需的所有纸牌 *top == decksize
- we've reached the end of the array, nor more cards may be dealt. *top == decksize
卡牌*top == decksize
-我们已经到达数组的末尾,不能再发更多的牌。 So, suppose you've dealt a number of hands, and there are only 3 cards left in the deck - if you try to deal 5 more cards, the loop will exit before you've dealt all 5 cards, and we'll return a 0 to indicate that no more cards are left in the deck. 因此,假设您已经发了很多手牌,而卡组中只剩下3张牌-如果您尝试再发5张牌,则在您全部发完5张牌之前 ,循环将退出,我们将返回0表示卡座中没有剩余的纸牌。
at which point is the desk shuffled randomly?
桌子什么时候随机洗了?
You would call a shuffle
function to do that before the first call to deal
: 您可以在第一个调用
deal
之前调用shuffle
函数来执行此操作:
int deck[DECKSIZE] = { ... };
...
shuffle( deck, DECKSIZE );
...
if ( deal( hero, HANDSIZE, deck, DECKSIZE, &top ) &&
deal( villain, HANDSIZE, deck, DECKSIZE, &top ) )
{
...
}
A simplistic (and not terribly good) implementation of the shuffle
function would be: 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;
}
}
Basically, what this does is swap each deck[i]
with a randomly chosen element from deck[i]
through deck[decksize-1]
(meaning the element may remain in place). 基本上,这是将每个
deck[i]
交换为一个从deck[i]
到deck[decksize-1]
随机选择的元素(这意味着该元素可能会保留在原位)。 Assume we have 5 cards. 假设我们有5张卡。 The first time through the loop,
i
points to the first card. 第一次通过循环,
i
指向第一张卡片。 We pick an offset from i
at random and call it r
: 我们从
i
随机选择一个偏移量,并将其称为r
:
i --> 1
2
3
4 <-- r
5
We then swap the contents of deck[i]
and deck[i+r]
, and then advance i
: 然后,我们交换
deck[i]
和deck[i+r]
,然后前进i
:
4
i --> 2
3
1
5
We pick another r
at random from the remaining elements: 我们从其余元素中随机选择另一个
r
:
4
i --> 2
3
1
5 <-- r
and do another swap and advance i
: 并进行另一次交换并推进
i
:
4
5
i --> 3
1
2
Lather, rinse, repeat - by the end of the loop, the array is more-or-less randomly shuffled. 泡沫,冲洗,重复-循环结束时,阵列或多或少随机洗牌。
No, there is no way in C to delete an element from an array. 不可以,C语言无法从数组中删除元素。 Indeed, as far as I know there is no way to delete an element from an array in C++, C# or Java either.
实际上,据我所知,也没有办法从C ++,C#或Java中删除数组中的元素。
Now, that being the case, you have a few options. 现在,既然如此,您有几个选择。 You can use a sentinel value in your code to mark an element as absent.
您可以在代码中使用哨兵值将元素标记为不存在。 You can resize your array to actually shrink it by one and relocate all elements backwards one place whenever an element is deleted.
您可以调整数组的大小,以实际将其缩小一,并在删除元素时将所有元素向后移到一个位置。
In your example, either 0 or -1 should serve fine as sentinel values. 在您的示例中,0或-1应该可以很好地用作哨兵值。
First at all, make the array deck static . 首先,使数组卡座为static 。 Next, introduce another static variable initially equal to the lenght of array=52.
接下来,引入另一个静态变量,该变量最初等于array = 52的长度。
Now when a card at a random position is dealt, it can be swapped with the last card in the deck, indexed with arraysize-1, and the arraysize being decreased. 现在,在随机位置处理一张牌时,可以将其与牌组中的最后一张牌交换,并用arraysize-1进行索引,并减小arraysize。
Making these two variables static allows the function to maintain a state between calls. 将这两个变量设为静态允许函数在调用之间保持状态 。 Of course better techniques would encapsulate these variables in a struct (allowing eg multiple games / resetting the deck).
当然,更好的技术会将这些变量封装在一个结构中(例如允许进行多个游戏/重置牌组)。
In short, the problem you're really trying to solve isn't one of deleting an entry in an array.. Rather, it is a more encompassing issue: How to perform repeated random selection from a fixed domain without repeating selected values. 简而言之,您真正要解决的问题不是删除数组中的一项。而是,它具有更广泛的意义:如何在不重复选定值的情况下从固定域执行重复随机选择。 The method described above is one way to do that.
上述方法是实现此目的的一种方法。 An example of the latter technique using a 20-slot array is below (the value 20 was chosen for short output lines (easy to read). It could just as easily be 52, 1000, etc.):
下面是使用20插槽数组的后一种技术的示例(为短输出线选择了值20(易于读取)。它也很容易为52、1000等):
Code 码
#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);
}
Output (obviously varies) 输出 (明显不同)
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
Notive that on each line, there are twenty selections, and each number in 1..20 appears exactly once per line. 请注意,每行上有20个选择,并且1..20中的每个数字每行仅出现一次 。 How this technique serves you is up to you.
该技术如何为您服务取决于您。 A worthy challenge will be how to enumerate the hands held by existing players when a reset (often called a deck-flip) happens.
一个值得挑战的挑战是如何在重置(通常称为套牌翻转)发生时枚举现有玩家的手牌。 That's an interesting problem to solve, and not one that is as intuitive as it may seem.
这是一个有趣的问题,并不是看起来那么直观的问题。
Hope it helps. 希望能帮助到你。
There are a couple of solutions you could use to solve your problem: 您可以使用几种解决方案来解决问题:
First, is using the vector class, especially the insert and remove functions. 首先,使用向量类,尤其是插入和删除函数。 here's a good explanation on how it works: C++ Vectors
这是一个很好的解释: C ++向量
Second, which is also my favorite is to use numbers or booleans as indicators. 其次,我最喜欢的是使用数字或布尔值作为指标。 for example, declare a simple boolean array of the same length as your deck.
例如,声明一个简单的布尔数组,其长度与卡片组的长度相同。 the initial value of all elements would be true.
所有元素的初始值都是真实的。 for each card, you'd like to remove from the deck simply change its value to false to indicate it's gone.
对于每张卡,您都希望将其从卡组中删除,只需将其值更改为false即可表示已消失。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.