2009-03-15 2 views
0

Kontext: Ein Kartenspiel; Ich möchte jedem Spieler im Spiel Karten von einem Deck auf saubere Art und Weise austeilen.Elegante Technik, um Objekte von einem Array zum anderen zu bewegen

Dies ist, was ich im Sinn hatte:

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; 
    } 

Sicherlich können Sie die Referenz Probleme sehen. Die Verwendung des Schlüsselworts ref führt zu etwas nicht so gut aussehenden Code, aber es kann unvermeidlich sein. Irgendwelche Vorschläge?

Ich würde eine Lösung bevorzugen, die flexibel genug ist, um andere "Kartenpass" -Situationen zu bewältigen (ein Spieler spielt eine Karte auf den Stapel, zieht Karten vom Stapel auf ein "Müll" -Deck usw.).

Antwort

1

Nun, eine einfache Möglichkeit besteht nicht darin, Arrays zu verwenden. Benutze Listen von Anfang an und du musst sie nicht neu zuweisen - entferne sie einfach vom Deck und füge sie der Hand hinzu. Vielleicht möchten Sie einen Queue<T> für das Deck verwenden.

Der funktionalere Weg wäre, unveränderliche Sammlungen und Ref-Parameter zu verwenden, aber das ist nicht besonders praktisch ohne einige gute unveränderliche Sammlungsklassen hinter Ihnen. (Sie sind verfügbar, aber nicht in das Framework integriert.)

Warum übergeben Sie das Kartenfeld in die Methode? Sollte es nicht einfach alles vom Deck aus erledigen? An diesem Punkt ist es einfacher zu schreiben: (.. Ich bin mir nicht sicher, warum Sie Erweiterungsmethoden hier verwenden, btw Das ist etwas aussieht, die besser geeignet als eine Instanz Methode wäre)

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

+0

Ich hatte nicht von der Queue-Klasse gehört, aber ich werde es ausprobieren. Ich habe es versäumt, dies zu erwähnen, aber ich möchte etwas, das flexibel genug ist, um es in anderen "Kartenpass" -Situationen zu verwenden (Karten spielen, Karten auf das "Müll" -Deck verschieben usw.). Verlängerungsmethoden erschienen geeignet. –

+0

Ich denke, dass Sie auf diese Weise mit mehr verschachteltem Code enden werden. Ich würde ernsthaft vorschlagen, es zuerst auf die einfachste Art und Weise zu schreiben, und dann Wiederverwendung zu erwägen, wenn Sie tatsächlich ähnlichen Code an mehreren Stellen haben. –

+0

FWIW, ich stimme Jon zu. Erweiterungsmethoden sollten verwendet werden, wenn Sie das Verhalten einer Klasse erweitern und nicht steuern möchten.Sie müssen das Verhalten von Sammlungen jedoch nicht erweitern. Sie wollen nur Dinge tun, die Sammlungen beinhalten. –

3

Das ist ein schlechter Fall für Arrays, denke ich, die normalerweise nicht dazu gedacht sind, wiederholt hinzugefügt und entfernt zu werden. Außerdem würde ich dies nicht zu einer Erweiterungsmethode machen, da es außerhalb einiger ausgewählter Orte in Ihrer Anwendung keine Relevanz hat.

Denken Sie nur daran, mit einer Liste zu bleiben und eine Klassenmethode zu haben, die für das Verschieben verantwortlich ist.

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); 
    } 
} 

Commen hat ein Kartenspiel kann besser als Queue, nach dem Vorbild vorgeschlagen, die ein legitimer Punkt ist abhängig davon, ob Sie nur Karten oben von dem Deck oder nicht stattfinden können. Wenn das tatsächlich der Fall ist, denken Sie darüber nach:

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); 
    } 
} 
+0

Wird in diesem Beispiel das Schlüsselwort "ref" benötigt? Auch habe ich es versäumt, dies zu erwähnen, aber ich würde etwas bevorzugen, das ich für andere "Kartenpass" -Situationen wiederverwenden kann. –

+0

Hier ist kein Ref-Schlüsselwort erforderlich. Sie ändern nicht, worauf sich die Liste bezieht, sondern nur den Inhalt. –

+0

Genau wie ich dachte. +1. @unknown, Dies ist ein wenig kompliziert, weil das Lesen von einem Deck wie eine Warteschlange ist, während die Hände mehr zufällig sind. Johns Beispiel behandelt das Deck als Random-Access, aber Sie können c = _deck.Last() oder was auch immer zuweisen, damit es wie eine Warteschlange funktioniert. – strager

0

Vielleicht so etwas?

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); 
} 
Verwandte Themen