Элегантная техника для перемещения предметов из одного массива в другой - PullRequest
0 голосов
/ 16 марта 2009

Контекст: карточная игра; Я хочу раздать карты из колоды каждому игроку в игре чистым способом.

Вот что я имел в виду:

public static CardGame.IGame DealAll(this CardGame.IGame objThis, CardGame.Card[] cards)
    {
        if (objThis.Players.Length > 0)
        {
            for (int i = 0; i < cards.Length; i++)
            {
                objThis.Deck.MoveTo(cards[i], objThis.CurrentPlayer.Hand);

                objThis.AdvancePlayer();
            }
        }

        return objThis;
    }

public static Card[] MoveTo(this Card[] objThis, Card card, Card[] cards)
    {
        List<Card> lstCards = cards.ToList();
        List<Card> lstThis = objThis.ToList();

        lstThis.Remove(card);
        lstCards.Add(card);

        objThis = lstThis.ToArray();
        cards = lstCards.ToArray();

        return cards;
    }

Конечно, вы можете увидеть проблемы со ссылками. Использование ключевого слова ref приводит к некоторому не очень приятному виду кода, но это может быть неизбежно. Есть предложения?

Я бы предпочел решение, которое было бы достаточно гибким, чтобы справляться с другими ситуациями «передачи карт» (игрок, играющий карту в колоду, перемещающий карты из колоды в колоду «мусора» и т.

Ответы [ 3 ]

3 голосов
/ 16 марта 2009

Я думаю, что это плохой случай для Arrays, который обычно не предназначен для повторного добавления и удаления. Кроме того, я бы не стал использовать этот метод расширения, поскольку он не имеет отношения к нескольким выбранным местам в вашем приложении.

Подумайте о том, чтобы просто придерживаться List и использовать метод класса, который отвечает за перемещение.

public class CardDealer {
...
  private List<Card> _deck;

  // Put the card [c] into [hand], and remove it from the deck.
  public void Deal(List<Card> hand, Card c) {
    _deck.Remove(c);
    hand.Add(c);
  }
}

Комментаторы предположили, что колода карт может быть лучше смоделирована как Очередь, что является законным пунктом в зависимости от того, можете ли вы брать карты только с верха колоды или нет. Если это действительно так, рассмотрите это:

public class CardDealer {
...
  private Queue<Card> _deck;

  // Put the top card of the deck into the specified hand.
  public void Deal(List<Card> hand) {
    // Deck is a Queue now. No need to specify which card to take.
    Card c = _deck.Dequeue(); 
    hand.Add(c);
  }
}
1 голос
/ 16 марта 2009

Ну, во-первых, простой способ - не использовать массивы. Используйте списки с самого начала, и вам не нужно перераспределять и т. Д. - просто удалите из колоды и добавьте в руку. Возможно, вы захотите использовать Queue<T> для колоды.

Более функциональным способом было бы использование неизменяемых коллекций и параметров ref, но это не очень практично без некоторых хороших неизменных классов коллекций позади вас. (Они доступны, но не встроены в фреймворк.)

Почему вы передаете массив карт в метод? Разве это не должно иметь дело со всем из колоды? В этот момент легче написать:

foreach (Card card in deck)
{
    CurrentPlayer.Hand.Add(card);
    AdvancePlayer();
}
deck.Clear();

(я не уверен, почему вы используете здесь методы расширения, кстати. Это выглядит как нечто более подходящее в качестве метода экземпляра.)

0 голосов
/ 16 марта 2009

Может как то так?

interface ICardPile
{
    ICollection<Card> Cards
    {
        get;
    }
}

interface IOrderedCardPile : ICardPile // FIXME Better name.
{
}

class Deck : ICardPile
{
    private Stack<Card> _cards = new Stack<Card>();

    ICollection<Card> Cards
    {
        get
        {
            return _cards;
        }
    }

    public Deck()
    {
        // TODO Fill deck.
    }

    public void DealCardsTo(IEnumerable<ICardPile> piles, int cardCount)
    {
        for(int i = 0; i < cardCount; ++i)
        {
            foreach(var pile in piles)
                Cards.MoveSomeTo(piles, 1);
        }
    }
}

class Hand : IOrderedCardPile
{
    private HashSet<Card> _cards = new HashSet<Card>();

    ICollection<Card> Cards
    {
        get
        {
            return _cards;
        }
    }
}

// Extension methods
static void MoveSomeTo(this ICardPile pile, ICardPile other, int count)
{
    // Removes cards from the end of pile and puts them at the end of other.

    foreach(Card card in pile.Cards.Reverse().Take(count))
    {
        other.Add(card);
    }

    pile.Cards = pile.Cards.Take(count);
}

static void MoveCardTo(this IOrderedCardPile pile, ICardPile other, Card card)
{
    // Removes card from pile and puts it at the end of other.
    pile.Remove(card);
    other.Add(card);
}

// Examples
Deck deck;
DiscardPile discard;
var hands = new Hand[4];

deck.DealCardsTo(hands, 7);

// Discard all aces.
forach(var hand in hands)
{
    foreach(var card in hand.Cards.Where(card => card.Number == Card.SomeEnum.Ace))
        hand.MoveCardTo(discard, card);
}
...