Dobry sposób na tworzenie klas dla bardziej złożonych typów kart do gry niż te znajdujące się w standardowej talii?

9

Jestem zupełnie nowy w programowaniu obiektowym i staram się uczyć w Pythonie, tworząc prostą grę karcianą (jak się wydaje tradycyjną!). Zrobiłem następujący przykład, który działa dobrze i uczy mnie tworzenia wielu instancji PlayingCard()klasy w celu utworzenia instancji Deck()klasy:

class PlayingCard(object):
    def __init__(self, suit, val):
        self.suit = suit
        self.value = val

    def print_card(self):
        print("{} of {}".format(self.value, self.suit))

class Deck(object):
    def __init__(self):
        self.playingcards = []
        self.build()

    def build(self):
        for s in ["Spades", "Clubs", "Diamonds", "Hearts"]:
            for v in range(1,14):
                self.playingcards.append(PlayingCard(s,v))

deck = Deck()



Chcę zrobić coś teraz z bardziej złożonymi kartami, a nie tylko ze standardową talią 52 (która ma ładnie rosnące wartości). Talia, o której myślę, to gra karciana Monopoly:

wprowadź opis zdjęcia tutaj

Istnieją 3 podstawowe typy kart - karty AKCJA, karty WŁASNOŚCI i karty PIENIĄDZE. Karty akcji wykonują różne akcje, karty własności należą do różnych zestawów kolorów, a karty pieniężne mogą mieć różne wartości. Dodatkowo karty właściwości mogą być „symbolami wieloznacznymi” i mogą być używane jako część jednego z dwóch zestawów. Wreszcie, każda karta ma również równoważną wartość pieniężną (wskazaną w górnym rogu każdej karty). W czynszowych kartach akcji karta może odnosić się tylko do właściwości koloru wskazanej na karcie.

Moje pytanie brzmi, jak poradzić sobie z taką sytuacją i jaki byłby fajny sposób na włączenie tych różnych kart do opartego na klasach programu python? Powinienem zachować moją pojedynczą PlayingCard()klasę i mieć po prostu wiele danych wejściowych, takich jak PlayingCard(type="PROPERTY", value="3M"). Czy byłoby lepiej, aby utworzyć oddzielne klasy, takie jak ActionPlayingCard(), PropertyPlayingCard()itp? Czy jest jakiś lepszy sposób? Jak mówię, jestem na początku nauki tutaj i jak organizować tego rodzaju sytuacje w zakresie projektowania wyższego poziomu.

Wielkie dzięki.

teeeeee
źródło
Jeśli okaże się, że różne typy kart mają takie same funkcje, możesz użyć dziedziczenia, a nawet klasy abstrakcyjnej. Możesz przeczytać i używać Wzorca fabrycznego, aby przekazać typ karty, a odpowiednia klasa zostanie użyta
Tomerikoo
@Tomerikoo Dziękuję za zwrócenie na to uwagi - przeczytałem trochę o fabrycznym wzorze, o którym wspomniałeś. Jak rozumiem, jest to najbardziej przydatne, gdy nie wiesz z góry, jakie klasy obiektów będziesz musiał stworzyć (być może wiedząc tylko w czasie wykonywania). Ponieważ jednak w tym przypadku wiem, jak powinna wyglądać cała talia (ile kart każdego rodzaju, co każda z nich robi itp.), Czy ma tutaj zastosowanie wzór fabryczny?
teeeeee

Odpowiedzi:

3

Kiedy zbliżasz się do problemu z OOP , zwykle chcesz modelować zachowanie i właściwości w sposób wielokrotnego użytku, tj. Powinieneś pomyśleć o abstrakcjach i na tej podstawie uporządkować hierarchię klas.

Chciałbym napisać coś takiego:

class Card:
    def __init__(self, money_value=0):
        self.money_value = money_value

class ActionCard(Card):
    def __init__(self, action, money_value=0):
        super().__init__(money_value=money_value)

        self.action = action

class RentActionCard(ActionCard):
    def __init__(self, action, color, money_value=0):
        super().__init__(action, money_value=money_value)

        self.color = color

    def apply(self, property_card):
        if property_card.color != self.color:
            # Don't apply
        # Apply

class PropertyCard(Card):
    def __init__(self, color, money_value=0):
        super().__init__(money_value=money_value)

        self.color = color

class WildcardPropertyCard(PropertyCard):
    def __init__(self, color, money_value=0):
        super().__init__(color, money_value=money_value)

class MoneyCard(Card):
    def __init__(self, money_value=0):
        super().__init__(money_value=money_value)


Ponieważ Python jest językiem dynamicznie typowanym, moim zdaniem OOP jest nieco trudniejsze do uzasadnienia, ponieważ możemy polegać na pisaniu kaczek i dynamicznym wiązaniu , dlatego sposób organizacji hierarchii jest mniej ważny.

Gdybym miał modelować ten problem na przykład w języku C # , bez wątpienia użyłbym powyższej hierarchii, ponieważ mógłbym polegać na polimorfizmie do reprezentowania różnych typów i kierować przepływem mojej logiki w oparciu o rodzaj analizowanej karty.

Kilka uwag końcowych:

  1. Python ma bardzo potężne typy wbudowane, ale przez większość czasu korzystanie z nowych niestandardowych typów, które się na nich opierają, ułatwia życie.
  2. Nie musisz dziedziczyć objectpo typach w Pythonie 3 (który jest jedynym obsługiwanym na dzień dzisiejszy) objectdomyślnie dziedziczy .

Ale na koniec dnia nie ma idealnej odpowiedzi, najlepszym sposobem byłoby wypróbowanie obu metod i sprawdzenie, z czym jest Ci wygodniej.

alex
źródło
7

Są to tak zwane „decyzje projektowe”. Często „poprawny” sposób jest kwestią opinii. Jako początkujący myślę, że warto wypróbować obie implementacje, aby zobaczyć, jak działają. Będą kompromisy bez względu na to, który wybierzesz. Musisz zdecydować, które z tych kompromisów są najważniejsze. Podejmowanie tego rodzaju decyzji będzie informowane w miarę zdobywania doświadczenia.

Uczeń kodowy
źródło
Tak, dziękuję, robię dokładnie tak, jak mówisz - po prostu szukam wskazówek od osób, które są wystarczająco doświadczone, aby instynktownie poznać dobre podejście i mam nadzieję zrozumieć, dlaczego.
teeeeee
2

Możesz użyć dziedziczenia. To tutaj tworzysz klasę główną, a następnie masz podklasy, które nadal zawierają funkcje i wartości z klasy macierzystej, jednak mogą również mieć dodatkowe wartości i funkcje dla tej konkretnej klasy.

class Apple:
    def __init__(self, yearMade):
        pass

    def ring(self):
        print('ring ring')

class iPhone(Apple):
    def __init__(self, number)
        number = number

    def func():
        pass

Teraz klasa iPhone'a ma te same funkcje co klasa Apple i swoją własną funkcję. Jeśli chcesz dowiedzieć się więcej na temat dziedziczenia, polecam przeprowadzenie badań.

MoriartyPy
źródło
@teeeeee Ponieważ każda karta ma wartość, możesz ją wymieniać i grać, możesz stworzyć funkcje / procedury w klasie, aby poradzić sobie z tymi zdarzeniami, a następnie mieć dodatkowe atrybuty i funkcje dla niektórych kart w podklasie.
MoriartyPy
0

W przypadku monopolu zaprojektowałbym punkt widzenia na lądowanie gry. Bez kart. Karty po prostu przedstawiają lądowania w prawdziwym świecie.

Ali Berat Çetin
źródło
Proszę opracować.
teeeeee