Programming Technique: How to Create a Simple Card Game

Programming Technique: How to create a simple card game

Something to get you started

You can ensure unique cards very easily by using numbers from 0 to 51.

The Array#shuffle method is based off the Knuth-Fisher-Yates shuffle algorithm. http://en.wikipedia.org/wiki/Fisher–Yates_shuffle

class Card
RANKS = %w(2 3 4 5 6 7 8 9 10 J Q K A)
SUITS = %w(Spade Heart Club Diamond)

attr_accessor :rank, :suit

def initialize(id)
self.rank = RANKS[id % 13]
self.suit = SUITS[id % 4]
end
end

class Deck
attr_accessor :cards
def initialize
# shuffle array and init each Card
self.cards = (0..51).to_a.shuffle.collect { |id| Card.new(id) }
end
end

# people with Ruby 1.9 (or 1.8.7 with backports) can safely ignore this duck punch
class Array
# knuth-fisher-yates shuffle algorithm
def shuffle!
n = length
for i in 0...n
r = rand(n-i)+i
self[r], self[i] = self[i], self[r]
end
self
end
def shuffle
dup.shuffle!
end
end

test

d = Deck.new
d.cards.each do |card|
puts "#{card.rank} #{card.suit}"
end

output

6 Spade
5 Heart
2 Heart
8 Heart
8 Diamond
7 Club
J Diamond
4 Club
K Spade
5 Diamond
J Heart
8 Spade
10 Club
4 Diamond
9 Heart
7 Diamond
3 Diamond
K Diamond
7 Spade
Q Diamond
9 Diamond
6 Heart
A Heart
9 Club
A Spade
5 Club
J Club
Q Spade
2 Club
2 Spade
Q Heart
A Diamond
10 Spade
10 Diamond
Q Club
3 Club
A Club
K Club
6 Club
10 Heart
2 Diamond
3 Spade
K Heart
5 Spade
9 Spade
7 Heart
4 Spade
J Spade
3 Heart
4 Heart
8 Club
6 Diamond

Python Simple Card Game to Learn Classes

This is more of a code/approach review. A card game is a case for composition, not inheritance; the Deck contains Cards, but isn't in itself a type of Card, and vice versa.

I think you are duplicating information in the Card. Just store suit and rank, and use __str__ to create 'x of y'. You can also implement the rich comparison methods:

class Card(object):

FACES = {11: 'Jack', 12: 'Queen', 13: 'King', 14: 'Ace'}

def __init__(self, rank, suit):
self.suit = suit
self.rank = rank

def __str__(self):
value = self.FACES.get(self.rank, self.rank)
return "{0} of {1}".format(value, self.suit)

def __lt__(self, other):
return self.rank < other.rank

Now e.g. str(Card(13, 'Clubs')) == "King of Clubs". This way you don't duplicate the rank and value in card.

Next, I think the Deck should incorporate the population generation in __init__; you can provide optional arguments for a non-standard deck. I have included two implementations; the commented-out version is a list comprehension using itertools to do the same job in one line. I also provide a function to pick n different random cards from self.deck.

from itertools import product 
import random

class Deck(object):

def __init__(self, ranks=None, suits=None):
if ranks is None:
ranks = range(2, 15)
if suits is None:
suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
## self.deck = [Card(r, s) for r, s in product(ranks, suits)]
self.deck = []
for r in ranks:
for s in suits:
self.deck.append(Card(r, s))

def deal(self, n):
return random.sample(self.deck, n)

Now the game is simple; you can deal three cards per hand, and compare the cards naturally (using e.g. <) because of the comparison methods.

deck = Deck()
hand = deck.deal(3)
print(" - ".join(map(str, hand)))
if min(hand[0], hand[1]) < hand[2] < max(hand[0], hand[1]):
print("Winner!")
else:
print("Loser.")

Simple card game in python

To check if all elements in a collection are the same, a short and elegant solution is to use a set :

suits_in_hand = set(card.suit for card in hand)

if len(suits_in_hand) == 1:
print "\nAll cards are of the same suit"
print "You Win!"

Same for ranks.

For runs, you could compare the set of ranks to a set from range :

ranks_in_hand = set(card.rank for card in hand)
min_rank == min(ranks_in_hand)
if set(ranks_in_hand) == set(range(min_rank, min_rank + 2)):
print "\nGot a run !"

I am coding a card game in Java. The second object of the Hands class for player2 gives the player the same set of cards as player1, why?

This is happening because you have marked deckOfCards a static, which means it is a class level variable.
Stop using static there and the problem will be solved.

More on static variable.
https://beginnersbook.com/2013/04/java-static-class-block-methods-variables/#:~:text=Java%20Static%20Variables,the%20instances%20of%20the%20class.&text=Static%20variables%20are%20also%20known%20as%20Class%20Variables.

Visual Basic Simple Card Game. Having problems with coding the scoring process - Beginner

I'll Update this answer with Comments to explain everything later. For now, here's how I would start to have written your game. I used a Console Application just because it was the fastest to get going. The meat of the answer is with the Deck class coupled with the fact that using a Queue(Of T) ensures that every card is only handed out once.

Public Module Module1
Public Sub Main()
Dim PlayDeck As New Queue(Of Deck.Card)
Dim Deck As New Deck(True)

Deck.CardList.ForEach(Sub(x) PlayDeck.Enqueue(x))

Dim player1card As Deck.Card = PlayDeck.Dequeue()
Dim player2card As Deck.Card = PlayDeck.Dequeue()

Console.WriteLine(player1card.ToString())
Console.WriteLine(player2card.ToString())

Console.ReadLine()
End Sub

Public Class Deck
Public Structure Card
Public Property Suit As Suits
Public Property Value As CardValue

Public Enum Suits
Hearts = 0
Spades = 1
Clubs = 2
Diamonds = 3
End Enum

Public Enum CardValue
Two = 2
Three = 3
Four = 4
Five = 5
Six = 6
Seven = 7
Eight = 8
Nine = 9
Ten = 10
Jack = 11
Queen = 12
King = 13
Ace = 14
End Enum

Public Overrides Function ToString() As String
Return [Enum].GetName(GetType(CardValue), Me.Value) & " of " & [Enum].GetName(GetType(Suits), Me.Suit)
End Function
End Structure

Public CardList As List(Of Card)

Public Sub New()
CardList = New List(Of Card)
For Each suit As Card.Suits In [Enum].GetValues(GetType(Card.Suits))
For Each val As Card.CardValue In [Enum].GetValues(GetType(Card.CardValue))
CardList.Add(New Card With {.Suit = suit, .Value = val})
Next
Next
End Sub

Public Sub New(Shuffle As Boolean)
Me.New()
If Shuffle Then ShuffleCards()
End Sub

Public Sub ShuffleCards()
Dim csprng As New System.Security.Cryptography.RNGCryptoServiceProvider
Dim c As Integer = Me.CardList.Count
While c > 1
Dim b As Byte() = New Byte(0) {}
Do
csprng.GetBytes(b)
Loop While Not b(0) < c * (Byte.MaxValue / c)

Dim k As Integer = (b(0) Mod c)
c -= 1

Dim v As Card = Me.CardList(k)
Me.CardList(k) = Me.CardList(c)
Me.CardList(c) = v
End While
End Sub
End Class
End Module

Design for Card Game

The beautiful thing about Object Oriented approaches is that there are infinite ways to abstract and design the same concept. The question is which you would like to choose.

It seems that in this particular instance, your concern with the abstraction that you have described is that player1 needs to use the methods of player2 to accomplish the goal. However, I'd like to change the way you think of methods.

Methods, in general, should be thought of, first, as our public interfaces with the world. Properties, on the other hand, are those private things that we keep secret and locked up inside our objects/heads/bodies/etc.

It is true, that many programming languages have private and protected methods that are intended for internal use only, but that is really just to clean up the code so our public methods are not hundreds of lines long.

For your card game, a Player can be given a card by any other Object, so I would define the public method Player.giveCard( Card ). This is the simple part, which has already been touched on by other answers.

But the question becomes, what happens inside this method?
Also, how does the Card get removed from the original hand?

We can do several things here, and this list is by no means complete:

  1. The player can interact just with the other player.

In this situation, player1 chooses to give card_589 to player2. So, player1 calls the method player2.giveCard( card_589 ). In the real world, this would be demonstrated by player1 physically holding the card out for player2 to take. If player2 accepts the card, player1 no longer has it and must remove it from his hand. If player2 does not accept it, then player1 does not loose the card, and puts it back in his hand.

To model this, we would make one simple rule: the giveCard method returns a boolean result to indicate whether player2 takes the card.... After player1 calls player2.giveCard(), he has no say in whether player2 takes the card, because that is up to player2, in this scenario.

Our code might look like this somewhere inside player1's functions:

//begin pseudocode to give card to player identified by player2
//let self refer to player1's own methods
Player{
public giveCardToPlayer( player2, card_id ){
card = self.removeCardFromHand( card_id );
cardTaken = player2.giveCard( card );
if( cardTaken === false ){
self.addCardToHand( card );
}
}
//in the game management system, or elsewhere in player1's code, you then write
player1.giveCardToPlayer( player2, card_587 );
//or, if in another method "player1.play()", for example:
//self.giveCardToPlayer( player2, card_587 )
//
//end pseudocode

This is the simplest solution. Here, player1 does not see anything within player2's decision making as to whether card1 gets taken. Player1 chooses to remove the card from his own hand before he hands it over so the card is not in two places at once. If Player2 does not take the card, player1 puts it back in his deck, but otherwise does nothing, because the card is now with player2.

Personally, this is the simplest way to abstract the model, and my favorite.


  1. The player can interact through some intermediary

This is my favorite scenario when we are modeling a game that has some sort of delay such as in a computer network simulation, or a chess by mail simulation. Player1 mails the card to player2, but player2 may or may not receive the card. In this game, lets assume you have a table, like a poker table, and any player can put a card down between himself and another player so that only that other player can reach the card.

For this scenario, we would create a new object called Table, and while there are many ways that we can choose to abstract the placing of the card on the table, I will choose this method as the publicly available interface for the action:

Table.placeCardForUser( card, userId, myId, securityToken ) : bool
Table.countCardsOnTableToUserFromUser( userId, myId, securityToken ) : int
Table.pickUpCardToUser( userId, myId, securityToken ) : Card[0..*]
Table.pickUpCardsToMe( myId, securityToken ) : Card[0..*]

This introduces security issues, because I am telling the Table that only userId can pick up the card, and only myId can verify and retrieve the card, so the Table object needs some way to verify that I ("the current object") have the right to access the spot on the table identified by "userId" and "myId", but there are lots of solutions to this as well.

//begin psuedocode for current example
//we are in player1's function body
card = self.removeCardFromHand( card_587 );
player2_id = self.identifyPlayerToReceive( card );
table.placeCardForUser( card, player2_id, myId, securityToken );
//end current action

//at next opportunity to act, check to see
//if card was taken
cardCount = table.countCardsOnTableToUserFromUser( userId, myId, securityToken );
if( cardCount > 1 ){
//player2 has not taken card or other cards that have accumulated
pickUpCards = self.decideToPickUpCardsToPlayer( player2_id );
if( pickUpCards === true ){
cards = table.pickUpCardToUser( player2_id, myId, securityToken );
foreach( cards as card ){
self.addToHand( card );
}
}
}
//now check to see if anyone has given me cards between last round and this round
cards = table.pickUpCardsToMe( myId, securityToken );
foreach( cards as card ){
//let us assume that player1 takes all cards given to him
self.addToHand( card );
}

Variations of this can be made. You can imagine a tunnel between player1 and player2. Player1 establishes the tunnel by recognizing that he does not currently have a way to give cards to player2 and so he creates a tunnel. He gives a copy of the tunnel to player2, holding the "other end", and player2 then keeps a copy of the tunnel as well. Like the table situation, this tunnel now is a place that can keep items that are being passed back and forth to player2, but because only player1 and player2 have links, or pointers, to the tunnel, only these two players can put items in the tunnel or take them out, so therefore, we have an intermediary that does not require as much security. We can create tunnels to link all players with all other players, and this is still a variation of the intermediary design.


  1. The self aware card

Sometimes, we want designs that are easier to code and less like reality. What happens if the code for the Player object forgets to remove the card object from his hand? Now, because objects are generally passed by reference, player2 and player1 each have a reference to the card, and the game simulation thinks there are two copies of the same card!

In this situation, we can design the card to be self-aware, and give the card access to a player's hand.

For this abstraction, I will model the card as such:

//begin pseudocode
Card{
private owner;
//this is a private link to the object in which the card lies
//we will allow any object to be the owner of the card, as long
//as the object implements the "CardOwner" interface.

public putInto( newOwner ){
//whoever takes the card must specify a newOwner, which will
//implement the "CardHolder" interface.
success = newOwner.addCard( self );
if( success ){
self.owner.removeCard( self );
self.owner = newOwner;
}
}
}

We then can define the interface as follows:

//begin psuedocode
iCardHolder{
public removeCard( card ) : bool
public addCard( card ) : bool
}

In this scenario, we have divorced ourselves from "reality" by giving the card itself the ability to perform actions. But where this is useful is in large projects where you cannot trust the other programmers to remember the details about how the Card is properly handled.

By giving the card control over who has pointers to it, we can ensure that only one copy of the card exists at any time, no matter who is using it.

Now, player1's code might look like this:

 //give card to player2
card = self.chooseCard();
player2.giveCard( card );

//put card on the floor
card = self.chooseCard();
floor.giveCard( card );

//put card on the table
card = self.chooseCard();
table.giveCard( card );

And in each of these objects, we have the freedom to change the way we receive the card and where we keep it.

//player2 - is a simple CardHolder
public function giveCard( card ){
myHand = self;
card.putInto( myHand );
}

//the dealer is a cheat and does not implement CardHolder,
//but he has three locations that can act as CardHoldes
//they are:
// dealer.sleave, dealer.hand, and dealer.pocket
public function giveCard( card ){
location = self.chooseCardOwner( [ sleeve, hand, pocket ] );
card.putInto( location );
}

//the floor has random piles that are accumulating
public function giveCard( card ){
pile = self.chooseRandomPile();
card.putInto( pile );
}

This option is bizarre, but it gives us a lot of flexibility. In the above example, the Dealer and the Floor are not even implementers of the iCardHolder interface, but they hold references to objects to that do implement that interface, so they can still take the card.

In each of these implementations using iCardHolder, which is completely different from the others, the code is incredibly simple, because we have offloaded the manipulation of the cards location and put that responsibility on the card itself, and all the card cares about is that the objects that interact with it, agree to a contract of sorts and promise to implement one removeCard method, and one addCard method. As security, the card keeps a copy of the current owner in its own memory, so that if there is a mistake by one of the CardHolders, the Card itself holds the answer to its current owner.

Long Story Short

There is no one right way to model your game. It really is all about personal preference and how you want the system to behave. That's the beautiful thing about being a programmer. As the person who is making the code-design, you get to set the rules for how the program will operate, and what is good object-interaction, and what is bad object-interaction.

Dynamic Programming Card Game

The algorithm is simple and you can use memoization and dynamic programming in this way:

def findMax(mem, cards, myTurn):
maxValue = 0
if(not cards):
return 0
if str(cards) in mem: #If we have already compute this state
return mem[str(cards)]
elif not myTurn: #turn of Elmo
if cards[0] > cards[len(cards) - 1]:
maxValue = findMax(mem, cards[1:], True)
else:
maxValue = findMax(mem, cards[:-1], True)
else: #your turn
maxValue = max(cards[0] + findMax(mem, cards[1:], False), cards[len(cards) - 1] + findMax(mem, cards[:-1], False))
mem[str(cards)] = maxValue #Store the max value for this state
return maxValue

import random
size = int(10 + random.randint(0,1))
cards = [random.randint(0,50) for x in range(size)]
print "Cards" + str(cards)
print findMax({}, cards, True)

Output:

Cards: [28, 33, 48, 0, 26, 1, 3, 11, 22, 32, 12]
Max value: 120

A good way to make classes for more complex playing card types than those found in a standard deck?

When you are approaching a problem with OOP, you usually want to model behavior and properties in a reusable way, i.e., you should think of abstractions and organize your class hierarchy based on that.

I would write something like the following:

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)

Due to Python being a dynamically typed language, OOP is a little harder to justify in my opinion, since we can just rely on duck typing and dynamic binding,
the way you organize your hierarchy is less important.

If I were to model this problem in C# for example, I would without a doubt use the hierarchy showed above, because I could rely on polymorphism to represent different types and guide the flow of my logic based on what type of card is being analyzed.

A couple of final remarks:

  1. Python has very powerful builtin types, but most of the time
    using new custom types that build on them makes your life easier.
  2. You don't have to inherit from object since types in Python 3 (which is
    the only one maintained as of today) inherit from object by default.

But, at the end of the day, there isn't a perfect answer, the best way would be to try both of the approaches and see what you're more comfortable with.



Related Topics



Leave a reply



Submit